Merge branch 'master' into next
* master: (497 commits)
Prepare 7.3.0-SNAPSHOT builds
Prepare 7.2.1-SNAPSHOT builds
JGit v7.2.0.202503040940-r
JGit v7.2.0.202503040805-r
CacheRegion: fix non translatable text warnings
Ensure access to autoRefresh is thread-safe
FileReftableStack: use FileSnapshot to detect modification
FileReftableDatabase: consider ref updates by another process
BlameRegionMerger: report invalid regions with checked exception.
Prepare 7.2.0-SNAPSHOT builds
[ssh known_hosts] Handle unknown keys better
[releng] Remove unused target platform definitions
JGit v7.2.0.202502261823-rc1
[ssh known_hosts] Handle host certificates
[ssh known_hosts] Improve updating modified keys
[ssh known_hosts] Add tests and fix problems
[ssh, releng] Remove net.i2p.crypto.eddsa
AddCommand: Use parenthesis to make the operator precedence explicit
AddCommand: implement --all/--no-all
Do not load bitmap indexes during directory scans
...
Change-Id: I619c89071f5f7a05bcd0218840f7f47bd19b779d
diff --git a/.bazelrc b/.bazelrc
index efa007a..70322dd 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -2,7 +2,7 @@
# https://issues.gerritcodereview.com/issues/303819949
common --noenable_bzlmod
-build --workspace_status_command="python ./tools/workspace_status.py"
+build --workspace_status_command="python3 ./tools/workspace_status.py"
build --repository_cache=~/.gerritcodereview/bazel-cache/repository
build --incompatible_strict_action_env
build --action_env=PATH
@@ -37,5 +37,6 @@
test --build_tests_only
test --test_output=errors
test --flaky_test_attempts=3
+test --test_tag_filters=-ext
import %workspace%/tools/remote-bazelrc
diff --git a/.mailmap b/.mailmap
index 7116ebb..1c77395 100644
--- a/.mailmap
+++ b/.mailmap
@@ -1,20 +1,33 @@
+Adithya Chakilam <quic_achakila@quicinc.com> Adithya Chakilam <achakila@codeaurora.org>
Chris Aniszczyk <caniszczyk@gmail.com> Chris Aniszczyk <zx@eclipsesource.com>
Christian Halstrick <christian.halstrick@sap.com> Christian Halstrick <christian.halstrick@gmail.com>
Dani Megert <Daniel_Megert@ch.ibm.com> Daniel Megert <daniel_megert@ch.ibm.com>
+Dariusz Luksza <dariusz.luksza@gmail.com> Dariusz Luksza <dariusz@luksza.org>
+David Ostrovsky <david.ostrovsky@gmail.com> David Ostrovsky <david@ostrovsky.org>
David Pursehouse <david.pursehouse@gmail.com> David Pursehouse <david.pursehouse@sonymobile.com>
Han-Wen Nienhuys <hanwen@google.com> Han-Wen NIenhuys <hanwen@google.com>
Hector Oswaldo Caballero <hector.caballero@ericsson.com> Hector Caballero <hector.caballero@ericsson.com>
+Jackson Toeniskoetter <jackdt@google.com> <jackdt@google.com>
Lars Vogel <Lars.Vogel@vogella.com> Lars Vogel <Lars.Vogel@gmail.com>
Mark Ingram <markdingram@gmail.com> markdingram <markdingram@gmail.com>
Markus Duft <markus.duft@ssi-schaefer.com> Markus Duft <markus.duft@salomon.at>
+Martin Fick <mfick@nvidia.com> Martin Fick <mfick@codeaurora.org>
+Martin Fick <mfick@nvidia.com> Martin Fick <quic_mfick@quicinc.com>
Michael Keppler <michael.keppler@gmx.de> Michael Keppler <Michael.Keppler@gmx.de>
+Nasser Grainawi <quic_nasserg@quicinc.com> Nasser Grainawi <nasser@codeaurora.org>
Roberto Tyley <roberto.tyley@guardian.co.uk> roberto <roberto.tyley@guardian.co.uk>
Saša Živkov <sasa.zivkov@sap.com> Sasa Zivkov <sasa.zivkov@sap.com>
Saša Živkov <sasa.zivkov@sap.com> Saša Živkov <zivkov@gmail.com>
Saša Živkov <sasa.zivkov@sap.com> Sasa Zivkov <zivkov@gmail.com>
Sebastian Schuberth <sschuberth@gmail.com> Sebastian Schuberth <sebastian.schuberth@bosch.io>
+Sebastian Schuberth <sschuberth@gmail.com> <opensource@schuberth.dev>
Shawn Pearce <spearce@spearce.org> Shawn O. Pearce <sop@google.com>
Shawn Pearce <spearce@spearce.org> Shawn Pearce <sop@google.com>
Shawn Pearce <spearce@spearce.org> Shawn O. Pearce <spearce@spearce.org>
+Sven Selberg <sven.selberg@axis.com> Sven Selberg <sven.selberg@sonymobile.com>
Terry Parker <tparker@google.com> tparker <tparker@google.com>
Thomas Wolf <twolf@apache.org> Thomas Wolf <thomas.wolf@paranor.ch>
+Tomasz Zarna <tzarna@gmail.com> <Tomasz.Zarna@pl.ibm.com>
+Tomasz Zarna <tzarna@gmail.com> <tomasz.zarna@tasktop.com>
+Tobias Pfeifer <to.pfeifer@web.de> <to.pfeifer@sap.com>
+Yunjie Li <yunjieli@google.com> <yunjieli@google.com>
diff --git a/Documentation/config-options.md b/Documentation/config-options.md
index 78930e6..4dde8f8 100644
--- a/Documentation/config-options.md
+++ b/Documentation/config-options.md
@@ -31,6 +31,7 @@
| `core.dfs.blockSize` | `64 kiB` | ⃞ | Size in bytes of a single window read in from the pack file into the DFS block cache. |
| `core.dfs.concurrencyLevel` | `32` | ⃞ | The estimated number of threads concurrently accessing the DFS block cache. |
| `core.dfs.deltaBaseCacheLimit` | `10 MiB` | ⃞ | Maximum number of bytes to hold in per-reader DFS delta base cache. |
+| `core.dfs.loadRevIndexInParallel` | false; | ⃞ | Try to load the reverse index in parallel with the bitmap index. |
| `core.dfs.streamFileThreshold` | `50 MiB` | ⃞ | The size threshold beyond which objects must be streamed. |
| `core.dfs.streamBuffer` | Block size of the pack | ⃞ | Number of bytes to use for buffering when streaming a pack file during copying. If 0 the block size of the pack is used|
| `core.dfs.streamRatio` | `0.30` | ⃞ | Ratio of DFS block cache to occupy with a copied pack. Values between `0` and `1.0`. |
@@ -54,9 +55,13 @@
| `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, 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.trustLooseRefStat` | `always` | ⃞ | Whether to trust the file attributes (Java equivalent of stat command on *nix) of the loose ref. If `always` JGit will trust the file attributes of the loose ref and its parent directories. `after_open` behaves similar to `always`, except that all parent directories of the loose ref up to the repository root are opened and closed before its file attributes are considered. An open/close of these parent directories is known to refresh the file attributes, at least on some NFS clients. |
+| ~~`core.trustFolderStat`~~ | `true` | ⃞ | __Deprecated__, use `core.trustStat` instead. If set to `true` translated to `core.trustStat=always`, if `false` translated to `core.trustStat=never`, see below. If both `core.trustFolderStat` and `core.trustStat` are configured then `trustStat` takes precedence and `trustFolderStat` is ignored. |
+| `core.trustLooseRefStat` | `inherit` | ⃞ | Whether to trust the file attributes of loose refs and its fan-out parent directory. See `core.trustStat` for possible values. If `inherit`, JGit will use the behavior configured in `trustStat`. |
+| `core.trustPackedRefsStat` | `inherit` | ⃞ | Whether to trust the file attributes of the packed-refs file. See `core.trustStat` for possible values. If `inherit`, JGit will use the behavior configured in `core.trustStat`. |
+| `core.trustTablesListStat` | `inherit` | ⃞ | Whether to trust the file attributes of the `tables.list` file used by the reftable ref storage backend to store the list of reftable filenames. See `core.trustStat` for possible values. If `inherit`, JGit will use the behavior configured in `core.trustStat`. The reftable backend is used if `extensions.refStorage = reftable`. |
+| `core.trustLooseObjectStat` | `inherit` | ⃞ | Whether to trust the file attributes of the loose object file and its fan-out parent directory. See `core.trustStat` for possible values. If `inherit`, JGit will use the behavior configured in `core.trustStat`. |
+| `core.trustPackStat` | `inherit` | ⃞ | Whether to trust the file attributes of the `objects/pack` directory. See `core.trustStat` for possible values. If `inherit`, JGit will use the behavior configured in `core.trustStat`. |
+| `core.trustStat` | `always` | ⃞ | Global option to configure whether to trust file attributes (Java equivalent of stat command on Unix) of files storing git objects. Can be overridden for specific files by configuring `core.trustLooseRefStat, core.trustPackedRefsStat, core.trustLooseObjectStat, core.trustPackStat,core.trustTablesListStat`. If `never` JGit will ignore the file attributes of the file and always read it. If `always` JGit will trust the file attributes and will only read it if a file attribute has changed. `after_open` behaves the same as `always`, but file attributes are only considered *after* the file itself and any transient parent directories have been opened and closed. An open/close of the file/directory is known to refresh its file attributes, at least on some NFS clients. |
| `core.worktree` | Root directory of the working tree if it is not the parent directory of the `.git` directory | ✅ | The path to the root of the working tree. |
## __fetch__ options
@@ -130,6 +135,13 @@
| `pack.window` | `10` | ✅ | Number of objects to try when looking for a delta base per thread searching for deltas. |
| `pack.windowMemory` | `0` (unlimited) | ✅ | Maximum number of bytes to put into the delta search window. |
+## reftable options
+
+| option | default | git option | description |
+|---------|---------|------------|-------------|
+| `reftable.autoRefresh` | `false` | ⃞ | Whether to auto-refresh the reftable stack if it is out of date. |
+
+
## __repack__ options
| option | default | git option | description |
diff --git a/WORKSPACE b/WORKSPACE
index ab80a64..505141c 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -73,12 +73,6 @@
)
maven_jar(
- name = "eddsa",
- artifact = "net.i2p.crypto:eddsa:0.3.0",
- sha1 = "1901c8d4d8bffb7d79027686cfb91e704217c3e1",
-)
-
-maven_jar(
name = "jsch",
artifact = "com.jcraft:jsch:0.1.55",
sha1 = "bbd40e5aa7aa3cfad5db34965456cee738a42a50",
@@ -108,44 +102,44 @@
sha1 = "51cf043c87253c9f58b539c9f7e44c8894223850",
)
-SSHD_VERS = "2.12.0"
+SSHD_VERS = "2.15.0"
maven_jar(
name = "sshd-osgi",
artifact = "org.apache.sshd:sshd-osgi:" + SSHD_VERS,
- sha1 = "32b8de1cbb722ba75bdf9898e0c41d42af00ce57",
+ sha1 = "aa76898fe47eab7da0878dd60e6f3be5631e076c",
)
maven_jar(
name = "sshd-sftp",
artifact = "org.apache.sshd:sshd-sftp:" + SSHD_VERS,
- sha1 = "0f96f00a07b186ea62838a6a4122e8f4cad44df6",
+ sha1 = "2e226055ed060c64ed76256a9c45de6d0109eef8",
)
-JNA_VERS = "5.14.0"
+JNA_VERS = "5.16.0"
maven_jar(
name = "jna",
artifact = "net.java.dev.jna:jna:" + JNA_VERS,
- sha1 = "67bf3eaea4f0718cb376a181a629e5f88fa1c9dd",
+ sha1 = "ebea09f91dc9f7048099f963fb8d6f919f0a4d9c",
)
maven_jar(
name = "jna-platform",
artifact = "net.java.dev.jna:jna-platform:" + JNA_VERS,
- sha1 = "28934d48aed814f11e4c584da55c49fa7032b31b",
+ sha1 = "b2a9065f97c166893d504b164706512338e3bbc2",
)
maven_jar(
name = "commons-codec",
- artifact = "commons-codec:commons-codec:1.16.0",
- sha1 = "4e3eb3d79888d76b54e28b350915b5dc3919c9de",
+ artifact = "commons-codec:commons-codec:1.18.0",
+ sha1 = "ee45d1cf6ec2cc2b809ff04b4dc7aec858e0df8f",
)
maven_jar(
name = "commons-logging",
- artifact = "commons-logging:commons-logging:1.2",
- sha1 = "4bfc12adfe4842bf07b657f0369c4cb522955686",
+ artifact = "commons-logging:commons-logging:1.3.5",
+ sha1 = "a3fcc5d3c29b2b03433aa2d2f2d2c1b1638924a1",
)
maven_jar(
@@ -162,32 +156,38 @@
maven_jar(
name = "servlet-api",
- artifact = "jakarta.servlet:jakarta.servlet-api:6.0.0",
- sha1 = "abecc699286e65035ebba9844c03931357a6a963",
+ artifact = "jakarta.servlet:jakarta.servlet-api:6.1.0",
+ sha1 = "1169a246913fe3823782af7943e7a103634867c5",
)
maven_jar(
name = "commons-compress",
- artifact = "org.apache.commons:commons-compress:1.26.0",
- sha1 = "659feffdd12280201c8aacb8f7be94f9a883c824",
+ artifact = "org.apache.commons:commons-compress:1.27.1",
+ sha1 = "a19151084758e2fbb6b41eddaa88e7b8ff4e6599",
+)
+
+maven_jar(
+ name = "commons-lang3",
+ artifact = "org.apache.commons:commons-lang3:3.17.0",
+ sha1 = "b17d2136f0460dcc0d2016ceefca8723bdf4ee70",
)
maven_jar(
name = "commons-io",
- artifact = "commons-io:commons-io:2.15.1",
- sha1 = "f11560da189ab563a5c8e351941415430e9304ea",
+ artifact = "commons-io:commons-io:2.18.0",
+ sha1 = "44084ef756763795b31c578403dd028ff4a22950",
)
maven_jar(
name = "tukaani-xz",
- artifact = "org.tukaani:xz:1.9",
- sha1 = "1ea4bec1a921180164852c65006d928617bd2caf",
+ artifact = "org.tukaani:xz:1.10",
+ sha1 = "1be8166f89e035a56c6bfc67dbc423996fe577e2",
)
maven_jar(
name = "args4j",
- artifact = "args4j:args4j:2.33",
- sha1 = "bd87a75374a6d6523de82fef51fc3cfe9baf9fc9",
+ artifact = "args4j:args4j:2.37",
+ sha1 = "244f60c057d72a785227c0562d3560f42a7ea54b",
)
maven_jar(
@@ -204,126 +204,114 @@
maven_jar(
name = "mockito",
- artifact = "org.mockito:mockito-core:5.10.0",
- sha1 = "b3812fa2ee069f1d0b41c1c0155da79d0e1dcde0",
+ artifact = "org.mockito:mockito-core:5.15.2",
+ sha1 = "87be4b1e0cc5febc07ab3197a8ff3ede56b37a79",
)
maven_jar(
name = "assertj-core",
- artifact = "org.assertj:assertj-core:3.25.3",
- sha1 = "792b270e73aa1cfc28fa135be0b95e69ea451432",
+ artifact = "org.assertj:assertj-core:3.27.3",
+ sha1 = "31f5d58a202bd5df4993fb10fa2cffd610c20d6f",
)
-BYTE_BUDDY_VERSION = "1.14.12"
+BYTE_BUDDY_VERSION = "1.17.1"
maven_jar(
name = "bytebuddy",
artifact = "net.bytebuddy:byte-buddy:" + BYTE_BUDDY_VERSION,
- sha1 = "6e37f743dc15a8d7a4feb3eb0025cbc612d5b9e1",
+ sha1 = "8b5205fad48196a88d3d66dddff5a7417bce3596",
)
maven_jar(
name = "bytebuddy-agent",
artifact = "net.bytebuddy:byte-buddy-agent:" + BYTE_BUDDY_VERSION,
- sha1 = "be4984cb6fd1ef1d11f218a648889dfda44b8a15",
+ sha1 = "0669a13b59d5ffd8198a79e4dc99018a9278e457",
)
maven_jar(
name = "objenesis",
- artifact = "org.objenesis:objenesis:3.3",
- sha1 = "1049c09f1de4331e8193e579448d0916d75b7631",
+ artifact = "org.objenesis:objenesis:3.4",
+ sha1 = "675cbe121a68019235d27f6c34b4f0ac30e07418",
)
maven_jar(
name = "gson",
- artifact = "com.google.code.gson:gson:2.10.1",
- sha1 = "b3add478d4382b78ea20b1671390a858002feb6c",
+ artifact = "com.google.code.gson:gson:2.12.1",
+ sha1 = "4e773a317740b83b43cfc3d652962856041697cb",
)
-JETTY_VER = "12.0.9"
+JETTY_VER = "12.0.16"
maven_jar(
name = "jetty-servlet",
artifact = "org.eclipse.jetty.ee10:jetty-ee10-servlet:" + JETTY_VER,
- sha1 = "19e056d75741e7348411d677a9b26a54ea4b7d16",
- src_sha1 = "d7fcb4e9d259c1dd8595c6163054be449072fe14",
+ sha1 = "022a746c00b1ac5c790fee65a398c707160a46d8",
)
maven_jar(
name = "jetty-security",
artifact = "org.eclipse.jetty:jetty-security:" + JETTY_VER,
- sha1 = "0c03a77f9d1a8b595cb4a83011cef735bd34bc95",
- src_sha1 = "9fba93bbce1466ef9c77d7a75338abd479641721",
+ sha1 = "23b1a3abecf9d6f5498064a32d9145ae1d8330f9",
)
maven_jar(
name = "jetty-server",
artifact = "org.eclipse.jetty:jetty-server:" + JETTY_VER,
- sha1 = "87adc518dd68b674e08d7e4968d07b6dc73214f1",
- src_sha1 = "5404f097aae0126b820b040b4e924f31fe4ff25b",
+ sha1 = "3e3638b4bfbee04c27b3ae68e4949fc43b40a042",
)
maven_jar(
name = "jetty-session",
artifact = "org.eclipse.jetty:jetty-session:" + JETTY_VER,
- sha1 = "628444f02dfbc4efbd1920a12e055580b227e86b",
- src_sha1 = "2ac0ca2c84fa8e40655af1482a9c67d60d659ad2",
+ sha1 = "79cdedc7afebbdba4453f603dfe2f970baa35cc3",
)
maven_jar(
name = "jetty-http",
artifact = "org.eclipse.jetty:jetty-http:" + JETTY_VER,
- sha1 = "cb54f006b1484306bc4b24dc3802cff472a6ba82",
- src_sha1 = "a1a6bc169e06007cabf6534fd7c7d1f2e91ab775",
+ sha1 = "68019fa90e8420ae15c109bd8c8611cacbaf43e5",
)
maven_jar(
name = "jetty-io",
artifact = "org.eclipse.jetty:jetty-io:" + JETTY_VER,
- sha1 = "03e8f5b5c6d583ea591064671c23b8639c132052",
- src_sha1 = "41b5752f3aa4c77f872649c215142aee1d6a3395",
+ sha1 = "7a162c537a99bbaf35a074fec9a50815e6c81d9d",
)
maven_jar(
name = "jetty-util",
artifact = "org.eclipse.jetty:jetty-util:" + JETTY_VER,
- sha1 = "996ebc69825af41d49e2edc6796eb714acdc3369",
- src_sha1 = "fe3b4ecf5a176bfd3c0055e5e1490503d90965e8",
+ sha1 = "e262e505363e5925df15618622d9888aefc1b0d0",
)
maven_jar(
name = "jetty-util-ajax",
artifact = "org.eclipse.jetty:jetty-util-ajax:" + JETTY_VER,
- sha1 = "634f67e811e0ad2acb32feaaf409927d80cd69ae",
- src_sha1 = "192f1e1254f659af64c6cd1124807fa12bdfa721",
+ sha1 = "60225034131e3f771b40bc75c15bd9cc4952302b",
)
-BOUNCYCASTLE_VER = "1.77"
+BOUNCYCASTLE_VER = "1.80"
maven_jar(
name = "bcpg",
artifact = "org.bouncycastle:bcpg-jdk18on:" + BOUNCYCASTLE_VER,
- sha1 = "bb0be51e8b378baae6e0d86f5282cd3887066539",
- src_sha1 = "33ff3269cede7165dac44033a3b150cc9f9f11cf",
+ sha1 = "163889a825393854dbe7dc52f1a8667e715e9859",
)
maven_jar(
name = "bcprov",
artifact = "org.bouncycastle:bcprov-jdk18on:" + BOUNCYCASTLE_VER,
- sha1 = "2cc971b6c20949c1ff98d1a4bc741ee848a09523",
- src_sha1 = "14ea9a3d759261358c6a1f59490ded125b5273a6",
+ sha1 = "e22100b41042decf09cab914a5af8d2c57b5ac4a",
)
maven_jar(
name = "bcutil",
artifact = "org.bouncycastle:bcutil-jdk18on:" + BOUNCYCASTLE_VER,
- sha1 = "de3eaef351545fe8562cf29ddff4a403a45b49b7",
- src_sha1 = "6f8f56ab009e7a3204817a0d45ed9638f5e30116",
+ sha1 = "b95726d1d49a0c65010c59a3e6640311d951bfd1",
)
maven_jar(
name = "bcpkix",
artifact = "org.bouncycastle:bcpkix-jdk18on:" + BOUNCYCASTLE_VER,
- sha1 = "ed953791ba0229747dd0fd9911e3d76a462acfd3",
- src_sha1 = "fdff397d5de0306db014f0a17e91717150db2768",
+ sha1 = "5277dfaaef2e92ce1d802499599a0ca7488f86e6",
)
diff --git a/lib/BUILD b/lib/BUILD
index c2a8271..d236b3a 100644
--- a/lib/BUILD
+++ b/lib/BUILD
@@ -20,6 +20,16 @@
)
java_library(
+ name = "commons-lang3",
+ visibility = [
+ "//org.eclipse.jgit.archive:__pkg__",
+ "//org.eclipse.jgit.pgm.test:__pkg__",
+ "//org.eclipse.jgit.test:__pkg__",
+ ],
+ exports = ["@commons-lang3//jar"],
+)
+
+java_library(
name = "commons-io",
visibility = [
"//org.eclipse.jgit.archive:__pkg__",
@@ -45,16 +55,6 @@
)
java_library(
- name = "eddsa",
- visibility = [
- "//org.eclipse.jgit.ssh.apache:__pkg__",
- "//org.eclipse.jgit.ssh.apache.test:__pkg__",
- "//org.eclipse.jgit.ssh.jsch.test:__pkg__",
- ],
- exports = ["@eddsa//jar"],
-)
-
-java_library(
name = "gson",
visibility = [
"//org.eclipse.jgit.lfs:__pkg__",
@@ -208,6 +208,8 @@
visibility = [
"//org.eclipse.jgit.gpg.bc:__pkg__",
"//org.eclipse.jgit.gpg.bc.test:__pkg__",
+ "//org.eclipse.jgit.ssh.apache.test:__pkg__",
+ "//org.eclipse.jgit.ssh.jsch.test:__pkg__",
"//org.eclipse.jgit.test:__pkg__",
],
exports = ["@bcprov//jar"],
@@ -218,6 +220,8 @@
visibility = [
"//org.eclipse.jgit.gpg.bc:__pkg__",
"//org.eclipse.jgit.gpg.bc.test:__pkg__",
+ "//org.eclipse.jgit.ssh.apache.test:__pkg__",
+ "//org.eclipse.jgit.ssh.jsch.test:__pkg__",
"//org.eclipse.jgit.test:__pkg__",
],
exports = ["@bcutil//jar"],
@@ -227,6 +231,8 @@
name = "bcpkix",
visibility = [
"//org.eclipse.jgit.gpg.bc:__pkg__",
+ "//org.eclipse.jgit.ssh.apache.test:__pkg__",
+ "//org.eclipse.jgit.ssh.jsch.test:__pkg__",
"//org.eclipse.jgit.test:__pkg__",
],
exports = ["@bcpkix//jar"],
diff --git a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
index 7c079a5..fa9a416 100644
--- a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
@@ -5,13 +5,13 @@
Automatic-Module-Name: org.eclipse.jgit.ant.test
Bundle-SymbolicName: org.eclipse.jgit.ant.test
Bundle-Vendor: %Bundle-Vendor
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: JavaSE-17
Import-Package: org.apache.tools.ant,
- org.eclipse.jgit.ant.tasks;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.junit;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)",
+ org.eclipse.jgit.ant.tasks;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.junit;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.0)",
org.hamcrest.core;version="[1.1.0,3.0.0)",
org.junit;version="[4.13,5.0.0)"
diff --git a/org.eclipse.jgit.ant.test/pom.xml b/org.eclipse.jgit.ant.test/pom.xml
index be954bd..4a54d87 100644
--- a/org.eclipse.jgit.ant.test/pom.xml
+++ b/org.eclipse.jgit.ant.test/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.ant.test</artifactId>
diff --git a/org.eclipse.jgit.ant/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant/META-INF/MANIFEST.MF
index b6b52e5..480d88e 100644
--- a/org.eclipse.jgit.ant/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ant/META-INF/MANIFEST.MF
@@ -3,13 +3,13 @@
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.ant
Bundle-SymbolicName: org.eclipse.jgit.ant
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-RequiredExecutionEnvironment: JavaSE-17
Import-Package: org.apache.tools.ant,
- org.eclipse.jgit.storage.file;version="[7.0.0,7.1.0)"
+ org.eclipse.jgit.storage.file;version="[7.3.0,7.4.0)"
Bundle-Localization: OSGI-INF/l10n/plugin
Bundle-Vendor: %Bundle-Vendor
-Export-Package: org.eclipse.jgit.ant;version="7.0.0",
- org.eclipse.jgit.ant.tasks;version="7.0.0";
+Export-Package: org.eclipse.jgit.ant;version="7.3.0",
+ org.eclipse.jgit.ant.tasks;version="7.3.0";
uses:="org.apache.tools.ant,
org.apache.tools.ant.types"
diff --git a/org.eclipse.jgit.ant/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.ant/META-INF/SOURCE-MANIFEST.MF
index 22f8aff..136b7cf 100644
--- a/org.eclipse.jgit.ant/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.ant/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@
Bundle-Name: org.eclipse.jgit.ant - Sources
Bundle-SymbolicName: org.eclipse.jgit.ant.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.0.0.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.ant;version="7.0.0.qualifier";roots="."
+Bundle-Version: 7.3.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.ant;version="7.3.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.ant/pom.xml b/org.eclipse.jgit.ant/pom.xml
index 7c3edf4..4e85ee4 100644
--- a/org.eclipse.jgit.ant/pom.xml
+++ b/org.eclipse.jgit.ant/pom.xml
@@ -15,7 +15,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.ant</artifactId>
diff --git a/org.eclipse.jgit.archive/BUILD b/org.eclipse.jgit.archive/BUILD
index 3d7dbd2..d4f5340 100644
--- a/org.eclipse.jgit.archive/BUILD
+++ b/org.eclipse.jgit.archive/BUILD
@@ -12,6 +12,7 @@
resources = glob(["resources/**"]),
deps = [
"//lib:commons-compress",
+ "//lib:commons-lang3",
# We want these deps to be provided_deps
"//org.eclipse.jgit:jgit",
],
diff --git a/org.eclipse.jgit.archive/META-INF/MANIFEST.MF b/org.eclipse.jgit.archive/META-INF/MANIFEST.MF
index e1a3671..fdfc635 100644
--- a/org.eclipse.jgit.archive/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.archive/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.archive
Bundle-SymbolicName: org.eclipse.jgit.archive
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-Vendor: %Bundle-Vendor
Bundle-Localization: OSGI-INF/l10n/plugin
Bundle-RequiredExecutionEnvironment: JavaSE-17
@@ -13,17 +13,18 @@
org.apache.commons.compress.compressors.bzip2;version="[1.4,2.0)",
org.apache.commons.compress.compressors.gzip;version="[1.4,2.0)",
org.apache.commons.compress.compressors.xz;version="[1.4,2.0)",
- org.eclipse.jgit.api;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.nls;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.revwalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)",
- org.osgi.framework;version="[1.3.0,2.0.0)"
+ org.eclipse.jgit.api;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.nls;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.revwalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.0)",
+ org.osgi.framework;version="[1.3.0,2.0.0)",
+ org.tukaani.xz
Bundle-ActivationPolicy: lazy
Bundle-Activator: org.eclipse.jgit.archive.FormatActivator
-Export-Package: org.eclipse.jgit.archive;version="7.0.0";
- uses:="org.eclipse.jgit.lib,
+Export-Package: org.eclipse.jgit.archive;version="7.3.0";
+ uses:="org.apache.commons.compress.archivers,
+ org.osgi.framework,
org.eclipse.jgit.api,
- org.apache.commons.compress.archivers,
- org.osgi.framework",
- org.eclipse.jgit.archive.internal;version="7.0.0";x-internal:=true
+ org.eclipse.jgit.lib",
+ org.eclipse.jgit.archive.internal;version="7.3.0";x-internal:=true
diff --git a/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF
index 322f52f..716e8d0 100644
--- a/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@
Bundle-Name: org.eclipse.jgit.archive - Sources
Bundle-SymbolicName: org.eclipse.jgit.archive.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.0.0.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.archive;version="7.0.0.qualifier";roots="."
+Bundle-Version: 7.3.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.archive;version="7.3.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.archive/pom.xml b/org.eclipse.jgit.archive/pom.xml
index 054aa59..3935dfa 100644
--- a/org.eclipse.jgit.archive/pom.xml
+++ b/org.eclipse.jgit.archive/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.archive</artifactId>
@@ -41,6 +41,11 @@
</dependency>
<dependency>
+ <groupId>org.tukaani</groupId>
+ <artifactId>xz</artifactId>
+ </dependency>
+
+ <dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId>
<version>${project.version}</version>
diff --git a/org.eclipse.jgit.benchmarks/pom.xml b/org.eclipse.jgit.benchmarks/pom.xml
index d8a616f..87d2bb3 100644
--- a/org.eclipse.jgit.benchmarks/pom.xml
+++ b/org.eclipse.jgit.benchmarks/pom.xml
@@ -16,7 +16,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.benchmarks</artifactId>
@@ -52,6 +52,10 @@
<artifactId>org.eclipse.jgit.junit</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
</dependencies>
<build>
@@ -79,7 +83,6 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
- <version>${maven-compiler-plugin-version}</version>
<configuration>
<encoding>UTF-8</encoding>
<release>${java.version}</release>
diff --git a/org.eclipse.jgit.benchmarks/src/org/eclipse/jgit/benchmarks/GetRefsBenchmark.java b/org.eclipse.jgit.benchmarks/src/org/eclipse/jgit/benchmarks/GetRefsBenchmark.java
index 52a881b..44e862e 100644
--- a/org.eclipse.jgit.benchmarks/src/org/eclipse/jgit/benchmarks/GetRefsBenchmark.java
+++ b/org.eclipse.jgit.benchmarks/src/org/eclipse/jgit/benchmarks/GetRefsBenchmark.java
@@ -24,10 +24,12 @@
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.internal.storage.file.FileReftableDatabase;
import org.eclipse.jgit.internal.storage.file.FileRepository;
import org.eclipse.jgit.lib.BatchRefUpdate;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.CoreConfig.TrustStat;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryCache;
@@ -38,8 +40,10 @@
import org.eclipse.jgit.transport.ReceiveCommand;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.FileUtils;
+import org.junit.Assume;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
@@ -66,11 +70,14 @@ public static class BenchmarkState {
@Param({ "true", "false" })
boolean useRefTable;
- @Param({ "100", "2500", "10000", "50000" })
+ @Param({ "true", "false" })
+ boolean autoRefresh;
+
+ @Param({ "100", "1000", "10000", "100000" })
int numBranches;
- @Param({ "true", "false" })
- boolean trustFolderStat;
+ @Param({ "ALWAYS", "AFTER_OPEN", "NEVER" })
+ TrustStat trustStat;
List<String> branches = new ArrayList<>(numBranches);
@@ -81,10 +88,13 @@ public static class BenchmarkState {
@Setup
@SuppressWarnings("boxing")
public void setupBenchmark() throws IOException, GitAPIException {
+ // if we use RefDirectory skip autoRefresh = false
+ Assume.assumeTrue(useRefTable || autoRefresh);
+
String firstBranch = "firstbranch";
testDir = Files.createDirectory(Paths.get("testrepos"));
- String repoName = "branches-" + numBranches + "-trustFolderStat-"
- + trustFolderStat + "-" + refDatabaseType();
+ String repoName = "branches-" + numBranches + "-trustStat-"
+ + trustStat + "-" + refDatabaseType();
Path workDir = testDir.resolve(repoName);
Path repoPath = workDir.resolve(".git");
Git git = Git.init().setDirectory(workDir.toFile()).call();
@@ -97,10 +107,13 @@ public void setupBenchmark() throws IOException, GitAPIException {
((FileRepository) git.getRepository()).convertRefStorage(
ConfigConstants.CONFIG_REF_STORAGE_REFTABLE, false,
false);
+ FileReftableDatabase refdb = (FileReftableDatabase) git
+ .getRepository().getRefDatabase();
+ refdb.setAutoRefresh(autoRefresh);
} else {
- cfg.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
- ConfigConstants.CONFIG_KEY_TRUSTFOLDERSTAT,
- trustFolderStat);
+ cfg.setEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
+ ConfigConstants.CONFIG_KEY_TRUST_STAT,
+ trustStat);
}
cfg.setInt(ConfigConstants.CONFIG_RECEIVE_SECTION, null,
"maxCommandBytes", Integer.MAX_VALUE);
@@ -112,7 +125,8 @@ public void setupBenchmark() throws IOException, GitAPIException {
System.out.println("Preparing test");
System.out.println("- repository: \t\t" + repoPath);
System.out.println("- refDatabase: \t\t" + refDatabaseType());
- System.out.println("- trustFolderStat: \t" + trustFolderStat);
+ System.out.println("- autoRefresh: \t\t" + autoRefresh);
+ System.out.println("- trustStat: \t" + trustStat);
System.out.println("- branches: \t\t" + numBranches);
BatchRefUpdate u = repo.getRefDatabase().newBatchUpdate();
@@ -152,7 +166,8 @@ public void teardown() throws IOException {
@BenchmarkMode({ Mode.AverageTime })
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Warmup(iterations = 2, time = 100, timeUnit = TimeUnit.MILLISECONDS)
- @Measurement(iterations = 2, time = 10, timeUnit = TimeUnit.SECONDS)
+ @Measurement(iterations = 2, time = 5, timeUnit = TimeUnit.SECONDS)
+ @Fork(2)
public void testGetExactRef(Blackhole blackhole, BenchmarkState state)
throws IOException {
String branchName = state.branches
@@ -164,7 +179,8 @@ public void testGetExactRef(Blackhole blackhole, BenchmarkState state)
@BenchmarkMode({ Mode.AverageTime })
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Warmup(iterations = 2, time = 100, timeUnit = TimeUnit.MILLISECONDS)
- @Measurement(iterations = 2, time = 10, timeUnit = TimeUnit.SECONDS)
+ @Measurement(iterations = 2, time = 5, timeUnit = TimeUnit.SECONDS)
+ @Fork(2)
public void testGetRefsByPrefix(Blackhole blackhole, BenchmarkState state)
throws IOException {
String branchPrefix = "refs/heads/branch/" + branchIndex.nextInt(100)
diff --git a/org.eclipse.jgit.benchmarks/src/org/eclipse/jgit/benchmarks/RawTextBenchmark.java b/org.eclipse.jgit.benchmarks/src/org/eclipse/jgit/benchmarks/RawTextBenchmark.java
new file mode 100644
index 0000000..19297eb
--- /dev/null
+++ b/org.eclipse.jgit.benchmarks/src/org/eclipse/jgit/benchmarks/RawTextBenchmark.java
@@ -0,0 +1,454 @@
+/*
+ * Copyright (C) 2022, Matthias Sohn <matthias.sohn@sap.com> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.benchmarks;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.TearDown;
+import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.infra.Blackhole;
+import org.openjdk.jmh.runner.Runner;
+import org.openjdk.jmh.runner.RunnerException;
+import org.openjdk.jmh.runner.options.Options;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+
+import java.util.concurrent.TimeUnit;
+
+import static org.eclipse.jgit.diff.RawText.getBufferSize;
+import static org.eclipse.jgit.diff.RawText.isBinary;
+import static org.eclipse.jgit.diff.RawText.isCrLfText;
+
+@State(Scope.Thread)
+public class RawTextBenchmark {
+
+ @State(Scope.Benchmark)
+ public static class BenchmarkState {
+
+ @Param({"1", "2", "3", "4", "5", "6"})
+ int testIndex;
+
+ @Param({"false", "true"})
+ boolean complete;
+
+ byte[] bytes;
+
+ @Setup
+ public void setupBenchmark() {
+ switch (testIndex) {
+ case 1: {
+ byte[] tmpBytes = "a".repeat(102400).getBytes();
+ bytes = tmpBytes;
+ break;
+ }
+ case 2: {
+ byte[] tmpBytes = "a".repeat(102400).getBytes();
+ byte[] tmpBytes2 = new byte[tmpBytes.length + 1];
+ System.arraycopy(tmpBytes, 0, tmpBytes2, 0, tmpBytes.length);
+ tmpBytes2[500] = '\0';
+ tmpBytes2[tmpBytes.length] = '\0';
+ bytes = tmpBytes2;
+ break;
+ }
+ case 3: {
+ byte[] tmpBytes = "a".repeat(102400).getBytes();
+ byte[] tmpBytes2 = new byte[tmpBytes.length + 1];
+ System.arraycopy(tmpBytes, 0, tmpBytes2, 0, tmpBytes.length);
+ tmpBytes2[500] = '\r';
+ tmpBytes2[tmpBytes.length] = '\r';
+ bytes = tmpBytes2;
+ break;
+ }
+ case 4: {
+ byte[] tmpBytes = "a".repeat(102400).getBytes();
+ byte[] tmpBytes2 = new byte[tmpBytes.length + 1];
+ System.arraycopy(tmpBytes, 0, tmpBytes2, 0, tmpBytes.length);
+ tmpBytes2[499] = '\r';
+ tmpBytes2[500] = '\n';
+ tmpBytes2[tmpBytes.length - 1] = '\r';
+ tmpBytes2[tmpBytes.length] = '\n';
+ bytes = tmpBytes2;
+ break;
+ }
+ case 5: {
+ byte[] tmpBytes = "a".repeat(102400).getBytes();
+ tmpBytes[0] = '\0';
+ bytes = tmpBytes;
+ break;
+ }
+ case 6: {
+ byte[] tmpBytes = "a".repeat(102400).getBytes();
+ tmpBytes[0] = '\r';
+ bytes = tmpBytes;
+ break;
+ }
+ default:
+ }
+ }
+
+ @TearDown
+ public void teardown() {
+ }
+ }
+
+ @Benchmark
+ @BenchmarkMode({Mode.AverageTime})
+ @OutputTimeUnit(TimeUnit.NANOSECONDS)
+ @Warmup(iterations = 2, time = 5, timeUnit = TimeUnit.SECONDS)
+ @Measurement(iterations = 2, time = 5, timeUnit = TimeUnit.SECONDS)
+ @Fork(1)
+ public void testIsCrLfTextOld(Blackhole blackhole, BenchmarkState state) {
+ blackhole.consume(
+ isCrLfTextOld(
+ state.bytes,
+ state.bytes.length,
+ state.complete
+ )
+ );
+ }
+
+ @Benchmark
+ @BenchmarkMode({Mode.AverageTime})
+ @OutputTimeUnit(TimeUnit.NANOSECONDS)
+ @Warmup(iterations = 2, time = 5, timeUnit = TimeUnit.SECONDS)
+ @Measurement(iterations = 2, time = 5, timeUnit = TimeUnit.SECONDS)
+ @Fork(1)
+ public void testIsCrLfTextNewCandidate1(Blackhole blackhole, BenchmarkState state) {
+ blackhole.consume(
+ isCrLfTextNewCandidate1(
+ state.bytes,
+ state.bytes.length,
+ state.complete
+ )
+ );
+ }
+
+ @Benchmark
+ @BenchmarkMode({Mode.AverageTime})
+ @OutputTimeUnit(TimeUnit.NANOSECONDS)
+ @Warmup(iterations = 2, time = 5, timeUnit = TimeUnit.SECONDS)
+ @Measurement(iterations = 2, time = 5, timeUnit = TimeUnit.SECONDS)
+ @Fork(1)
+ public void testIsCrLfTextNewCandidate2(Blackhole blackhole, BenchmarkState state) {
+ blackhole.consume(
+ isCrLfTextNewCandidate2(
+ state.bytes,
+ state.bytes.length,
+ state.complete
+ )
+ );
+ }
+
+ @Benchmark
+ @BenchmarkMode({Mode.AverageTime})
+ @OutputTimeUnit(TimeUnit.NANOSECONDS)
+ @Warmup(iterations = 2, time = 5, timeUnit = TimeUnit.SECONDS)
+ @Measurement(iterations = 2, time = 5, timeUnit = TimeUnit.SECONDS)
+ @Fork(1)
+ public void testIsCrLfTextNewCandidate3(Blackhole blackhole, BenchmarkState state) {
+ blackhole.consume(
+ isCrLfTextNewCandidate3(
+ state.bytes,
+ state.bytes.length,
+ state.complete
+ )
+ );
+ }
+
+ @Benchmark
+ @BenchmarkMode({Mode.AverageTime})
+ @OutputTimeUnit(TimeUnit.NANOSECONDS)
+ @Warmup(iterations = 2, time = 5, timeUnit = TimeUnit.SECONDS)
+ @Measurement(iterations = 2, time = 5, timeUnit = TimeUnit.SECONDS)
+ @Fork(1)
+ public void testIsCrLfTextNew(Blackhole blackhole, BenchmarkState state) {
+ blackhole.consume(
+ isCrLfText(
+ state.bytes,
+ state.bytes.length,
+ state.complete
+ )
+ );
+ }
+
+ @Benchmark
+ @BenchmarkMode({Mode.AverageTime})
+ @OutputTimeUnit(TimeUnit.NANOSECONDS)
+ @Warmup(iterations = 2, time = 5, timeUnit = TimeUnit.SECONDS)
+ @Measurement(iterations = 2, time = 5, timeUnit = TimeUnit.SECONDS)
+ @Fork(1)
+ public void testIsBinaryOld(Blackhole blackhole, BenchmarkState state) {
+ blackhole.consume(
+ isBinaryOld(
+ state.bytes,
+ state.bytes.length,
+ state.complete
+ )
+ );
+ }
+
+
+ @Benchmark
+ @BenchmarkMode({Mode.AverageTime})
+ @OutputTimeUnit(TimeUnit.NANOSECONDS)
+ @Warmup(iterations = 2, time = 5, timeUnit = TimeUnit.SECONDS)
+ @Measurement(iterations = 2, time = 5, timeUnit = TimeUnit.SECONDS)
+ @Fork(1)
+ public void testIsBinaryNew(Blackhole blackhole, BenchmarkState state) {
+ blackhole.consume(
+ isBinary(
+ state.bytes,
+ state.bytes.length,
+ state.complete
+ )
+ );
+ }
+
+
+ /**
+ * Determine heuristically whether a byte array represents binary (as
+ * opposed to text) content.
+ *
+ * @param raw
+ * the raw file content.
+ * @param length
+ * number of bytes in {@code raw} to evaluate. This should be
+ * {@code raw.length} unless {@code raw} was over-allocated by
+ * the caller.
+ * @param complete
+ * whether {@code raw} contains the whole data
+ * @return true if raw is likely to be a binary file, false otherwise
+ * @since 6.0
+ */
+ public static boolean isBinaryOld(byte[] raw, int length, boolean complete) {
+ // Similar heuristic as C Git. Differences:
+ // - limited buffer size; may be only the beginning of a large blob
+ // - no counting of printable vs. non-printable bytes < 0x20 and 0x7F
+ int maxLength = getBufferSize();
+ boolean isComplete = complete;
+ if (length > maxLength) {
+ // We restrict the length in all cases to getBufferSize() to get
+ // predictable behavior. Sometimes we load streams, and sometimes we
+ // have the full data in memory. With streams, we never look at more
+ // than the first getBufferSize() bytes. If we looked at more when
+ // we have the full data, different code paths in JGit might come to
+ // different conclusions.
+ length = maxLength;
+ isComplete = false;
+ }
+ byte last = 'x'; // Just something inconspicuous.
+ for (int ptr = 0; ptr < length; ptr++) {
+ byte curr = raw[ptr];
+ if (isBinary(curr, last)) {
+ return true;
+ }
+ last = curr;
+ }
+ if (isComplete) {
+ // Buffer contains everything...
+ return last == '\r'; // ... so this must be a lone CR
+ }
+ return false;
+ }
+
+ /**
+ * Determine heuristically whether a byte array represents text content
+ * using CR-LF as line separator.
+ *
+ * @param raw the raw file content.
+ * @param length number of bytes in {@code raw} to evaluate.
+ * @param complete whether {@code raw} contains the whole data
+ * @return {@code true} if raw is likely to be CR-LF delimited text,
+ * {@code false} otherwise
+ * @since 6.0
+ */
+ public static boolean isCrLfTextOld(byte[] raw, int length, boolean complete) {
+ boolean has_crlf = false;
+ byte last = 'x'; // Just something inconspicuous
+ for (int ptr = 0; ptr < length; ptr++) {
+ byte curr = raw[ptr];
+ if (isBinary(curr, last)) {
+ return false;
+ }
+ if (curr == '\n' && last == '\r') {
+ has_crlf = true;
+ }
+ last = curr;
+ }
+ if (last == '\r') {
+ if (complete) {
+ // Lone CR: it's binary after all.
+ return false;
+ }
+ // Tough call. If the next byte, which we don't have, would be a
+ // '\n', it'd be a CR-LF text, otherwise it'd be binary. Just decide
+ // based on what we already scanned; it wasn't binary until now.
+ }
+ return has_crlf;
+ }
+
+ /**
+ * Determine heuristically whether a byte array represents text content
+ * using CR-LF as line separator.
+ *
+ * @param raw
+ * the raw file content.
+ * @param length
+ * number of bytes in {@code raw} to evaluate.
+ * @return {@code true} if raw is likely to be CR-LF delimited text,
+ * {@code false} otherwise
+ * @param complete
+ * whether {@code raw} contains the whole data
+ * @since 6.0
+ */
+ public static boolean isCrLfTextNewCandidate1(byte[] raw, int length, boolean complete) {
+ boolean has_crlf = false;
+
+ // first detect empty
+ if (length <= 0) {
+ return false;
+ }
+
+ // next detect '\0'
+ for (int reversePtr = length - 1; reversePtr >= 0; --reversePtr) {
+ if (raw[reversePtr] == '\0') {
+ return false;
+ }
+ }
+
+ // if '\r' be last, then if complete then return non-crlf
+ if (raw[length - 1] == '\r' && complete) {
+ return false;
+ }
+
+ for (int ptr = 0; ptr < length - 1; ptr++) {
+ byte curr = raw[ptr];
+ if (curr == '\r') {
+ byte next = raw[ptr + 1];
+ if (next != '\n') {
+ return false;
+ }
+ // else
+ // we have crlf here
+ has_crlf = true;
+ // as next is '\n', it can never be '\r', just skip it from next check
+ ++ptr;
+ }
+ }
+
+ return has_crlf;
+ }
+
+ /**
+ * Determine heuristically whether a byte array represents text content
+ * using CR-LF as line separator.
+ *
+ * @param raw
+ * the raw file content.
+ * @param length
+ * number of bytes in {@code raw} to evaluate.
+ * @return {@code true} if raw is likely to be CR-LF delimited text,
+ * {@code false} otherwise
+ * @param complete
+ * whether {@code raw} contains the whole data
+ * @since 6.0
+ */
+ public static boolean isCrLfTextNewCandidate2(byte[] raw, int length, boolean complete) {
+ boolean has_crlf = false;
+
+ // first detect empty
+ if (length <= 0) {
+ return false;
+ }
+
+ // if '\r' be last, then if complete then return non-crlf
+ byte last = raw[length - 1];
+ if (last == '\0' || last == '\r' && complete) {
+ return false;
+ }
+
+ for (int ptr = 0; ptr < length - 1; ptr++) {
+ byte b = raw[ptr];
+ switch (b) {
+ case '\0':
+ return false;
+ case '\r': {
+ ++ptr;
+ b = raw[ptr];
+ if (b != '\n') {
+ return false;
+ }
+ // else
+ // we have crlf here
+ has_crlf = true;
+ // as next is '\n', it can never be '\r', just skip it from next check
+ break;
+ }
+ default:
+ // do nothing;
+ break;
+ }
+ }
+
+ return has_crlf;
+ }
+
+ /**
+ * Determine heuristically whether a byte array represents text content
+ * using CR-LF as line separator.
+ *
+ * @param raw
+ * the raw file content.
+ * @param length
+ * number of bytes in {@code raw} to evaluate.
+ * @return {@code true} if raw is likely to be CR-LF delimited text,
+ * {@code false} otherwise
+ * @param complete
+ * whether {@code raw} contains the whole data
+ * @since 6.0
+ */
+ public static boolean isCrLfTextNewCandidate3(byte[] raw, int length, boolean complete) {
+ boolean has_crlf = false;
+
+ int ptr = -1;
+ byte current;
+ while (ptr < length - 2) {
+ current = raw[++ptr];
+ if ('\0' == current || '\r' == current && (raw[++ptr] != '\n' || !(has_crlf = true))) {
+ return false;
+ }
+ }
+
+ if (ptr == length - 2) {
+ // if '\r' be last, then if isComplete then return binary
+ current = raw[++ptr];
+ if('\0' == current || '\r' == current && complete){
+ return false;
+ }
+ }
+
+ return has_crlf;
+ }
+
+
+ public static void main(String[] args) throws RunnerException {
+ Options opt = new OptionsBuilder()
+ .include(RawTextBenchmark.class.getSimpleName())
+ .forks(1).jvmArgs("-ea").build();
+ new Runner(opt).run();
+ }
+}
diff --git a/org.eclipse.jgit.coverage/pom.xml b/org.eclipse.jgit.coverage/pom.xml
index 1fe5318..8cab250 100644
--- a/org.eclipse.jgit.coverage/pom.xml
+++ b/org.eclipse.jgit.coverage/pom.xml
@@ -14,7 +14,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -27,88 +27,88 @@
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.ant</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.archive</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.http.apache</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.http.server</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.lfs</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.lfs.server</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.pgm</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.ui</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.ssh.apache</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.test</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.ant.test</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.http.test</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.pgm.test</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.lfs.test</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.lfs.server.test</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.ssh.apache.test</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</dependency>
</dependencies>
diff --git a/org.eclipse.jgit.gpg.bc.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.gpg.bc.test/META-INF/MANIFEST.MF
index 27510e2..287d75f 100644
--- a/org.eclipse.jgit.gpg.bc.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.gpg.bc.test/META-INF/MANIFEST.MF
@@ -3,19 +3,20 @@
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.gpg.bc.test
Bundle-SymbolicName: org.eclipse.jgit.gpg.bc.test
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-Vendor: %Bundle-Vendor
Bundle-Localization: plugin
Bundle-RequiredExecutionEnvironment: JavaSE-17
Require-Bundle: org.hamcrest.core;bundle-version="[1.3.0,2.0.0)"
-Import-Package: org.bouncycastle.jce.provider;version="[1.65.0,2.0.0)",
- org.bouncycastle.openpgp;version="[1.65.0,2.0.0)",
- org.bouncycastle.openpgp.operator;version="[1.65.0,2.0.0)",
- org.bouncycastle.openpgp.operator.jcajce;version="[1.65.0,2.0.0)",
- org.bouncycastle.util.encoders;version="[1.65.0,2.0.0)",
- org.eclipse.jgit.gpg.bc.internal;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.gpg.bc.internal.keys;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util.sha1;version="[7.0.0,7.1.0)",
+Import-Package: org.bouncycastle.asn1.cryptlib;version="[1.79.0,2.0.0)",
+ org.bouncycastle.jce.provider;version="[1.79.0,2.0.0)",
+ org.bouncycastle.openpgp;version="[1.79.0,2.0.0)",
+ org.bouncycastle.openpgp.operator;version="[1.79.0,2.0.0)",
+ org.bouncycastle.openpgp.operator.jcajce;version="[1.79.0,2.0.0)",
+ org.bouncycastle.util.encoders;version="[1.79.0,2.0.0)",
+ org.eclipse.jgit.gpg.bc.internal;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.gpg.bc.internal.keys;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util.sha1;version="[7.3.0,7.4.0)",
org.junit;version="[4.13,5.0.0)",
org.junit.runner;version="[4.13,5.0.0)",
org.junit.runners;version="[4.13,5.0.0)"
diff --git a/org.eclipse.jgit.gpg.bc.test/pom.xml b/org.eclipse.jgit.gpg.bc.test/pom.xml
index d865b13..10aa742 100644
--- a/org.eclipse.jgit.gpg.bc.test/pom.xml
+++ b/org.eclipse.jgit.gpg.bc.test/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.gpg.bc.test</artifactId>
diff --git a/org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/keys/SecretKeysTest.java b/org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/keys/SecretKeysTest.java
index fed0610..d486c97 100644
--- a/org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/keys/SecretKeysTest.java
+++ b/org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/keys/SecretKeysTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 Thomas Wolf <thomas.wolf@paranor.ch> and others
+ * Copyright (C) 2021, 2024 Thomas Wolf <twolf@apache.org> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -9,10 +9,7 @@
*/
package org.eclipse.jgit.gpg.bc.internal.keys;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
import java.io.BufferedInputStream;
import java.io.IOException;
@@ -20,8 +17,6 @@
import java.security.Security;
import java.util.Iterator;
-import javax.crypto.Cipher;
-
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKey;
@@ -49,39 +44,15 @@ public static void ensureBC() {
}
}
- private static volatile Boolean haveOCB;
-
- private static boolean ocbAvailable() {
- Boolean haveIt = haveOCB;
- if (haveIt != null) {
- return haveIt.booleanValue();
- }
- try {
- Cipher c = Cipher.getInstance("AES/OCB/NoPadding"); //$NON-NLS-1$
- if (c == null) {
- haveOCB = Boolean.FALSE;
- return false;
- }
- } catch (NoClassDefFoundError | Exception e) {
- haveOCB = Boolean.FALSE;
- return false;
- }
- haveOCB = Boolean.TRUE;
- return true;
- }
-
private static class TestData {
final String name;
final boolean encrypted;
- final boolean keyValue;
-
- TestData(String name, boolean encrypted, boolean keyValue) {
+ TestData(String name, boolean encrypted) {
this.name = name;
this.encrypted = encrypted;
- this.keyValue = keyValue;
}
@Override
@@ -93,19 +64,12 @@ public String toString() {
@Parameters(name = "{0}")
public static TestData[] initTestData() {
return new TestData[] {
- new TestData("AFDA8EA10E185ACF8C0D0F8885A0EF61A72ECB11", false, false),
- new TestData("2FB05DBB70FC07CB84C13431F640CA6CEA1DBF8A", false, true),
- new TestData("66CCECEC2AB46A9735B10FEC54EDF9FD0F77BAF9", true, true),
- new TestData("F727FAB884DA3BD402B6E0F5472E108D21033124", true, true),
- new TestData("62D43D7F117F7A5E4998ECB6617EE9942D069C14", true, true),
- new TestData("faked", false, true) };
- }
-
- private static byte[] readTestKey(String filename) throws Exception {
- try (InputStream in = new BufferedInputStream(
- SecretKeysTest.class.getResourceAsStream(filename))) {
- return SecretKeys.keyFromNameValueFormat(in);
- }
+ new TestData("AFDA8EA10E185ACF8C0D0F8885A0EF61A72ECB11", false),
+ new TestData("2FB05DBB70FC07CB84C13431F640CA6CEA1DBF8A", false),
+ new TestData("66CCECEC2AB46A9735B10FEC54EDF9FD0F77BAF9", true),
+ new TestData("F727FAB884DA3BD402B6E0F5472E108D21033124", true),
+ new TestData("62D43D7F117F7A5E4998ECB6617EE9942D069C14", true),
+ new TestData("faked", false) };
}
private static PGPPublicKey readAsc(InputStream in)
@@ -131,11 +95,6 @@ private static PGPPublicKey readAsc(InputStream in)
@Test
public void testKeyRead() throws Exception {
- if (data.keyValue) {
- byte[] bytes = readTestKey(data.name + ".key");
- assertEquals('(', bytes[0]);
- assertEquals(')', bytes[bytes.length - 1]);
- }
try (InputStream pubIn = this.getClass()
.getResourceAsStream(data.name + ".asc")) {
if (pubIn != null) {
@@ -151,11 +110,6 @@ public void testKeyRead() throws Exception {
: null,
publicKey);
assertNotNull(secretKey);
- } catch (PGPException e) {
- // Currently we may not be able to load OCB-encrypted keys.
- assertTrue(e.toString(), e.getMessage().contains("OCB"));
- assertTrue(data.encrypted);
- assertFalse(ocbAvailable());
}
}
}
diff --git a/org.eclipse.jgit.gpg.bc/META-INF/MANIFEST.MF b/org.eclipse.jgit.gpg.bc/META-INF/MANIFEST.MF
index 9749ac1..f35e5e5 100644
--- a/org.eclipse.jgit.gpg.bc/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.gpg.bc/META-INF/MANIFEST.MF
@@ -3,34 +3,28 @@
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.gpg.bc
Bundle-SymbolicName: org.eclipse.jgit.gpg.bc;singleton:=true
-Fragment-Host: org.eclipse.jgit;bundle-version="[7.0.0,7.1.0)"
+Fragment-Host: org.eclipse.jgit;bundle-version="[7.3.0,7.4.0)"
Bundle-Vendor: %Bundle-Vendor
Bundle-Localization: OSGI-INF/l10n/gpg_bc
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-RequiredExecutionEnvironment: JavaSE-17
-Import-Package: org.bouncycastle.asn1;version="[1.69.0,2.0.0)",
- org.bouncycastle.asn1.x9;version="[1.69.0,2.0.0)",
- org.bouncycastle.bcpg;version="[1.69.0,2.0.0)",
- org.bouncycastle.bcpg.sig;version="[1.69.0,2.0.0)",
- org.bouncycastle.crypto.ec;version="[1.69.0,2.0.0)",
- org.bouncycastle.gpg;version="[1.69.0,2.0.0)",
- org.bouncycastle.gpg.keybox;version="[1.69.0,2.0.0)",
- org.bouncycastle.gpg.keybox.jcajce;version="[1.69.0,2.0.0)",
- org.bouncycastle.jcajce.interfaces;version="[1.69.0,2.0.0)",
- org.bouncycastle.jcajce.util;version="[1.69.0,2.0.0)",
- org.bouncycastle.jce.provider;version="[1.69.0,2.0.0)",
- org.bouncycastle.math.ec;version="[1.69.0,2.0.0)",
- org.bouncycastle.math.field;version="[1.69.0,2.0.0)",
- org.bouncycastle.openpgp;version="[1.69.0,2.0.0)",
- org.bouncycastle.openpgp.jcajce;version="[1.69.0,2.0.0)",
- org.bouncycastle.openpgp.operator;version="[1.69.0,2.0.0)",
- org.bouncycastle.openpgp.operator.jcajce;version="[1.69.0,2.0.0)",
- org.bouncycastle.util;version="[1.69.0,2.0.0)",
- org.bouncycastle.util.encoders;version="[1.69.0,2.0.0)",
- org.bouncycastle.util.io;version="[1.69.0,2.0.0)",
- org.eclipse.jgit.annotations;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.api.errors;version="[7.0.0,7.1.0)",
+Import-Package: org.bouncycastle.asn1;version="[1.79.0,2.0.0)",
+ org.bouncycastle.asn1.x9;version="[1.79.0,2.0.0)",
+ org.bouncycastle.bcpg;version="[1.79.0,2.0.0)",
+ org.bouncycastle.bcpg.sig;version="[1.79.0,2.0.0)",
+ org.bouncycastle.crypto.ec;version="[1.79.0,2.0.0)",
+ org.bouncycastle.gpg;version="[1.79.0,2.0.0)",
+ org.bouncycastle.gpg.keybox;version="[1.79.0,2.0.0)",
+ org.bouncycastle.gpg.keybox.jcajce;version="[1.79.0,2.0.0)",
+ org.bouncycastle.jcajce.interfaces;version="[1.79.0,2.0.0)",
+ org.bouncycastle.jcajce.util;version="[1.79.0,2.0.0)",
+ org.bouncycastle.math.ec;version="[1.79.0,2.0.0)",
+ org.bouncycastle.math.field;version="[1.79.0,2.0.0)",
+ org.bouncycastle.openpgp;version="[1.79.0,2.0.0)",
+ org.bouncycastle.openpgp.jcajce;version="[1.79.0,2.0.0)",
+ org.bouncycastle.openpgp.operator;version="[1.79.0,2.0.0)",
+ org.bouncycastle.openpgp.operator.jcajce;version="[1.79.0,2.0.0)",
+ org.bouncycastle.util.encoders;version="[1.79.0,2.0.0)",
org.slf4j;version="[1.7.0,3.0.0)"
-Export-Package: org.eclipse.jgit.gpg.bc;version="7.0.0",
- org.eclipse.jgit.gpg.bc.internal;version="7.0.0";x-friends:="org.eclipse.jgit.gpg.bc.test",
- org.eclipse.jgit.gpg.bc.internal.keys;version="7.0.0";x-friends:="org.eclipse.jgit.gpg.bc.test"
+Export-Package: org.eclipse.jgit.gpg.bc.internal;version="7.3.0";x-friends:="org.eclipse.jgit.gpg.bc.test",
+ org.eclipse.jgit.gpg.bc.internal.keys;version="7.3.0";x-friends:="org.eclipse.jgit.gpg.bc.test"
diff --git a/org.eclipse.jgit.gpg.bc/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.gpg.bc/META-INF/SOURCE-MANIFEST.MF
index 0733a68..5134716 100644
--- a/org.eclipse.jgit.gpg.bc/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.gpg.bc/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@
Bundle-Name: org.eclipse.jgit.gpg.bc - Sources
Bundle-SymbolicName: org.eclipse.jgit.gpg.bc.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.0.0.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.gpg.bc;version="7.0.0.qualifier";roots="."
+Bundle-Version: 7.3.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.gpg.bc;version="7.3.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.gpg.bc/about.html b/org.eclipse.jgit.gpg.bc/about.html
index fc527d5..92b9409 100644
--- a/org.eclipse.jgit.gpg.bc/about.html
+++ b/org.eclipse.jgit.gpg.bc/about.html
@@ -58,32 +58,6 @@
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.</p>
-<hr>
-<p><b>org.eclipse.jgit.gpg.bc.internal.keys.SExprParser - MIT</b></p>
-
-<p>Copyright (c) 2000-2021 The Legion of the Bouncy Castle Inc.
-(<a href="https://www.bouncycastle.org">https://www.bouncycastle.org</a>)</p>
-
-<p>
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software
-and associated documentation files (the "Software"), to deal in the Software without restriction,
-including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
-and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
-subject to the following conditions:
-</p>
-<p>
-The above copyright notice and this permission notice shall be included in all copies or substantial
-portions of the Software.
-</p>
-<p>
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
-INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
-PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
-OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
-</p>
-
</body>
</html>
diff --git a/org.eclipse.jgit.gpg.bc/pom.xml b/org.eclipse.jgit.gpg.bc/pom.xml
index f4dce68..6159129 100644
--- a/org.eclipse.jgit.gpg.bc/pom.xml
+++ b/org.eclipse.jgit.gpg.bc/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.gpg.bc</artifactId>
@@ -160,7 +160,7 @@
<breakBuildOnBinaryIncompatibleModifications>false</breakBuildOnBinaryIncompatibleModifications>
<onlyBinaryIncompatible>false</onlyBinaryIncompatible>
<includeSynthetic>false</includeSynthetic>
- <ignoreMissingClasses>false</ignoreMissingClasses>
+ <ignoreMissingClasses>true</ignoreMissingClasses>
<skipPomModules>true</skipPomModules>
</parameter>
<skip>false</skip>
diff --git a/org.eclipse.jgit.gpg.bc/resources/META-INF/services/org.eclipse.jgit.lib.GpgSigner b/org.eclipse.jgit.gpg.bc/resources/META-INF/services/org.eclipse.jgit.lib.GpgSigner
deleted file mode 100644
index 6752b64..0000000
--- a/org.eclipse.jgit.gpg.bc/resources/META-INF/services/org.eclipse.jgit.lib.GpgSigner
+++ /dev/null
@@ -1 +0,0 @@
-org.eclipse.jgit.gpg.bc.internal.BouncyCastleGpgSigner
diff --git a/org.eclipse.jgit.gpg.bc/resources/META-INF/services/org.eclipse.jgit.lib.GpgSignatureVerifierFactory b/org.eclipse.jgit.gpg.bc/resources/META-INF/services/org.eclipse.jgit.lib.SignatureVerifierFactory
similarity index 100%
rename from org.eclipse.jgit.gpg.bc/resources/META-INF/services/org.eclipse.jgit.lib.GpgSignatureVerifierFactory
rename to org.eclipse.jgit.gpg.bc/resources/META-INF/services/org.eclipse.jgit.lib.SignatureVerifierFactory
diff --git a/org.eclipse.jgit.gpg.bc/resources/META-INF/services/org.eclipse.jgit.lib.SignerFactory b/org.eclipse.jgit.gpg.bc/resources/META-INF/services/org.eclipse.jgit.lib.SignerFactory
new file mode 100644
index 0000000..c0b214d
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc/resources/META-INF/services/org.eclipse.jgit.lib.SignerFactory
@@ -0,0 +1 @@
+org.eclipse.jgit.gpg.bc.internal.BouncyCastleGpgSignerFactory
diff --git a/org.eclipse.jgit.gpg.bc/resources/org/eclipse/jgit/gpg/bc/internal/BCText.properties b/org.eclipse.jgit.gpg.bc/resources/org/eclipse/jgit/gpg/bc/internal/BCText.properties
index 77ca2cd..9e7f98c 100644
--- a/org.eclipse.jgit.gpg.bc/resources/org/eclipse/jgit/gpg/bc/internal/BCText.properties
+++ b/org.eclipse.jgit.gpg.bc/resources/org/eclipse/jgit/gpg/bc/internal/BCText.properties
@@ -1,7 +1,5 @@
corrupt25519Key=Ed25519/Curve25519 public key has wrong length: {0}
credentialPassphrase=Passphrase
-cryptCipherError=Cannot create cipher to decrypt: {0}
-cryptWrongDecryptedLength=Decrypted key has wrong length; expected {0} bytes, got only {1} bytes
gpgFailedToParseSecretKey=Failed to parse secret key file {0}. Is the entered passphrase correct?
gpgNoCredentialsProvider=missing credentials provider
gpgNoKeygrip=Cannot find key {0}: cannot determine key grip
@@ -9,22 +7,14 @@
gpgNoKeyInLegacySecring=no matching secret key found in legacy secring.gpg for key or user id: {0}
gpgNoPublicKeyFound=Unable to find a public-key with key or user id: {0}
gpgNoSecretKeyForPublicKey=unable to find associated secret key for public key: {0}
-gpgNoSuchAlgorithm=Cannot decrypt encrypted secret key: encryption algorithm {0} is not available
gpgNotASigningKey=Secret key ({0}) is not suitable for signing
gpgKeyInfo=GPG Key (fingerprint {0})
gpgSigningCancelled=Signing was cancelled
+keyAlgorithmMismatch=Secret key has a different algorithm than the public key
+keyMismatch=Secret key does not match public key; public key is {0} {1} while secret key is for {2} {3}
logWarnGnuPGHome=Cannot access GPG home directory given by environment variable GNUPGHOME={}
logWarnGpgHomeProperty=Cannot access GPG home directory given by Java system property jgit.gpg.home={}
nonSignatureError=Signature does not decode into a signature object
-secretKeyTooShort=Secret key file corrupt; only {0} bytes read
-sexprHexNotClosed=Hex number in s-expression not closed
-sexprHexOdd=Hex number in s-expression has an odd number of digits
-sexprStringInvalidEscape=Invalid escape {0} in s-expression
-sexprStringInvalidEscapeAtEnd=Invalid s-expression: quoted string ends with escape character
-sexprStringInvalidHexEscape=Invalid hex escape in s-expression
-sexprStringInvalidOctalEscape=Invalid octal escape in s-expression
-sexprStringNotClosed=String in s-expression not closed
-sexprUnhandled=Unhandled token {0} in s-expression
signatureInconsistent=Inconsistent signature; key ID {0} does not match issuer fingerprint {1}
signatureKeyLookupError=Error occurred while looking for public key
signatureNoKeyInfo=No way to determine a public key from the signature
diff --git a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/BouncyCastleGpgSignerFactory.java b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/BouncyCastleGpgSignerFactory.java
deleted file mode 100644
index fdd1a2b..0000000
--- a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/BouncyCastleGpgSignerFactory.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2021 Thomas Wolf <thomas.wolf@paranor.ch> and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-package org.eclipse.jgit.gpg.bc;
-
-import org.eclipse.jgit.gpg.bc.internal.BouncyCastleGpgSigner;
-import org.eclipse.jgit.lib.GpgSigner;
-
-/**
- * Factory for creating a {@link GpgSigner} based on Bouncy Castle.
- *
- * @since 5.11
- */
-public final class BouncyCastleGpgSignerFactory {
-
- private BouncyCastleGpgSignerFactory() {
- // No instantiation
- }
-
- /**
- * Creates a new {@link GpgSigner}.
- *
- * @return the {@link GpgSigner}
- */
- public static GpgSigner create() {
- return new BouncyCastleGpgSigner();
- }
-}
diff --git a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BCText.java b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BCText.java
index 705e195..fcae7c2 100644
--- a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BCText.java
+++ b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BCText.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018, 2021 Salesforce and others
+ * Copyright (C) 2018, 2024 Salesforce and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -30,8 +30,6 @@ public static BCText get() {
// @formatter:off
/***/ public String corrupt25519Key;
/***/ public String credentialPassphrase;
- /***/ public String cryptCipherError;
- /***/ public String cryptWrongDecryptedLength;
/***/ public String gpgFailedToParseSecretKey;
/***/ public String gpgNoCredentialsProvider;
/***/ public String gpgNoKeygrip;
@@ -39,22 +37,14 @@ public static BCText get() {
/***/ public String gpgNoKeyInLegacySecring;
/***/ public String gpgNoPublicKeyFound;
/***/ public String gpgNoSecretKeyForPublicKey;
- /***/ public String gpgNoSuchAlgorithm;
/***/ public String gpgNotASigningKey;
/***/ public String gpgKeyInfo;
/***/ public String gpgSigningCancelled;
+ /***/ public String keyAlgorithmMismatch;
+ /***/ public String keyMismatch;
/***/ public String logWarnGnuPGHome;
/***/ public String logWarnGpgHomeProperty;
/***/ public String nonSignatureError;
- /***/ public String secretKeyTooShort;
- /***/ public String sexprHexNotClosed;
- /***/ public String sexprHexOdd;
- /***/ public String sexprStringInvalidEscape;
- /***/ public String sexprStringInvalidEscapeAtEnd;
- /***/ public String sexprStringInvalidHexEscape;
- /***/ public String sexprStringInvalidOctalEscape;
- /***/ public String sexprStringNotClosed;
- /***/ public String sexprUnhandled;
/***/ public String signatureInconsistent;
/***/ public String signatureKeyLookupError;
/***/ public String signatureNoKeyInfo;
diff --git a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgPublicKey.java b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgPublicKey.java
index d736536..9ec5b45 100644
--- a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgPublicKey.java
+++ b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgPublicKey.java
@@ -1,3 +1,12 @@
+/*
+ * Copyright (C) 2024 Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
package org.eclipse.jgit.gpg.bc.internal;
import java.util.List;
diff --git a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSignatureVerifier.java b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSignatureVerifier.java
index 3378bb3..5a3d43b 100644
--- a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSignatureVerifier.java
+++ b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSignatureVerifier.java
@@ -12,7 +12,6 @@
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.security.Security;
import java.text.MessageFormat;
import java.time.Instant;
import java.util.Date;
@@ -20,7 +19,6 @@
import java.util.Locale;
import org.bouncycastle.bcpg.sig.IssuerFingerprint;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.PGPCompressedData;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKey;
@@ -31,33 +29,20 @@
import org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import org.bouncycastle.util.encoders.Hex;
-import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.api.errors.JGitInternalException;
-import org.eclipse.jgit.lib.AbstractGpgSignatureVerifier;
import org.eclipse.jgit.lib.GpgConfig;
-import org.eclipse.jgit.lib.GpgSignatureVerifier;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.SignatureVerifier;
import org.eclipse.jgit.util.LRUMap;
import org.eclipse.jgit.util.StringUtils;
/**
- * A {@link GpgSignatureVerifier} to verify GPG signatures using BouncyCastle.
+ * A {@link SignatureVerifier} to verify GPG signatures using BouncyCastle.
*/
public class BouncyCastleGpgSignatureVerifier
- extends AbstractGpgSignatureVerifier {
+ implements SignatureVerifier {
- private static void registerBouncyCastleProviderIfNecessary() {
- if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
- Security.addProvider(new BouncyCastleProvider());
- }
- }
-
- /**
- * Creates a new instance and registers the BouncyCastle security provider
- * if needed.
- */
- public BouncyCastleGpgSignatureVerifier() {
- registerBouncyCastleProviderIfNecessary();
- }
+ private static final String NAME = "bc"; //$NON-NLS-1$
// To support more efficient signature verification of multiple objects we
// cache public keys once found in a LRU cache.
@@ -70,7 +55,7 @@ public BouncyCastleGpgSignatureVerifier() {
@Override
public String getName() {
- return "bc"; //$NON-NLS-1$
+ return NAME;
}
static PGPSignature parseSignature(InputStream in)
@@ -90,9 +75,8 @@ static PGPSignature parseSignature(InputStream in)
}
@Override
- public SignatureVerification verify(@NonNull GpgConfig config, byte[] data,
- byte[] signatureData)
- throws IOException {
+ public SignatureVerification verify(Repository repository, GpgConfig config,
+ byte[] data, byte[] signatureData) throws IOException {
PGPSignature signature = null;
String fingerprint = null;
String signer = null;
@@ -127,14 +111,15 @@ public SignatureVerification verify(@NonNull GpgConfig config, byte[] data,
}
Date signatureCreatedAt = signature.getCreationTime();
if (fingerprint == null && signer == null && keyId == null) {
- return new VerificationResult(signatureCreatedAt, null, null, null,
- false, false, TrustLevel.UNKNOWN,
+ return new SignatureVerification(NAME, signatureCreatedAt,
+ null, null, null, false, false, TrustLevel.UNKNOWN,
BCText.get().signatureNoKeyInfo);
}
if (fingerprint != null && keyId != null
&& !fingerprint.endsWith(keyId)) {
- return new VerificationResult(signatureCreatedAt, signer, fingerprint,
- signer, false, false, TrustLevel.UNKNOWN,
+ return new SignatureVerification(NAME, signatureCreatedAt,
+ signer, fingerprint, signer, false, false,
+ TrustLevel.UNKNOWN,
MessageFormat.format(BCText.get().signatureInconsistent,
keyId, fingerprint));
}
@@ -175,15 +160,16 @@ public SignatureVerification verify(@NonNull GpgConfig config, byte[] data,
bySigner.put(signer, NO_KEY);
}
}
- return new VerificationResult(signatureCreatedAt, signer,
- fingerprint, signer, false, false, TrustLevel.UNKNOWN,
- BCText.get().signatureNoPublicKey);
+ return new SignatureVerification(NAME, signatureCreatedAt,
+ signer, fingerprint, signer, false, false,
+ TrustLevel.UNKNOWN, BCText.get().signatureNoPublicKey);
}
if (fingerprint != null && !publicKey.isExactMatch()) {
// We did find _some_ signing key for the signer, but it doesn't
// match the given fingerprint.
- return new VerificationResult(signatureCreatedAt, signer,
- fingerprint, signer, false, false, TrustLevel.UNKNOWN,
+ return new SignatureVerification(NAME, signatureCreatedAt,
+ signer, fingerprint, signer, false, false,
+ TrustLevel.UNKNOWN,
MessageFormat.format(BCText.get().signatureNoSigningKey,
fingerprint));
}
@@ -229,8 +215,7 @@ public SignatureVerification verify(@NonNull GpgConfig config, byte[] data,
boolean verified = false;
try {
signature.init(
- new JcaPGPContentVerifierBuilderProvider()
- .setProvider(BouncyCastleProvider.PROVIDER_NAME),
+ new JcaPGPContentVerifierBuilderProvider(),
pubKey);
signature.update(data);
verified = signature.verify();
@@ -238,15 +223,8 @@ public SignatureVerification verify(@NonNull GpgConfig config, byte[] data,
throw new JGitInternalException(
BCText.get().signatureVerificationError, e);
}
- return new VerificationResult(signatureCreatedAt, signer, fingerprint, user,
- verified, expired, trust, null);
- }
-
- @Override
- public SignatureVerification verify(byte[] data, byte[] signatureData)
- throws IOException {
- throw new UnsupportedOperationException(
- "Call verify(GpgConfig, byte[], byte[]) instead."); //$NON-NLS-1$
+ return new SignatureVerification(NAME, signatureCreatedAt, signer,
+ fingerprint, user, verified, expired, trust, null);
}
private TrustLevel parseGpgTrustPacket(byte[] packet) {
@@ -282,76 +260,4 @@ public void clear() {
byFingerprint.clear();
bySigner.clear();
}
-
- private static class VerificationResult implements SignatureVerification {
-
- private final Date creationDate;
-
- private final String signer;
-
- private final String keyUser;
-
- private final String fingerprint;
-
- private final boolean verified;
-
- private final boolean expired;
-
- private final @NonNull TrustLevel trustLevel;
-
- private final String message;
-
- public VerificationResult(Date creationDate, String signer,
- String fingerprint, String user, boolean verified,
- boolean expired, @NonNull TrustLevel trust, String message) {
- this.creationDate = creationDate;
- this.signer = signer;
- this.fingerprint = fingerprint;
- this.keyUser = user;
- this.verified = verified;
- this.expired = expired;
- this.trustLevel = trust;
- this.message = message;
- }
-
- @Override
- public Date getCreationDate() {
- return creationDate;
- }
-
- @Override
- public String getSigner() {
- return signer;
- }
-
- @Override
- public String getKeyUser() {
- return keyUser;
- }
-
- @Override
- public String getKeyFingerprint() {
- return fingerprint;
- }
-
- @Override
- public boolean isExpired() {
- return expired;
- }
-
- @Override
- public TrustLevel getTrustLevel() {
- return trustLevel;
- }
-
- @Override
- public String getMessage() {
- return message;
- }
-
- @Override
- public boolean getVerified() {
- return verified;
- }
- }
}
diff --git a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSignatureVerifierFactory.java b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSignatureVerifierFactory.java
index ae82b75..566ad1b 100644
--- a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSignatureVerifierFactory.java
+++ b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSignatureVerifierFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ * Copyright (C) 2021, 2024 Thomas Wolf <twolf@apache.org> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -9,20 +9,27 @@
*/
package org.eclipse.jgit.gpg.bc.internal;
-import org.eclipse.jgit.lib.GpgSignatureVerifier;
-import org.eclipse.jgit.lib.GpgSignatureVerifierFactory;
+import org.eclipse.jgit.lib.GpgConfig.GpgFormat;
+import org.eclipse.jgit.lib.SignatureVerifier;
+import org.eclipse.jgit.lib.SignatureVerifierFactory;
/**
- * A {@link GpgSignatureVerifierFactory} that creates
- * {@link GpgSignatureVerifier} instances that verify GPG signatures using
- * BouncyCastle and that do cache public keys.
+ * A {@link SignatureVerifierFactory} that creates {@link SignatureVerifier}
+ * instances that verify GPG signatures using BouncyCastle and that do cache
+ * public keys.
*/
public final class BouncyCastleGpgSignatureVerifierFactory
- extends GpgSignatureVerifierFactory {
+ implements SignatureVerifierFactory {
@Override
- public GpgSignatureVerifier getVerifier() {
+ public GpgFormat getType() {
+ return GpgFormat.OPENPGP;
+ }
+
+ @Override
+ public SignatureVerifier create() {
return new BouncyCastleGpgSignatureVerifier();
}
+
}
diff --git a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSigner.java b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSigner.java
index 763b7f7..adac9b1 100644
--- a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSigner.java
+++ b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSigner.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018, 2021, Salesforce and others
+ * Copyright (C) 2018, 2024, Salesforce and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -14,13 +14,11 @@
import java.net.URISyntaxException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
-import java.security.Security;
import java.util.Iterator;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.bcpg.BCPGOutputStream;
import org.bouncycastle.bcpg.HashAlgorithmTags;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKey;
@@ -30,79 +28,23 @@
import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
-import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.api.errors.CanceledException;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.api.errors.UnsupportedSigningFormatException;
import org.eclipse.jgit.errors.UnsupportedCredentialItem;
-import org.eclipse.jgit.internal.JGitText;
-import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.GpgConfig;
-import org.eclipse.jgit.lib.GpgObjectSigner;
import org.eclipse.jgit.lib.GpgSignature;
-import org.eclipse.jgit.lib.GpgSigner;
-import org.eclipse.jgit.lib.ObjectBuilder;
import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.GpgConfig.GpgFormat;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.Signer;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.util.StringUtils;
/**
* GPG Signer using the BouncyCastle library.
*/
-public class BouncyCastleGpgSigner extends GpgSigner
- implements GpgObjectSigner {
-
- private static void registerBouncyCastleProviderIfNecessary() {
- if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
- Security.addProvider(new BouncyCastleProvider());
- }
- }
-
- /**
- * Create a new instance.
- * <p>
- * The BounceCastleProvider will be registered if necessary.
- * </p>
- */
- public BouncyCastleGpgSigner() {
- registerBouncyCastleProviderIfNecessary();
- }
-
- @Override
- public boolean canLocateSigningKey(@Nullable String gpgSigningKey,
- PersonIdent committer, CredentialsProvider credentialsProvider)
- throws CanceledException {
- try {
- return canLocateSigningKey(gpgSigningKey, committer,
- credentialsProvider, null);
- } catch (UnsupportedSigningFormatException e) {
- // Cannot occur with a null config
- return false;
- }
- }
-
- @Override
- public boolean canLocateSigningKey(@Nullable String gpgSigningKey,
- PersonIdent committer, CredentialsProvider credentialsProvider,
- GpgConfig config)
- throws CanceledException, UnsupportedSigningFormatException {
- if (config != null && config.getKeyFormat() != GpgFormat.OPENPGP) {
- throw new UnsupportedSigningFormatException(
- JGitText.get().onlyOpenPgpSupportedForSigning);
- }
- try (BouncyCastleGpgKeyPassphrasePrompt passphrasePrompt = new BouncyCastleGpgKeyPassphrasePrompt(
- credentialsProvider)) {
- BouncyCastleGpgKey gpgKey = locateSigningKey(gpgSigningKey,
- committer, passphrasePrompt);
- return gpgKey != null;
- } catch (CanceledException e) {
- throw e;
- } catch (Exception e) {
- return false;
- }
- }
+public class BouncyCastleGpgSigner implements Signer {
private BouncyCastleGpgKey locateSigningKey(@Nullable String gpgSigningKey,
PersonIdent committer,
@@ -121,38 +63,24 @@ private BouncyCastleGpgKey locateSigningKey(@Nullable String gpgSigningKey,
}
@Override
- public void sign(@NonNull CommitBuilder commit,
- @Nullable String gpgSigningKey, @NonNull PersonIdent committer,
- CredentialsProvider credentialsProvider) throws CanceledException {
- try {
- signObject(commit, gpgSigningKey, committer, credentialsProvider,
- null);
- } catch (UnsupportedSigningFormatException e) {
- // Cannot occur with a null config
- }
- }
-
- @Override
- public void signObject(@NonNull ObjectBuilder object,
- @Nullable String gpgSigningKey, @NonNull PersonIdent committer,
- CredentialsProvider credentialsProvider, GpgConfig config)
- throws CanceledException, UnsupportedSigningFormatException {
- if (config != null && config.getKeyFormat() != GpgFormat.OPENPGP) {
- throw new UnsupportedSigningFormatException(
- JGitText.get().onlyOpenPgpSupportedForSigning);
+ public GpgSignature sign(Repository repository, GpgConfig config,
+ byte[] data, PersonIdent committer, String signingKey,
+ CredentialsProvider credentialsProvider) throws CanceledException,
+ IOException, UnsupportedSigningFormatException {
+ String gpgSigningKey = signingKey;
+ if (gpgSigningKey == null) {
+ gpgSigningKey = config.getSigningKey();
}
try (BouncyCastleGpgKeyPassphrasePrompt passphrasePrompt = new BouncyCastleGpgKeyPassphrasePrompt(
credentialsProvider)) {
BouncyCastleGpgKey gpgKey = locateSigningKey(gpgSigningKey,
- committer,
- passphrasePrompt);
+ committer, passphrasePrompt);
PGPSecretKey secretKey = gpgKey.getSecretKey();
if (secretKey == null) {
throw new JGitInternalException(
BCText.get().unableToSignCommitNoSecretKey);
}
- JcePBESecretKeyDecryptorBuilder decryptorBuilder = new JcePBESecretKeyDecryptorBuilder()
- .setProvider(BouncyCastleProvider.PROVIDER_NAME);
+ JcePBESecretKeyDecryptorBuilder decryptorBuilder = new JcePBESecretKeyDecryptorBuilder();
PGPPrivateKey privateKey = null;
if (!passphrasePrompt.hasPassphrase()) {
// Either the key is not encrypted, or it was read from the
@@ -177,8 +105,8 @@ public void signObject(@NonNull ObjectBuilder object,
PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(
new JcaPGPContentSignerBuilder(
publicKey.getAlgorithm(),
- HashAlgorithmTags.SHA256).setProvider(
- BouncyCastleProvider.PROVIDER_NAME));
+ HashAlgorithmTags.SHA256),
+ publicKey);
signatureGenerator.init(PGPSignature.BINARY_DOCUMENT, privateKey);
PGPSignatureSubpacketGenerator subpackets = new PGPSignatureSubpacketGenerator();
subpackets.setIssuerFingerprint(false, publicKey);
@@ -202,16 +130,36 @@ public void signObject(@NonNull ObjectBuilder object,
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
try (BCPGOutputStream out = new BCPGOutputStream(
new ArmoredOutputStream(buffer))) {
- signatureGenerator.update(object.build());
+ signatureGenerator.update(data);
signatureGenerator.generate().encode(out);
}
- object.setGpgSignature(new GpgSignature(buffer.toByteArray()));
- } catch (PGPException | IOException | NoSuchAlgorithmException
+ return new GpgSignature(buffer.toByteArray());
+ } catch (PGPException | NoSuchAlgorithmException
| NoSuchProviderException | URISyntaxException e) {
throw new JGitInternalException(e.getMessage(), e);
}
}
+ @Override
+ public boolean canLocateSigningKey(Repository repository, GpgConfig config,
+ PersonIdent committer, String signingKey,
+ CredentialsProvider credentialsProvider) throws CanceledException {
+ String gpgSigningKey = signingKey;
+ if (gpgSigningKey == null) {
+ gpgSigningKey = config.getSigningKey();
+ }
+ try (BouncyCastleGpgKeyPassphrasePrompt passphrasePrompt = new BouncyCastleGpgKeyPassphrasePrompt(
+ credentialsProvider)) {
+ BouncyCastleGpgKey gpgKey = locateSigningKey(gpgSigningKey,
+ committer, passphrasePrompt);
+ return gpgKey != null;
+ } catch (CanceledException e) {
+ throw e;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
static String extractSignerId(String pgpUserId) {
int from = pgpUserId.indexOf('<');
if (from >= 0) {
diff --git a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSignerFactory.java b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSignerFactory.java
new file mode 100644
index 0000000..92ab65d
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSignerFactory.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021, 2024 Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.gpg.bc.internal;
+
+import org.eclipse.jgit.lib.GpgConfig.GpgFormat;
+import org.eclipse.jgit.lib.Signer;
+import org.eclipse.jgit.lib.SignerFactory;
+
+/**
+ * Factory for creating a {@link Signer} for OPENPGP signatures based on Bouncy
+ * Castle.
+ */
+public final class BouncyCastleGpgSignerFactory implements SignerFactory {
+
+ @Override
+ public GpgFormat getType() {
+ return GpgFormat.OPENPGP;
+ }
+
+ @Override
+ public Signer create() {
+ return new BouncyCastleGpgSigner();
+ }
+}
diff --git a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/OCBPBEProtectionRemoverFactory.java b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/OCBPBEProtectionRemoverFactory.java
deleted file mode 100644
index 3924d68..0000000
--- a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/OCBPBEProtectionRemoverFactory.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2021 Thomas Wolf <thomas.wolf@paranor.ch> and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-package org.eclipse.jgit.gpg.bc.internal.keys;
-
-import java.security.NoSuchAlgorithmException;
-import java.text.MessageFormat;
-
-import javax.crypto.Cipher;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
-
-import org.bouncycastle.openpgp.PGPException;
-import org.bouncycastle.openpgp.PGPUtil;
-import org.bouncycastle.openpgp.operator.PBEProtectionRemoverFactory;
-import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor;
-import org.bouncycastle.openpgp.operator.PGPDigestCalculatorProvider;
-import org.bouncycastle.util.Arrays;
-import org.eclipse.jgit.gpg.bc.internal.BCText;
-
-/**
- * A {@link PBEProtectionRemoverFactory} using AES/OCB/NoPadding for decryption.
- * It accepts an AAD in the factory's constructor, so the factory can be used to
- * create a {@link PBESecretKeyDecryptor} only for a particular input.
- * <p>
- * For JGit's needs, this is sufficient, but for a general upstream
- * implementation that limitation might not be acceptable.
- * </p>
- */
-class OCBPBEProtectionRemoverFactory
- implements PBEProtectionRemoverFactory {
-
- private final PGPDigestCalculatorProvider calculatorProvider;
-
- private final char[] passphrase;
-
- private final byte[] aad;
-
- /**
- * Creates a new factory instance with the given parameters.
- * <p>
- * Because the AAD is given at factory level, the {@link PBESecretKeyDecryptor}s
- * created by the factory can be used to decrypt only a particular input
- * matching this AAD.
- * </p>
- *
- * @param passphrase to use for secret key derivation
- * @param calculatorProvider for computing digests
- * @param aad for the OCB decryption
- */
- OCBPBEProtectionRemoverFactory(char[] passphrase,
- PGPDigestCalculatorProvider calculatorProvider, byte[] aad) {
- this.calculatorProvider = calculatorProvider;
- this.passphrase = passphrase;
- this.aad = aad;
- }
-
- @Override
- public PBESecretKeyDecryptor createDecryptor(String protection)
- throws PGPException {
- return new PBESecretKeyDecryptor(passphrase, calculatorProvider) {
-
- @Override
- public byte[] recoverKeyData(int encAlgorithm, byte[] key,
- byte[] iv, byte[] encrypted, int encryptedOffset,
- int encryptedLength) throws PGPException {
- String algorithmName = PGPUtil
- .getSymmetricCipherName(encAlgorithm);
- byte[] decrypted = null;
- try {
- // errorprone: "Dynamically constructed transformation
- // strings are also flagged, as they may conceal an instance
- // of ECB mode."
- @SuppressWarnings("InsecureCryptoUsage")
- Cipher c = Cipher
- .getInstance(algorithmName + "/OCB/NoPadding"); //$NON-NLS-1$
- SecretKey secretKey = new SecretKeySpec(key, algorithmName);
- c.init(Cipher.DECRYPT_MODE, secretKey,
- new IvParameterSpec(iv));
- c.updateAAD(aad);
- decrypted = new byte[c.getOutputSize(encryptedLength)];
- int decryptedLength = c.update(encrypted, encryptedOffset,
- encryptedLength, decrypted);
- // doFinal() for OCB will check the MAC and throw an
- // exception if it doesn't match
- decryptedLength += c.doFinal(decrypted, decryptedLength);
- if (decryptedLength != decrypted.length) {
- throw new PGPException(MessageFormat.format(
- BCText.get().cryptWrongDecryptedLength,
- Integer.valueOf(decryptedLength),
- Integer.valueOf(decrypted.length)));
- }
- byte[] result = decrypted;
- decrypted = null; // Don't clear in finally
- return result;
- } catch (NoClassDefFoundError e) {
- String msg = MessageFormat.format(
- BCText.get().gpgNoSuchAlgorithm,
- algorithmName + "/OCB"); //$NON-NLS-1$
- throw new PGPException(msg,
- new NoSuchAlgorithmException(msg, e));
- } catch (PGPException e) {
- throw e;
- } catch (Exception e) {
- throw new PGPException(
- MessageFormat.format(BCText.get().cryptCipherError,
- e.getLocalizedMessage()),
- e);
- } finally {
- if (decrypted != null) {
- // Prevent halfway decrypted data leaking.
- Arrays.fill(decrypted, (byte) 0);
- }
- }
- }
- };
- }
-}
\ No newline at end of file
diff --git a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/SExprParser.java b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/SExprParser.java
deleted file mode 100644
index fd030ee..0000000
--- a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/SExprParser.java
+++ /dev/null
@@ -1,859 +0,0 @@
-/*
- * Copyright (c) 2000-2021 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org)
- * <p>
- * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
- * and associated documentation files (the "Software"), to deal in the Software without restriction,
- *including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- * </p>
- * <p>
- * The above copyright notice and this permission notice shall be included in all copies or substantial
- * portions of the Software.
- * </p>
- * <p>
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
- * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
- * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
- * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- * </p>
- */
-package org.eclipse.jgit.gpg.bc.internal.keys;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.math.BigInteger;
-import java.util.Date;
-
-import org.bouncycastle.asn1.ASN1ObjectIdentifier;
-import org.bouncycastle.asn1.x9.ECNamedCurveTable;
-import org.bouncycastle.bcpg.DSAPublicBCPGKey;
-import org.bouncycastle.bcpg.DSASecretBCPGKey;
-import org.bouncycastle.bcpg.ECDSAPublicBCPGKey;
-import org.bouncycastle.bcpg.ECPublicBCPGKey;
-import org.bouncycastle.bcpg.ECSecretBCPGKey;
-import org.bouncycastle.bcpg.ElGamalPublicBCPGKey;
-import org.bouncycastle.bcpg.ElGamalSecretBCPGKey;
-import org.bouncycastle.bcpg.HashAlgorithmTags;
-import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
-import org.bouncycastle.bcpg.PublicKeyPacket;
-import org.bouncycastle.bcpg.RSAPublicBCPGKey;
-import org.bouncycastle.bcpg.RSASecretBCPGKey;
-import org.bouncycastle.bcpg.S2K;
-import org.bouncycastle.bcpg.SecretKeyPacket;
-import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
-import org.bouncycastle.openpgp.PGPException;
-import org.bouncycastle.openpgp.PGPPublicKey;
-import org.bouncycastle.openpgp.PGPSecretKey;
-import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator;
-import org.bouncycastle.openpgp.operator.PBEProtectionRemoverFactory;
-import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor;
-import org.bouncycastle.openpgp.operator.PGPDigestCalculator;
-import org.bouncycastle.openpgp.operator.PGPDigestCalculatorProvider;
-import org.bouncycastle.util.Arrays;
-import org.bouncycastle.util.Strings;
-
-/**
- * A parser for secret keys stored in s-expressions. Original BouncyCastle code
- * modified by the JGit team to:
- * <ul>
- * <li>handle unencrypted DSA, EC, and ElGamal keys (upstream only handles
- * unencrypted RSA)</li>
- * <li>handle secret keys using AES/OCB as encryption (those don't have a
- * hash)</li>
- * <li>fix EC parsing to account for "flags" sub-list present for ed25519 and
- * curve25519</li>
- * <li>add support for ed25519 OIDs unknown to BouncyCastle</li>
- * </ul>
- */
-@SuppressWarnings("nls")
-public class SExprParser {
- private final PGPDigestCalculatorProvider digestProvider;
-
- /**
- * Base constructor.
- *
- * @param digestProvider
- * a provider for digest calculations. Used to confirm key
- * protection hashes.
- */
- public SExprParser(PGPDigestCalculatorProvider digestProvider) {
- this.digestProvider = digestProvider;
- }
-
- /**
- * Parse a secret key from one of the GPG S expression keys associating it
- * with the passed in public key.
- *
- * @param inputStream
- * to read from
- * @param keyProtectionRemoverFactory
- * for decrypting encrypted keys
- * @param pubKey
- * the private key should belong to
- *
- * @return a secret key object.
- * @throws IOException
- * if an IO error occurred
- * @throws PGPException
- * if some PGP error occurred
- */
- public PGPSecretKey parseSecretKey(InputStream inputStream,
- PBEProtectionRemoverFactory keyProtectionRemoverFactory,
- PGPPublicKey pubKey) throws IOException, PGPException {
- SXprUtils.skipOpenParenthesis(inputStream);
-
- String type;
-
- type = SXprUtils.readString(inputStream, inputStream.read());
- if (type.equals("protected-private-key")
- || type.equals("private-key")) {
- SXprUtils.skipOpenParenthesis(inputStream);
-
- String keyType = SXprUtils.readString(inputStream,
- inputStream.read());
- if (keyType.equals("ecc")) {
- SXprUtils.skipOpenParenthesis(inputStream);
-
- String curveID = SXprUtils.readString(inputStream,
- inputStream.read());
- String curveName = SXprUtils.readString(inputStream,
- inputStream.read());
-
- SXprUtils.skipCloseParenthesis(inputStream);
-
- byte[] qVal;
-
- SXprUtils.skipOpenParenthesis(inputStream);
-
- type = SXprUtils.readString(inputStream, inputStream.read());
- // JGit: c.f. https://github.com/bcgit/bc-java/issues/1590.
- // There may be a flags sub-list here for ed25519 or curve25519.
- if (type.equals("flags")) {
- SXprUtils.readString(inputStream, inputStream.read());
- SXprUtils.skipCloseParenthesis(inputStream);
- SXprUtils.skipOpenParenthesis(inputStream);
- type = SXprUtils.readString(inputStream,
- inputStream.read());
- }
- if (type.equals("q")) {
- qVal = SXprUtils.readBytes(inputStream, inputStream.read());
- } else {
- throw new PGPException("no q value found");
- }
-
- SXprUtils.skipCloseParenthesis(inputStream);
-
- BigInteger d = processECSecretKey(inputStream, curveID,
- curveName, qVal, keyProtectionRemoverFactory);
-
- if (curveName.startsWith("NIST ")) {
- curveName = curveName.substring("NIST ".length());
- }
-
- // JGit: BC doesn't know Ed25519 curve name.
- ASN1ObjectIdentifier curveOid = ECNamedCurveTable
- .getOID(curveName);
- if (curveOid == null) {
- curveOid = ObjectIds.getByName(curveName);
- }
- ECPublicBCPGKey basePubKey = new ECDSAPublicBCPGKey(
- curveOid,
- new BigInteger(1, qVal));
- ECPublicBCPGKey assocPubKey = (ECPublicBCPGKey) pubKey
- .getPublicKeyPacket().getKey();
- if (!ObjectIds.match(basePubKey.getCurveOID(),
- assocPubKey.getCurveOID())
- || !basePubKey.getEncodedPoint()
- .equals(assocPubKey.getEncodedPoint())) {
- throw new PGPException(
- "passed in public key does not match secret key");
- }
-
- return new PGPSecretKey(
- new SecretKeyPacket(pubKey.getPublicKeyPacket(),
- SymmetricKeyAlgorithmTags.NULL, null, null,
- new ECSecretBCPGKey(d).getEncoded()),
- pubKey);
- } else if (keyType.equals("dsa")) {
- BigInteger p = readBigInteger("p", inputStream);
- BigInteger q = readBigInteger("q", inputStream);
- BigInteger g = readBigInteger("g", inputStream);
-
- BigInteger y = readBigInteger("y", inputStream);
-
- BigInteger x = processDSASecretKey(inputStream, p, q, g, y,
- keyProtectionRemoverFactory);
-
- DSAPublicBCPGKey basePubKey = new DSAPublicBCPGKey(p, q, g, y);
- DSAPublicBCPGKey assocPubKey = (DSAPublicBCPGKey) pubKey
- .getPublicKeyPacket().getKey();
- if (!basePubKey.getP().equals(assocPubKey.getP())
- || !basePubKey.getQ().equals(assocPubKey.getQ())
- || !basePubKey.getG().equals(assocPubKey.getG())
- || !basePubKey.getY().equals(assocPubKey.getY())) {
- throw new PGPException(
- "passed in public key does not match secret key");
- }
- return new PGPSecretKey(
- new SecretKeyPacket(pubKey.getPublicKeyPacket(),
- SymmetricKeyAlgorithmTags.NULL, null, null,
- new DSASecretBCPGKey(x).getEncoded()),
- pubKey);
- } else if (keyType.equals("elg")) {
- BigInteger p = readBigInteger("p", inputStream);
- BigInteger g = readBigInteger("g", inputStream);
-
- BigInteger y = readBigInteger("y", inputStream);
-
- BigInteger x = processElGamalSecretKey(inputStream, p, g, y,
- keyProtectionRemoverFactory);
-
- ElGamalPublicBCPGKey basePubKey = new ElGamalPublicBCPGKey(p, g,
- y);
- ElGamalPublicBCPGKey assocPubKey = (ElGamalPublicBCPGKey) pubKey
- .getPublicKeyPacket().getKey();
- if (!basePubKey.getP().equals(assocPubKey.getP())
- || !basePubKey.getG().equals(assocPubKey.getG())
- || !basePubKey.getY().equals(assocPubKey.getY())) {
- throw new PGPException(
- "passed in public key does not match secret key");
- }
-
- return new PGPSecretKey(
- new SecretKeyPacket(pubKey.getPublicKeyPacket(),
- SymmetricKeyAlgorithmTags.NULL, null, null,
- new ElGamalSecretBCPGKey(x).getEncoded()),
- pubKey);
- } else if (keyType.equals("rsa")) {
- BigInteger n = readBigInteger("n", inputStream);
- BigInteger e = readBigInteger("e", inputStream);
-
- BigInteger[] values = processRSASecretKey(inputStream, n, e,
- keyProtectionRemoverFactory);
-
- // TODO: type of RSA key?
- RSAPublicBCPGKey basePubKey = new RSAPublicBCPGKey(n, e);
- RSAPublicBCPGKey assocPubKey = (RSAPublicBCPGKey) pubKey
- .getPublicKeyPacket().getKey();
- if (!basePubKey.getModulus().equals(assocPubKey.getModulus())
- || !basePubKey.getPublicExponent()
- .equals(assocPubKey.getPublicExponent())) {
- throw new PGPException(
- "passed in public key does not match secret key");
- }
-
- return new PGPSecretKey(new SecretKeyPacket(
- pubKey.getPublicKeyPacket(),
- SymmetricKeyAlgorithmTags.NULL, null, null,
- new RSASecretBCPGKey(values[0], values[1], values[2])
- .getEncoded()),
- pubKey);
- } else {
- throw new PGPException("unknown key type: " + keyType);
- }
- }
-
- throw new PGPException("unknown key type found");
- }
-
- /**
- * Parse a secret key from one of the GPG S expression keys.
- *
- * @param inputStream
- * to read from
- * @param keyProtectionRemoverFactory
- * for decrypting encrypted keys
- * @param fingerPrintCalculator
- * for calculating key fingerprints
- *
- * @return a secret key object.
- * @throws IOException
- * if an IO error occurred
- * @throws PGPException
- * if a PGP error occurred
- */
- public PGPSecretKey parseSecretKey(InputStream inputStream,
- PBEProtectionRemoverFactory keyProtectionRemoverFactory,
- KeyFingerPrintCalculator fingerPrintCalculator)
- throws IOException, PGPException {
- SXprUtils.skipOpenParenthesis(inputStream);
-
- String type;
-
- type = SXprUtils.readString(inputStream, inputStream.read());
- if (type.equals("protected-private-key")
- || type.equals("private-key")) {
- SXprUtils.skipOpenParenthesis(inputStream);
-
- String keyType = SXprUtils.readString(inputStream,
- inputStream.read());
- if (keyType.equals("ecc")) {
- SXprUtils.skipOpenParenthesis(inputStream);
-
- String curveID = SXprUtils.readString(inputStream,
- inputStream.read());
- String curveName = SXprUtils.readString(inputStream,
- inputStream.read());
-
- if (curveName.startsWith("NIST ")) {
- curveName = curveName.substring("NIST ".length());
- }
-
- SXprUtils.skipCloseParenthesis(inputStream);
-
- byte[] qVal;
-
- SXprUtils.skipOpenParenthesis(inputStream);
-
- type = SXprUtils.readString(inputStream, inputStream.read());
- // JGit: c.f. https://github.com/bcgit/bc-java/issues/1590.
- // There may be a flags sub-list here for ed25519 or curve25519.
- if (type.equals("flags")) {
- SXprUtils.readString(inputStream, inputStream.read());
- SXprUtils.skipCloseParenthesis(inputStream);
- SXprUtils.skipOpenParenthesis(inputStream);
- type = SXprUtils.readString(inputStream,
- inputStream.read());
- }
- if (type.equals("q")) {
- qVal = SXprUtils.readBytes(inputStream, inputStream.read());
- } else {
- throw new PGPException("no q value found");
- }
-
- PublicKeyPacket pubPacket = new PublicKeyPacket(
- PublicKeyAlgorithmTags.ECDSA, new Date(),
- new ECDSAPublicBCPGKey(
- ECNamedCurveTable.getOID(curveName),
- new BigInteger(1, qVal)));
-
- SXprUtils.skipCloseParenthesis(inputStream);
-
- BigInteger d = processECSecretKey(inputStream, curveID,
- curveName, qVal, keyProtectionRemoverFactory);
-
- return new PGPSecretKey(
- new SecretKeyPacket(pubPacket,
- SymmetricKeyAlgorithmTags.NULL, null, null,
- new ECSecretBCPGKey(d).getEncoded()),
- new PGPPublicKey(pubPacket, fingerPrintCalculator));
- } else if (keyType.equals("dsa")) {
- BigInteger p = readBigInteger("p", inputStream);
- BigInteger q = readBigInteger("q", inputStream);
- BigInteger g = readBigInteger("g", inputStream);
-
- BigInteger y = readBigInteger("y", inputStream);
-
- BigInteger x = processDSASecretKey(inputStream, p, q, g, y,
- keyProtectionRemoverFactory);
-
- PublicKeyPacket pubPacket = new PublicKeyPacket(
- PublicKeyAlgorithmTags.DSA, new Date(),
- new DSAPublicBCPGKey(p, q, g, y));
-
- return new PGPSecretKey(
- new SecretKeyPacket(pubPacket,
- SymmetricKeyAlgorithmTags.NULL, null, null,
- new DSASecretBCPGKey(x).getEncoded()),
- new PGPPublicKey(pubPacket, fingerPrintCalculator));
- } else if (keyType.equals("elg")) {
- BigInteger p = readBigInteger("p", inputStream);
- BigInteger g = readBigInteger("g", inputStream);
-
- BigInteger y = readBigInteger("y", inputStream);
-
- BigInteger x = processElGamalSecretKey(inputStream, p, g, y,
- keyProtectionRemoverFactory);
-
- PublicKeyPacket pubPacket = new PublicKeyPacket(
- PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT, new Date(),
- new ElGamalPublicBCPGKey(p, g, y));
-
- return new PGPSecretKey(
- new SecretKeyPacket(pubPacket,
- SymmetricKeyAlgorithmTags.NULL, null, null,
- new ElGamalSecretBCPGKey(x).getEncoded()),
- new PGPPublicKey(pubPacket, fingerPrintCalculator));
- } else if (keyType.equals("rsa")) {
- BigInteger n = readBigInteger("n", inputStream);
- BigInteger e = readBigInteger("e", inputStream);
-
- BigInteger[] values = processRSASecretKey(inputStream, n, e,
- keyProtectionRemoverFactory);
-
- // TODO: type of RSA key?
- PublicKeyPacket pubPacket = new PublicKeyPacket(
- PublicKeyAlgorithmTags.RSA_GENERAL, new Date(),
- new RSAPublicBCPGKey(n, e));
-
- return new PGPSecretKey(
- new SecretKeyPacket(pubPacket,
- SymmetricKeyAlgorithmTags.NULL, null, null,
- new RSASecretBCPGKey(values[0], values[1],
- values[2]).getEncoded()),
- new PGPPublicKey(pubPacket, fingerPrintCalculator));
- } else {
- throw new PGPException("unknown key type: " + keyType);
- }
- }
-
- throw new PGPException("unknown key type found");
- }
-
- private BigInteger readBigInteger(String expectedType,
- InputStream inputStream) throws IOException, PGPException {
- SXprUtils.skipOpenParenthesis(inputStream);
-
- String type = SXprUtils.readString(inputStream, inputStream.read());
- if (!type.equals(expectedType)) {
- throw new PGPException(expectedType + " value expected");
- }
-
- byte[] nBytes = SXprUtils.readBytes(inputStream, inputStream.read());
- BigInteger v = new BigInteger(1, nBytes);
-
- SXprUtils.skipCloseParenthesis(inputStream);
-
- return v;
- }
-
- private static byte[][] extractData(InputStream inputStream,
- PBEProtectionRemoverFactory keyProtectionRemoverFactory)
- throws PGPException, IOException {
- byte[] data;
- byte[] protectedAt = null;
-
- SXprUtils.skipOpenParenthesis(inputStream);
-
- String type = SXprUtils.readString(inputStream, inputStream.read());
- if (type.equals("protected")) {
- String protection = SXprUtils.readString(inputStream,
- inputStream.read());
-
- SXprUtils.skipOpenParenthesis(inputStream);
-
- S2K s2k = SXprUtils.parseS2K(inputStream);
-
- byte[] iv = SXprUtils.readBytes(inputStream, inputStream.read());
-
- SXprUtils.skipCloseParenthesis(inputStream);
-
- byte[] secKeyData = SXprUtils.readBytes(inputStream,
- inputStream.read());
-
- SXprUtils.skipCloseParenthesis(inputStream);
-
- PBESecretKeyDecryptor keyDecryptor = keyProtectionRemoverFactory
- .createDecryptor(protection);
-
- // TODO: recognise other algorithms
- byte[] key = keyDecryptor.makeKeyFromPassPhrase(
- SymmetricKeyAlgorithmTags.AES_128, s2k);
-
- data = keyDecryptor.recoverKeyData(
- SymmetricKeyAlgorithmTags.AES_128, key, iv, secKeyData, 0,
- secKeyData.length);
-
- // check if protected at is present
- if (inputStream.read() == '(') {
- ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-
- bOut.write('(');
- int ch;
- while ((ch = inputStream.read()) >= 0 && ch != ')') {
- bOut.write(ch);
- }
-
- if (ch != ')') {
- throw new IOException("unexpected end to SExpr");
- }
-
- bOut.write(')');
-
- protectedAt = bOut.toByteArray();
- }
-
- SXprUtils.skipCloseParenthesis(inputStream);
- SXprUtils.skipCloseParenthesis(inputStream);
- } else if (type.equals("d") || type.equals("x")) {
- // JGit modification: unencrypted DSA or ECC keys can have an "x"
- // here
- return null;
- } else {
- throw new PGPException("protected block not found");
- }
-
- return new byte[][] { data, protectedAt };
- }
-
- private BigInteger processDSASecretKey(InputStream inputStream,
- BigInteger p, BigInteger q, BigInteger g, BigInteger y,
- PBEProtectionRemoverFactory keyProtectionRemoverFactory)
- throws IOException, PGPException {
- String type;
- byte[][] basicData = extractData(inputStream,
- keyProtectionRemoverFactory);
-
- // JGit modification: handle unencrypted DSA keys
- if (basicData == null) {
- byte[] nBytes = SXprUtils.readBytes(inputStream,
- inputStream.read());
- BigInteger x = new BigInteger(1, nBytes);
- SXprUtils.skipCloseParenthesis(inputStream);
- return x;
- }
-
- byte[] keyData = basicData[0];
- byte[] protectedAt = basicData[1];
-
- //
- // parse the secret key S-expr
- //
- InputStream keyIn = new ByteArrayInputStream(keyData);
-
- SXprUtils.skipOpenParenthesis(keyIn);
- SXprUtils.skipOpenParenthesis(keyIn);
-
- BigInteger x = readBigInteger("x", keyIn);
-
- SXprUtils.skipCloseParenthesis(keyIn);
-
- // JGit modification: OCB-encrypted keys don't have and don't need a
- // hash
- if (keyProtectionRemoverFactory instanceof OCBPBEProtectionRemoverFactory) {
- return x;
- }
-
- SXprUtils.skipOpenParenthesis(keyIn);
- type = SXprUtils.readString(keyIn, keyIn.read());
-
- if (!type.equals("hash")) {
- throw new PGPException("hash keyword expected");
- }
- type = SXprUtils.readString(keyIn, keyIn.read());
-
- if (!type.equals("sha1")) {
- throw new PGPException("hash keyword expected");
- }
-
- byte[] hashBytes = SXprUtils.readBytes(keyIn, keyIn.read());
-
- SXprUtils.skipCloseParenthesis(keyIn);
-
- if (digestProvider != null) {
- PGPDigestCalculator digestCalculator = digestProvider
- .get(HashAlgorithmTags.SHA1);
-
- OutputStream dOut = digestCalculator.getOutputStream();
-
- dOut.write(Strings.toByteArray("(3:dsa"));
- writeCanonical(dOut, "p", p);
- writeCanonical(dOut, "q", q);
- writeCanonical(dOut, "g", g);
- writeCanonical(dOut, "y", y);
- writeCanonical(dOut, "x", x);
-
- // check protected-at
- if (protectedAt != null) {
- dOut.write(protectedAt);
- }
-
- dOut.write(Strings.toByteArray(")"));
-
- byte[] check = digestCalculator.getDigest();
- if (!Arrays.constantTimeAreEqual(check, hashBytes)) {
- throw new PGPException(
- "checksum on protected data failed in SExpr");
- }
- }
-
- return x;
- }
-
- private BigInteger processElGamalSecretKey(InputStream inputStream,
- BigInteger p, BigInteger g, BigInteger y,
- PBEProtectionRemoverFactory keyProtectionRemoverFactory)
- throws IOException, PGPException {
- String type;
- byte[][] basicData = extractData(inputStream,
- keyProtectionRemoverFactory);
-
- // JGit modification: handle unencrypted EC keys
- if (basicData == null) {
- byte[] nBytes = SXprUtils.readBytes(inputStream,
- inputStream.read());
- BigInteger x = new BigInteger(1, nBytes);
- SXprUtils.skipCloseParenthesis(inputStream);
- return x;
- }
-
- byte[] keyData = basicData[0];
- byte[] protectedAt = basicData[1];
-
- //
- // parse the secret key S-expr
- //
- InputStream keyIn = new ByteArrayInputStream(keyData);
-
- SXprUtils.skipOpenParenthesis(keyIn);
- SXprUtils.skipOpenParenthesis(keyIn);
-
- BigInteger x = readBigInteger("x", keyIn);
-
- SXprUtils.skipCloseParenthesis(keyIn);
-
- // JGit modification: OCB-encrypted keys don't have and don't need a
- // hash
- if (keyProtectionRemoverFactory instanceof OCBPBEProtectionRemoverFactory) {
- return x;
- }
-
- SXprUtils.skipOpenParenthesis(keyIn);
- type = SXprUtils.readString(keyIn, keyIn.read());
-
- if (!type.equals("hash")) {
- throw new PGPException("hash keyword expected");
- }
- type = SXprUtils.readString(keyIn, keyIn.read());
-
- if (!type.equals("sha1")) {
- throw new PGPException("hash keyword expected");
- }
-
- byte[] hashBytes = SXprUtils.readBytes(keyIn, keyIn.read());
-
- SXprUtils.skipCloseParenthesis(keyIn);
-
- if (digestProvider != null) {
- PGPDigestCalculator digestCalculator = digestProvider
- .get(HashAlgorithmTags.SHA1);
-
- OutputStream dOut = digestCalculator.getOutputStream();
-
- dOut.write(Strings.toByteArray("(3:elg"));
- writeCanonical(dOut, "p", p);
- writeCanonical(dOut, "g", g);
- writeCanonical(dOut, "y", y);
- writeCanonical(dOut, "x", x);
-
- // check protected-at
- if (protectedAt != null) {
- dOut.write(protectedAt);
- }
-
- dOut.write(Strings.toByteArray(")"));
-
- byte[] check = digestCalculator.getDigest();
- if (!Arrays.constantTimeAreEqual(check, hashBytes)) {
- throw new PGPException(
- "checksum on protected data failed in SExpr");
- }
- }
-
- return x;
- }
-
- private BigInteger processECSecretKey(InputStream inputStream,
- String curveID, String curveName, byte[] qVal,
- PBEProtectionRemoverFactory keyProtectionRemoverFactory)
- throws IOException, PGPException {
- String type;
-
- byte[][] basicData = extractData(inputStream,
- keyProtectionRemoverFactory);
-
- // JGit modification: handle unencrypted EC keys
- if (basicData == null) {
- byte[] nBytes = SXprUtils.readBytes(inputStream,
- inputStream.read());
- BigInteger d = new BigInteger(1, nBytes);
- SXprUtils.skipCloseParenthesis(inputStream);
- return d;
- }
-
- byte[] keyData = basicData[0];
- byte[] protectedAt = basicData[1];
-
- //
- // parse the secret key S-expr
- //
- InputStream keyIn = new ByteArrayInputStream(keyData);
-
- SXprUtils.skipOpenParenthesis(keyIn);
- SXprUtils.skipOpenParenthesis(keyIn);
- BigInteger d = readBigInteger("d", keyIn);
- SXprUtils.skipCloseParenthesis(keyIn);
-
- // JGit modification: OCB-encrypted keys don't have and don't need a
- // hash
- if (keyProtectionRemoverFactory instanceof OCBPBEProtectionRemoverFactory) {
- return d;
- }
-
- SXprUtils.skipOpenParenthesis(keyIn);
-
- type = SXprUtils.readString(keyIn, keyIn.read());
-
- if (!type.equals("hash")) {
- throw new PGPException("hash keyword expected");
- }
- type = SXprUtils.readString(keyIn, keyIn.read());
-
- if (!type.equals("sha1")) {
- throw new PGPException("hash keyword expected");
- }
-
- byte[] hashBytes = SXprUtils.readBytes(keyIn, keyIn.read());
-
- SXprUtils.skipCloseParenthesis(keyIn);
-
- if (digestProvider != null) {
- PGPDigestCalculator digestCalculator = digestProvider
- .get(HashAlgorithmTags.SHA1);
-
- OutputStream dOut = digestCalculator.getOutputStream();
-
- dOut.write(Strings.toByteArray("(3:ecc"));
-
- dOut.write(Strings.toByteArray("(" + curveID.length() + ":"
- + curveID + curveName.length() + ":" + curveName + ")"));
-
- writeCanonical(dOut, "q", qVal);
- writeCanonical(dOut, "d", d);
-
- // check protected-at
- if (protectedAt != null) {
- dOut.write(protectedAt);
- }
-
- dOut.write(Strings.toByteArray(")"));
-
- byte[] check = digestCalculator.getDigest();
-
- if (!Arrays.constantTimeAreEqual(check, hashBytes)) {
- throw new PGPException(
- "checksum on protected data failed in SExpr");
- }
- }
-
- return d;
- }
-
- private BigInteger[] processRSASecretKey(InputStream inputStream,
- BigInteger n, BigInteger e,
- PBEProtectionRemoverFactory keyProtectionRemoverFactory)
- throws IOException, PGPException {
- String type;
- byte[][] basicData = extractData(inputStream,
- keyProtectionRemoverFactory);
-
- byte[] keyData;
- byte[] protectedAt = null;
-
- InputStream keyIn;
- BigInteger d;
-
- if (basicData == null) {
- keyIn = inputStream;
- byte[] nBytes = SXprUtils.readBytes(inputStream,
- inputStream.read());
- d = new BigInteger(1, nBytes);
-
- SXprUtils.skipCloseParenthesis(inputStream);
-
- } else {
- keyData = basicData[0];
- protectedAt = basicData[1];
-
- keyIn = new ByteArrayInputStream(keyData);
-
- SXprUtils.skipOpenParenthesis(keyIn);
- SXprUtils.skipOpenParenthesis(keyIn);
- d = readBigInteger("d", keyIn);
- }
-
- //
- // parse the secret key S-expr
- //
-
- BigInteger p = readBigInteger("p", keyIn);
- BigInteger q = readBigInteger("q", keyIn);
- BigInteger u = readBigInteger("u", keyIn);
-
- // JGit modification: OCB-encrypted keys don't have and don't need a
- // hash
- if (basicData == null
- || keyProtectionRemoverFactory instanceof OCBPBEProtectionRemoverFactory) {
- return new BigInteger[] { d, p, q, u };
- }
-
- SXprUtils.skipCloseParenthesis(keyIn);
-
- SXprUtils.skipOpenParenthesis(keyIn);
- type = SXprUtils.readString(keyIn, keyIn.read());
-
- if (!type.equals("hash")) {
- throw new PGPException("hash keyword expected");
- }
- type = SXprUtils.readString(keyIn, keyIn.read());
-
- if (!type.equals("sha1")) {
- throw new PGPException("hash keyword expected");
- }
-
- byte[] hashBytes = SXprUtils.readBytes(keyIn, keyIn.read());
-
- SXprUtils.skipCloseParenthesis(keyIn);
-
- if (digestProvider != null) {
- PGPDigestCalculator digestCalculator = digestProvider
- .get(HashAlgorithmTags.SHA1);
-
- OutputStream dOut = digestCalculator.getOutputStream();
-
- dOut.write(Strings.toByteArray("(3:rsa"));
-
- writeCanonical(dOut, "n", n);
- writeCanonical(dOut, "e", e);
- writeCanonical(dOut, "d", d);
- writeCanonical(dOut, "p", p);
- writeCanonical(dOut, "q", q);
- writeCanonical(dOut, "u", u);
-
- // check protected-at
- if (protectedAt != null) {
- dOut.write(protectedAt);
- }
-
- dOut.write(Strings.toByteArray(")"));
-
- byte[] check = digestCalculator.getDigest();
-
- if (!Arrays.constantTimeAreEqual(check, hashBytes)) {
- throw new PGPException(
- "checksum on protected data failed in SExpr");
- }
- }
-
- return new BigInteger[] { d, p, q, u };
- }
-
- private void writeCanonical(OutputStream dOut, String label, BigInteger i)
- throws IOException {
- writeCanonical(dOut, label, i.toByteArray());
- }
-
- private void writeCanonical(OutputStream dOut, String label, byte[] data)
- throws IOException {
- dOut.write(Strings.toByteArray(
- "(" + label.length() + ":" + label + data.length + ":"));
- dOut.write(data);
- dOut.write(Strings.toByteArray(")"));
- }
-}
diff --git a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/SXprUtils.java b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/SXprUtils.java
deleted file mode 100644
index 220aa28..0000000
--- a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/SXprUtils.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (c) 2000-2021 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org)
- * <p>
- * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
- * and associated documentation files (the "Software"), to deal in the Software without restriction,
- *including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- * </p>
- * <p>
- * The above copyright notice and this permission notice shall be included in all copies or substantial
- * portions of the Software.
- * </p>
- * <p>
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
- * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
- * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
- * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- * </p>
- */
-package org.eclipse.jgit.gpg.bc.internal.keys;
-
-// This class is an unmodified copy from Bouncy Castle; needed because it's package-visible only and used by SExprParser.
-
-import java.io.IOException;
-import java.io.InputStream;
-
-import org.bouncycastle.bcpg.HashAlgorithmTags;
-import org.bouncycastle.bcpg.S2K;
-import org.bouncycastle.util.io.Streams;
-
-/**
- * Utility functions for looking a S-expression keys. This class will move when
- * it finds a better home!
- * <p>
- * Format documented here:
- * http://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=agent/keyformat.txt;h=42c4b1f06faf1bbe71ffadc2fee0fad6bec91a97;hb=refs/heads/master
- * </p>
- */
-class SXprUtils {
- private static int readLength(InputStream in, int ch) throws IOException {
- int len = ch - '0';
-
- while ((ch = in.read()) >= 0 && ch != ':') {
- len = len * 10 + ch - '0';
- }
-
- return len;
- }
-
- static String readString(InputStream in, int ch) throws IOException {
- int len = readLength(in, ch);
-
- char[] chars = new char[len];
-
- for (int i = 0; i != chars.length; i++) {
- chars[i] = (char) in.read();
- }
-
- return new String(chars);
- }
-
- static byte[] readBytes(InputStream in, int ch) throws IOException {
- int len = readLength(in, ch);
-
- byte[] data = new byte[len];
-
- Streams.readFully(in, data);
-
- return data;
- }
-
- static S2K parseS2K(InputStream in) throws IOException {
- skipOpenParenthesis(in);
-
- // Algorithm is hard-coded to SHA1 below anyway.
- readString(in, in.read());
- byte[] iv = readBytes(in, in.read());
- final long iterationCount = Long.parseLong(readString(in, in.read()));
-
- skipCloseParenthesis(in);
-
- // we have to return the actual iteration count provided.
- S2K s2k = new S2K(HashAlgorithmTags.SHA1, iv, (int) iterationCount) {
- @Override
- public long getIterationCount() {
- return iterationCount;
- }
- };
-
- return s2k;
- }
-
- static void skipOpenParenthesis(InputStream in) throws IOException {
- int ch = in.read();
- if (ch != '(') {
- throw new IOException(
- "unknown character encountered: " + (char) ch); //$NON-NLS-1$
- }
- }
-
- static void skipCloseParenthesis(InputStream in) throws IOException {
- int ch = in.read();
- if (ch != ')') {
- throw new IOException("unknown character encountered"); //$NON-NLS-1$
- }
- }
-}
diff --git a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/SecretKeys.java b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/SecretKeys.java
index a659d38..a56e418 100644
--- a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/SecretKeys.java
+++ b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/SecretKeys.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 Thomas Wolf <thomas.wolf@paranor.ch> and others
+ * Copyright (C) 2021, 2024 Thomas Wolf <twolf@apache.org> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -9,34 +9,36 @@
*/
package org.eclipse.jgit.gpg.bc.internal.keys;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.EOFException;
+import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.io.StreamCorruptedException;
import java.net.URISyntaxException;
-import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
-import java.util.Arrays;
+import org.bouncycastle.bcpg.ECPublicBCPGKey;
+import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
+import org.bouncycastle.gpg.PGPSecretKeyParser;
+import org.bouncycastle.gpg.SExprParser;
+import org.bouncycastle.openpgp.OpenedPGPKeyData;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.operator.PBEProtectionRemoverFactory;
import org.bouncycastle.openpgp.operator.PGPDigestCalculatorProvider;
+import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
import org.bouncycastle.openpgp.operator.jcajce.JcePBEProtectionRemoverFactory;
-import org.bouncycastle.util.io.Streams;
import org.eclipse.jgit.api.errors.CanceledException;
import org.eclipse.jgit.errors.UnsupportedCredentialItem;
import org.eclipse.jgit.gpg.bc.internal.BCText;
-import org.eclipse.jgit.util.RawParseUtils;
/**
* Utilities for reading GPG secret keys from a gpg-agent key file.
*/
public final class SecretKeys {
+ // Maximum nesting depth of sub-lists in an S-Expression for a secret key.
+ private static final int MAX_SEXPR_NESTING = 20;
+
private SecretKeys() {
// No instantiation.
}
@@ -64,12 +66,6 @@ public interface PassphraseSupplier {
UnsupportedCredentialItem, URISyntaxException;
}
- private static final byte[] PROTECTED_KEY = "protected-private-key" //$NON-NLS-1$
- .getBytes(StandardCharsets.US_ASCII);
-
- private static final byte[] OCB_PROTECTED = "openpgp-s2k3-ocb-aes" //$NON-NLS-1$
- .getBytes(StandardCharsets.US_ASCII);
-
/**
* Reads a GPG secret key from the given stream.
*
@@ -99,500 +95,59 @@ public static PGPSecretKey readSecretKey(InputStream in,
PassphraseSupplier passphraseSupplier, PGPPublicKey publicKey)
throws IOException, PGPException, CanceledException,
UnsupportedCredentialItem, URISyntaxException {
- byte[] data = Streams.readAll(in);
- if (data.length == 0) {
- throw new EOFException();
- } else if (data.length < 4 + PROTECTED_KEY.length) {
- // +4 for "(21:" for a binary protected key
- throw new IOException(
- MessageFormat.format(BCText.get().secretKeyTooShort,
- Integer.toUnsignedString(data.length)));
+ OpenedPGPKeyData data;
+ try (InputStream keyIn = new BufferedInputStream(in)) {
+ data = PGPSecretKeyParser.parse(keyIn, MAX_SEXPR_NESTING);
}
- SExprParser parser = new SExprParser(calculatorProvider);
- byte firstChar = data[0];
- try {
- if (firstChar == '(') {
- // Binary format.
- PBEProtectionRemoverFactory decryptor = null;
- if (matches(data, 4, PROTECTED_KEY)) {
- // AES/CBC encrypted.
- decryptor = new JcePBEProtectionRemoverFactory(
- passphraseSupplier.getPassphrase(),
- calculatorProvider);
- }
- try (InputStream sIn = new ByteArrayInputStream(data)) {
- return parser.parseSecretKey(sIn, decryptor, publicKey);
- }
- }
- // Assume it's the new key-value format.
- try (ByteArrayInputStream keyIn = new ByteArrayInputStream(data)) {
- byte[] rawData = keyFromNameValueFormat(keyIn);
- if (!matches(rawData, 1, PROTECTED_KEY)) {
- // Not encrypted human-readable format.
- try (InputStream sIn = new ByteArrayInputStream(
- convertSexpression(rawData))) {
- return parser.parseSecretKey(sIn, null, publicKey);
- }
- }
- // An encrypted key from a key-value file. Most likely AES/OCB
- // encrypted.
- boolean isOCB[] = { false };
- byte[] sExp = convertSexpression(rawData, isOCB);
- PBEProtectionRemoverFactory decryptor;
- if (isOCB[0]) {
- decryptor = new OCBPBEProtectionRemoverFactory(
- passphraseSupplier.getPassphrase(),
- calculatorProvider, getAad(sExp));
- } else {
- decryptor = new JcePBEProtectionRemoverFactory(
- passphraseSupplier.getPassphrase(),
- calculatorProvider);
- }
- try (InputStream sIn = new ByteArrayInputStream(sExp)) {
- return parser.parseSecretKey(sIn, decryptor, publicKey);
- }
- }
- } catch (IOException e) {
- throw new PGPException(e.getLocalizedMessage(), e);
+ PBEProtectionRemoverFactory decryptor = null;
+ if (isProtected(data)) {
+ decryptor = new JcePBEProtectionRemoverFactory(
+ passphraseSupplier.getPassphrase(), calculatorProvider);
}
- }
-
- /**
- * Extract the AAD for the OCB decryption from an s-expression.
- *
- * @param sExp
- * buffer containing a valid binary s-expression
- * @return the AAD
- */
- private static byte[] getAad(byte[] sExp) {
- // Given a key
- // @formatter:off
- // (protected-private-key (rsa ... (protected openpgp-s2k3-ocb-aes ... )(protected-at ...)))
- // A B C D
- // The AAD is [A..B)[C..D). (From the binary serialized form.)
- // @formatter:on
- int i = 1; // Skip initial '('
- while (sExp[i] != '(') {
- i++;
- }
- int aadStart = i++;
- int aadEnd = skip(sExp, aadStart);
- byte[] protectedPrefix = "(9:protected" //$NON-NLS-1$
- .getBytes(StandardCharsets.US_ASCII);
- while (!matches(sExp, i, protectedPrefix)) {
- i++;
- }
- int protectedStart = i;
- int protectedEnd = skip(sExp, protectedStart);
- byte[] aadData = new byte[aadEnd - aadStart
- - (protectedEnd - protectedStart)];
- System.arraycopy(sExp, aadStart, aadData, 0, protectedStart - aadStart);
- System.arraycopy(sExp, protectedEnd, aadData, protectedStart - aadStart,
- aadEnd - protectedEnd);
- return aadData;
- }
-
- /**
- * Skips a list including nested lists.
- *
- * @param sExp
- * buffer containing valid binary s-expression data
- * @param start
- * index of the opening '(' of the list to skip
- * @return the index after the closing ')' of the skipped list
- */
- private static int skip(byte[] sExp, int start) {
- int i = start + 1;
- int depth = 1;
- while (depth > 0) {
- switch (sExp[i]) {
- case '(':
- depth++;
- break;
- case ')':
- depth--;
- break;
- default:
- // We must be on a length
- int j = i;
- while (sExp[j] >= '0' && sExp[j] <= '9') {
- j++;
- }
- // j is on the colon
- int length = Integer.parseInt(
- new String(sExp, i, j - i, StandardCharsets.US_ASCII));
- i = j + length;
+ switch (publicKey.getAlgorithm()) {
+ case PublicKeyAlgorithmTags.EDDSA_LEGACY:
+ case PublicKeyAlgorithmTags.Ed25519:
+ // If we let Bouncy Castle check whether the secret key matches the
+ // given public key it may get into trouble in some cases with
+ // ed25519 keys. It appears that we may end up with secret keys
+ // using the official RFC 8410 OID for ed25519, "1.3.101.112", while
+ // the public key passed in may have a non-standard OpenPGP-specific
+ // OID "1.3.6.1.4.1.11591.15.1", or vice versa. Bouncy Castle then
+ // throws an exception because of the different OIDs.
+ //
+ // The work-around is to just read the secret key, and double-check
+ // later that the OIDs are compatible and the curve points match.
+ PGPSecretKey secret = data.getKeyData(null, calculatorProvider,
+ decryptor, new JcaKeyFingerprintCalculator(),
+ MAX_SEXPR_NESTING);
+ PGPPublicKey pubKeyRead = secret.getPublicKey();
+ int algoRead = pubKeyRead.getAlgorithm();
+ if (algoRead != PublicKeyAlgorithmTags.EDDSA_LEGACY
+ && algoRead != PublicKeyAlgorithmTags.Ed25519) {
+ throw new PGPException(BCText.get().keyAlgorithmMismatch);
}
- i++;
- }
- return i;
- }
-
- /**
- * Checks whether the {@code needle} matches {@code src} at offset
- * {@code from}.
- *
- * @param src
- * to match against {@code needle}
- * @param from
- * position in {@code src} to start matching
- * @param needle
- * to match against
- * @return {@code true} if {@code src} contains {@code needle} at position
- * {@code from}, {@code false} otherwise
- */
- private static boolean matches(byte[] src, int from, byte[] needle) {
- if (from < 0 || from + needle.length > src.length) {
- return false;
- }
- return org.bouncycastle.util.Arrays.constantTimeAreEqual(needle.length,
- src, from, needle, 0);
- }
-
- /**
- * Converts a human-readable serialized s-expression into a binary
- * serialized s-expression.
- *
- * @param humanForm
- * to convert
- * @return the converted s-expression
- * @throws IOException
- * if the conversion fails
- */
- private static byte[] convertSexpression(byte[] humanForm)
- throws IOException {
- boolean[] isOCB = { false };
- return convertSexpression(humanForm, isOCB);
- }
-
- /**
- * Converts a human-readable serialized s-expression into a binary
- * serialized s-expression.
- *
- * @param humanForm
- * to convert
- * @param isOCB
- * returns whether the s-expression specified AES/OCB encryption
- * @return the converted s-expression
- * @throws IOException
- * if the conversion fails
- */
- private static byte[] convertSexpression(byte[] humanForm, boolean[] isOCB)
- throws IOException {
- int pos = 0;
- try (ByteArrayOutputStream out = new ByteArrayOutputStream(
- humanForm.length)) {
- while (pos < humanForm.length) {
- byte b = humanForm[pos];
- if (b == '(' || b == ')') {
- out.write(b);
- pos++;
- } else if (isGpgSpace(b)) {
- pos++;
- } else if (b == '#') {
- // Hex value follows up to the next #
- int i = ++pos;
- while (i < humanForm.length && isHex(humanForm[i])) {
- i++;
- }
- if (i == pos || humanForm[i] != '#') {
- throw new StreamCorruptedException(
- BCText.get().sexprHexNotClosed);
- }
- if ((i - pos) % 2 != 0) {
- throw new StreamCorruptedException(
- BCText.get().sexprHexOdd);
- }
- int l = (i - pos) / 2;
- out.write(Integer.toString(l)
- .getBytes(StandardCharsets.US_ASCII));
- out.write(':');
- while (pos < i) {
- int x = (nibble(humanForm[pos]) << 4)
- | nibble(humanForm[pos + 1]);
- pos += 2;
- out.write(x);
- }
- pos = i + 1;
- } else if (isTokenChar(b)) {
- // Scan the token
- int start = pos++;
- while (pos < humanForm.length
- && isTokenChar(humanForm[pos])) {
- pos++;
- }
- int l = pos - start;
- if (pos - start == OCB_PROTECTED.length
- && matches(humanForm, start, OCB_PROTECTED)) {
- isOCB[0] = true;
- }
- out.write(Integer.toString(l)
- .getBytes(StandardCharsets.US_ASCII));
- out.write(':');
- out.write(humanForm, start, pos - start);
- } else if (b == '"') {
- // Potentially quoted string.
- int start = ++pos;
- boolean escaped = false;
- while (pos < humanForm.length
- && (escaped || humanForm[pos] != '"')) {
- int ch = humanForm[pos++];
- escaped = !escaped && ch == '\\';
- }
- if (pos >= humanForm.length) {
- throw new StreamCorruptedException(
- BCText.get().sexprStringNotClosed);
- }
- // start is on the first character of the string, pos on the
- // closing quote.
- byte[] dq = dequote(humanForm, start, pos);
- out.write(Integer.toString(dq.length)
- .getBytes(StandardCharsets.US_ASCII));
- out.write(':');
- out.write(dq);
- pos++;
- } else {
- throw new StreamCorruptedException(
- MessageFormat.format(BCText.get().sexprUnhandled,
- Integer.toHexString(b & 0xFF)));
- }
+ ECPublicBCPGKey ec1 = (ECPublicBCPGKey) publicKey
+ .getPublicKeyPacket().getKey();
+ ECPublicBCPGKey ec2 = (ECPublicBCPGKey) pubKeyRead
+ .getPublicKeyPacket().getKey();
+ if (!ObjectIds.match(ec1.getCurveOID(), ec2.getCurveOID())
+ || !ec1.getEncodedPoint().equals(ec2.getEncodedPoint())) {
+ throw new PGPException(
+ MessageFormat.format(BCText.get().keyMismatch,
+ ec1.getCurveOID(), ec1.getEncodedPoint(),
+ ec2.getCurveOID(), ec2.getEncodedPoint()));
}
- return out.toByteArray();
- }
- }
-
- /**
- * GPG-style string de-quoting, which is basically C-style, with some
- * literal CR/LF escaping.
- *
- * @param in
- * buffer containing the quoted string
- * @param from
- * index after the opening quote in {@code in}
- * @param to
- * index of the closing quote in {@code in}
- * @return the dequoted raw string value
- * @throws StreamCorruptedException
- * if object stream is corrupt
- */
- private static byte[] dequote(byte[] in, int from, int to)
- throws StreamCorruptedException {
- // Result must be shorter or have the same length
- byte[] out = new byte[to - from];
- int j = 0;
- int i = from;
- while (i < to) {
- byte b = in[i++];
- if (b != '\\') {
- out[j++] = b;
- continue;
- }
- if (i == to) {
- throw new StreamCorruptedException(
- BCText.get().sexprStringInvalidEscapeAtEnd);
- }
- b = in[i++];
- switch (b) {
- case 'b':
- out[j++] = '\b';
- break;
- case 'f':
- out[j++] = '\f';
- break;
- case 'n':
- out[j++] = '\n';
- break;
- case 'r':
- out[j++] = '\r';
- break;
- case 't':
- out[j++] = '\t';
- break;
- case 'v':
- out[j++] = 0x0B;
- break;
- case '"':
- case '\'':
- case '\\':
- out[j++] = b;
- break;
- case '\r':
- // Escaped literal line end. If an LF is following, skip that,
- // too.
- if (i < to && in[i] == '\n') {
- i++;
- }
- break;
- case '\n':
- // Same for LF possibly followed by CR.
- if (i < to && in[i] == '\r') {
- i++;
- }
- break;
- case 'x':
- if (i + 1 >= to || !isHex(in[i]) || !isHex(in[i + 1])) {
- throw new StreamCorruptedException(
- BCText.get().sexprStringInvalidHexEscape);
- }
- out[j++] = (byte) ((nibble(in[i]) << 4) | nibble(in[i + 1]));
- i += 2;
- break;
- case '0':
- case '1':
- case '2':
- case '3':
- if (i + 2 >= to || !isOctal(in[i]) || !isOctal(in[i + 1])
- || !isOctal(in[i + 2])) {
- throw new StreamCorruptedException(
- BCText.get().sexprStringInvalidOctalEscape);
- }
- out[j++] = (byte) (((((in[i] - '0') << 3)
- | (in[i + 1] - '0')) << 3) | (in[i + 2] - '0'));
- i += 3;
- break;
- default:
- throw new StreamCorruptedException(MessageFormat.format(
- BCText.get().sexprStringInvalidEscape,
- Integer.toHexString(b & 0xFF)));
- }
- }
- return Arrays.copyOf(out, j);
- }
-
- /**
- * Extracts the key from a GPG name-value-pair key file.
- * <p>
- * Package-visible for tests only.
- * </p>
- *
- * @param in
- * {@link InputStream} to read from; should be buffered
- * @return the raw key data as extracted from the file
- * @throws IOException
- * if the {@code in} stream cannot be read or does not contain a
- * key
- */
- static byte[] keyFromNameValueFormat(InputStream in) throws IOException {
- // It would be nice if we could use RawParseUtils here, but GPG compares
- // names case-insensitively. We're only interested in the "Key:"
- // name-value pair.
- int[] nameLow = { 'k', 'e', 'y', ':' };
- int[] nameCap = { 'K', 'E', 'Y', ':' };
- int nameIdx = 0;
- for (;;) {
- int next = in.read();
- if (next < 0) {
- throw new EOFException();
- }
- if (next == '\n') {
- nameIdx = 0;
- } else if (nameIdx >= 0) {
- if (nameLow[nameIdx] == next || nameCap[nameIdx] == next) {
- nameIdx++;
- if (nameIdx == nameLow.length) {
- break;
- }
- } else {
- nameIdx = -1;
- }
- }
- }
- // We're after "Key:". Read the value as continuation lines.
- int last = ':';
- byte[] rawData;
- try (ByteArrayOutputStream out = new ByteArrayOutputStream(8192)) {
- for (;;) {
- int next = in.read();
- if (next < 0) {
- break;
- }
- if (last == '\n') {
- if (next == ' ' || next == '\t') {
- // Continuation line; skip this whitespace
- last = next;
- continue;
- }
- break; // Not a continuation line
- }
- out.write(next);
- last = next;
- }
- rawData = out.toByteArray();
- }
- // GPG trims off trailing whitespace, and a line having only whitespace
- // is a single LF.
- try (ByteArrayOutputStream out = new ByteArrayOutputStream(
- rawData.length)) {
- int lineStart = 0;
- boolean trimLeading = true;
- while (lineStart < rawData.length) {
- int nextLineStart = RawParseUtils.nextLF(rawData, lineStart);
- if (trimLeading) {
- while (lineStart < nextLineStart
- && isGpgSpace(rawData[lineStart])) {
- lineStart++;
- }
- }
- // Trim trailing
- int i = nextLineStart - 1;
- while (lineStart < i && isGpgSpace(rawData[i])) {
- i--;
- }
- if (i <= lineStart) {
- // Empty line signifies LF
- out.write('\n');
- trimLeading = true;
- } else {
- out.write(rawData, lineStart, i - lineStart + 1);
- trimLeading = false;
- }
- lineStart = nextLineStart;
- }
- return out.toByteArray();
- }
- }
-
- private static boolean isGpgSpace(int ch) {
- return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n';
- }
-
- private static boolean isTokenChar(int ch) {
- switch (ch) {
- case '-':
- case '.':
- case '/':
- case '_':
- case ':':
- case '*':
- case '+':
- case '=':
- return true;
+ return secret;
default:
- if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
- || (ch >= '0' && ch <= '9')) {
- return true;
- }
- return false;
+ // For other key types let Bouncy Castle do the check.
+ return data.getKeyData(publicKey, calculatorProvider, decryptor,
+ null, MAX_SEXPR_NESTING);
}
}
- private static boolean isHex(int ch) {
- return (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F')
- || (ch >= 'a' && ch <= 'f');
+ private static boolean isProtected(OpenedPGPKeyData data) {
+ return SExprParser.ProtectionFormatTypeTags.PROTECTED_PRIVATE_KEY == SExprParser
+ .getProtectionType(data.getKeyExpression().getString(0));
}
- private static boolean isOctal(int ch) {
- return (ch >= '0' && ch <= '7');
- }
-
- private static int nibble(int ch) {
- if (ch >= '0' && ch <= '9') {
- return ch - '0';
- } else if (ch >= 'A' && ch <= 'F') {
- return ch - 'A' + 10;
- } else if (ch >= 'a' && ch <= 'f') {
- return ch - 'a' + 10;
- }
- return -1;
- }
}
diff --git a/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF
index c1ec7ae..244b20a 100644
--- a/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.http.apache
Bundle-SymbolicName: org.eclipse.jgit.http.apache
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-RequiredExecutionEnvironment: JavaSE-17
Bundle-Localization: OSGI-INF/l10n/plugin
Bundle-Vendor: %Bundle-Vendor
@@ -26,11 +26,11 @@
org.apache.http.impl.conn;version="[4.4.0,5.0.0)",
org.apache.http.params;version="[4.3.0,5.0.0)",
org.apache.http.ssl;version="[4.3.0,5.0.0)",
- org.eclipse.jgit.annotations;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.nls;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport.http;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)"
-Export-Package: org.eclipse.jgit.transport.http.apache;version="7.0.0";
+ org.eclipse.jgit.annotations;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.nls;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport.http;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.0)"
+Export-Package: org.eclipse.jgit.transport.http.apache;version="7.3.0";
uses:="org.apache.http.client,
org.eclipse.jgit.transport.http,
org.apache.http.entity,
diff --git a/org.eclipse.jgit.http.apache/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.http.apache/META-INF/SOURCE-MANIFEST.MF
index d29d387..86adeb6 100644
--- a/org.eclipse.jgit.http.apache/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.http.apache/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@
Bundle-Name: org.eclipse.jgit.http.apache - Sources
Bundle-SymbolicName: org.eclipse.jgit.http.apache.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.0.0.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.http.apache;version="7.0.0.qualifier";roots="."
+Bundle-Version: 7.3.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.http.apache;version="7.3.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.http.apache/pom.xml b/org.eclipse.jgit.http.apache/pom.xml
index 51a79d6..0c7c5ff 100644
--- a/org.eclipse.jgit.http.apache/pom.xml
+++ b/org.eclipse.jgit.http.apache/pom.xml
@@ -15,7 +15,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.http.apache</artifactId>
diff --git a/org.eclipse.jgit.http.server/BUILD b/org.eclipse.jgit.http.server/BUILD
index f8aa44d..a0dae48 100644
--- a/org.eclipse.jgit.http.server/BUILD
+++ b/org.eclipse.jgit.http.server/BUILD
@@ -2,11 +2,16 @@
package(default_visibility = ["//visibility:public"])
+filegroup(
+ name = "jgit-servlet-resources",
+ srcs = glob(["resources/**"]),
+)
+
java_library(
name = "jgit-servlet",
srcs = glob(["src/**/*.java"]),
resource_strip_prefix = "org.eclipse.jgit.http.server/resources",
- resources = glob(["resources/**"]),
+ resources = [":jgit-servlet-resources"],
deps = [
"//lib:servlet-api",
# We want these deps to be provided_deps
diff --git a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
index 124129d..71c471d 100644
--- a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
@@ -3,14 +3,14 @@
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.http.server
Bundle-SymbolicName: org.eclipse.jgit.http.server
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-Localization: OSGI-INF/l10n/plugin
Bundle-Vendor: %Bundle-Vendor
-Export-Package: org.eclipse.jgit.http.server;version="7.0.0",
- org.eclipse.jgit.http.server.glue;version="7.0.0";
+Export-Package: org.eclipse.jgit.http.server;version="7.3.0",
+ org.eclipse.jgit.http.server.glue;version="7.3.0";
uses:="jakarta.servlet,
jakarta.servlet.http",
- org.eclipse.jgit.http.server.resolver;version="7.0.0";
+ org.eclipse.jgit.http.server.resolver;version="7.3.0";
uses:="jakarta.servlet.http
org.eclipse.jgit.transport.resolver,
org.eclipse.jgit.lib,
@@ -19,14 +19,14 @@
Bundle-RequiredExecutionEnvironment: JavaSE-17
Import-Package: jakarta.servlet;version="[6.0.0,7.0.0)",
jakarta.servlet.http;version="[6.0.0,7.0.0)",
- org.eclipse.jgit.annotations;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.file;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.transport.parser;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.nls;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.revwalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport.resolver;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)"
+ org.eclipse.jgit.annotations;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.transport.parser;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.nls;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.revwalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport.resolver;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.0)"
diff --git a/org.eclipse.jgit.http.server/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.http.server/META-INF/SOURCE-MANIFEST.MF
index bbb29cd..67a14cf 100644
--- a/org.eclipse.jgit.http.server/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.http.server/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@
Bundle-Name: org.eclipse.jgit.http.server - Sources
Bundle-SymbolicName: org.eclipse.jgit.http.server.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.0.0.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.http.server;version="7.0.0.qualifier";roots="."
+Bundle-Version: 7.3.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.http.server;version="7.3.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.http.server/pom.xml b/org.eclipse.jgit.http.server/pom.xml
index 7645dd1..b34ca2f 100644
--- a/org.eclipse.jgit.http.server/pom.xml
+++ b/org.eclipse.jgit.http.server/pom.xml
@@ -19,7 +19,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.http.server</artifactId>
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java
index d2f0cd2..bf3da4b 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java
@@ -255,9 +255,11 @@ private static void writePacket(PacketLineOut pckOut, String textForGit)
private static void send(HttpServletRequest req, HttpServletResponse res,
String type, byte[] buf, int httpStatus) throws IOException {
ServletUtils.consumeRequestBody(req);
- res.setStatus(httpStatus);
- res.setContentType(type);
- res.setContentLength(buf.length);
+ if (!res.isCommitted()) {
+ res.setStatus(httpStatus);
+ res.setContentType(type);
+ res.setContentLength(buf.length);
+ }
try (OutputStream os = res.getOutputStream()) {
os.write(buf);
}
diff --git a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
index edce2d8..518afb3 100644
--- a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.http.test
Bundle-SymbolicName: org.eclipse.jgit.http.test
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-Vendor: %Bundle-Vendor
Bundle-Localization: plugin
Bundle-RequiredExecutionEnvironment: JavaSE-17
@@ -29,26 +29,26 @@
org.eclipse.jetty.util.component;version="[12.0.0,13.0.0)",
org.eclipse.jetty.util.security;version="[12.0.0,13.0.0)",
org.eclipse.jetty.util.thread;version="[12.0.0,13.0.0)",
- org.eclipse.jgit.api;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.http.server;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.http.server.glue;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.http.server.resolver;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.file;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.reftable;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.junit;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.junit.http;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.nls;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.revwalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.storage.file;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport.http;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport.http.apache;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport.resolver;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)",
+ org.eclipse.jgit.api;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.http.server;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.http.server.glue;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.http.server.resolver;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.reftable;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.junit;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.junit.http;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.nls;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.revwalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport.http;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport.http.apache;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport.resolver;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.0)",
org.junit;version="[4.13,5.0.0)",
org.junit.rules;version="[4.13,5.0.0)",
org.junit.runner;version="[4.13,5.0.0)",
diff --git a/org.eclipse.jgit.http.test/pom.xml b/org.eclipse.jgit.http.test/pom.xml
index 01ee782..8ed3017 100644
--- a/org.eclipse.jgit.http.test/pom.xml
+++ b/org.eclipse.jgit.http.test/pom.xml
@@ -18,7 +18,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.http.test</artifactId>
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java
index 850e895..b0d17ad 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java
@@ -1728,7 +1728,8 @@ public void testPush_CreateBranch() throws Exception {
assertEquals(Q, remoteRepository.exactRef(dstName).getObjectId());
fsck(remoteRepository, Q);
- final ReflogReader log = remoteRepository.getReflogReader(dstName);
+ final ReflogReader log = remoteRepository.getRefDatabase()
+ .getReflogReader(dstName);
assertNotNull("has log for " + dstName, log);
final ReflogEntry last = log.getLastEntry();
diff --git a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF
index 3e98351..e3a6e26 100644
--- a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.junit.http
Bundle-SymbolicName: org.eclipse.jgit.junit.http
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-Localization: OSGI-INF/l10n/plugin
Bundle-Vendor: %Bundle-Vendor
Bundle-ActivationPolicy: lazy
@@ -22,17 +22,17 @@
org.eclipse.jetty.util.component;version="[12.0.0,13.0.0)",
org.eclipse.jetty.util.security;version="[12.0.0,13.0.0)",
org.eclipse.jetty.util.ssl;version="[12.0.0,13.0.0)",
- org.eclipse.jgit.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.http.server;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.file;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.junit;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.revwalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport.resolver;version="[7.0.0,7.1.0)",
+ org.eclipse.jgit.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.http.server;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.junit;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.revwalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport.resolver;version="[7.3.0,7.4.0)",
org.junit;version="[4.13,5.0.0)",
org.slf4j.helpers;version="[1.7.0,3.0.0)"
-Export-Package: org.eclipse.jgit.junit.http;version="7.0.0";
+Export-Package: org.eclipse.jgit.junit.http;version="7.3.0";
uses:="org.eclipse.jgit.transport,
jakarta.servlet,
jakarta.servlet.http,
diff --git a/org.eclipse.jgit.junit.http/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.junit.http/META-INF/SOURCE-MANIFEST.MF
index cfeb426..855f210 100644
--- a/org.eclipse.jgit.junit.http/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.junit.http/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@
Bundle-Name: org.eclipse.jgit.junit.http - Sources
Bundle-SymbolicName: org.eclipse.jgit.junit.http.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.0.0.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.junit.http;version="7.0.0.qualifier";roots="."
+Bundle-Version: 7.3.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.junit.http;version="7.3.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.junit.http/pom.xml b/org.eclipse.jgit.junit.http/pom.xml
index 67c8265..2947f21 100644
--- a/org.eclipse.jgit.junit.http/pom.xml
+++ b/org.eclipse.jgit.junit.http/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.junit.http</artifactId>
diff --git a/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.ssh/META-INF/MANIFEST.MF
index 424bac1..30c359b 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-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.junit.ssh
Bundle-SymbolicName: org.eclipse.jgit.junit.ssh
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-Localization: OSGI-INF/l10n/plugin
Bundle-Vendor: %Bundle-Vendor
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: JavaSE-17
-Import-Package: org.apache.sshd.common;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.config.keys;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.file.virtualfs;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.helpers;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.io;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.kex;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.keyprovider;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.session;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.signature;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.util.buffer;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.util.logging;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.util.security;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.util.threads;version="[2.12.0,2.13.0)",
- org.apache.sshd.core;version="[2.12.0,2.13.0)",
- org.apache.sshd.server;version="[2.12.0,2.13.0)",
- org.apache.sshd.server.auth;version="[2.12.0,2.13.0)",
- org.apache.sshd.server.auth.gss;version="[2.12.0,2.13.0)",
- org.apache.sshd.server.auth.keyboard;version="[2.12.0,2.13.0)",
- org.apache.sshd.server.auth.password;version="[2.12.0,2.13.0)",
- org.apache.sshd.server.command;version="[2.12.0,2.13.0)",
- org.apache.sshd.server.session;version="[2.12.0,2.13.0)",
- org.apache.sshd.server.shell;version="[2.12.0,2.13.0)",
- org.apache.sshd.server.subsystem;version="[2.12.0,2.13.0)",
- org.apache.sshd.sftp;version="[2.12.0,2.13.0)",
- org.apache.sshd.sftp.server;version="[2.12.0,2.13.0)",
- org.eclipse.jgit.annotations;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.api;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.api.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.junit;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.revwalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)",
+Import-Package: org.apache.sshd.common;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.config.keys;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.file.virtualfs;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.helpers;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.io;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.kex;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.keyprovider;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.session;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.signature;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.util.buffer;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.util.logging;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.util.security;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.util.threads;version="[2.15.0,2.16.0)",
+ org.apache.sshd.core;version="[2.15.0,2.16.0)",
+ org.apache.sshd.server;version="[2.15.0,2.16.0)",
+ org.apache.sshd.server.auth;version="[2.15.0,2.16.0)",
+ org.apache.sshd.server.auth.gss;version="[2.15.0,2.16.0)",
+ org.apache.sshd.server.auth.keyboard;version="[2.15.0,2.16.0)",
+ org.apache.sshd.server.auth.password;version="[2.15.0,2.16.0)",
+ org.apache.sshd.server.command;version="[2.15.0,2.16.0)",
+ org.apache.sshd.server.session;version="[2.15.0,2.16.0)",
+ org.apache.sshd.server.shell;version="[2.15.0,2.16.0)",
+ org.apache.sshd.server.subsystem;version="[2.15.0,2.16.0)",
+ org.apache.sshd.sftp;version="[2.15.0,2.16.0)",
+ org.apache.sshd.sftp.server;version="[2.15.0,2.16.0)",
+ org.eclipse.jgit.annotations;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.api;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.api.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.junit;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.revwalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.0)",
org.junit;version="[4.13,5.0.0)",
org.junit.experimental.theories;version="[4.13,5.0.0)",
org.slf4j;version="[1.7.0,3.0.0)"
-Export-Package: org.eclipse.jgit.junit.ssh;version="7.0.0"
+Export-Package: org.eclipse.jgit.junit.ssh;version="7.3.0"
diff --git a/org.eclipse.jgit.junit.ssh/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.junit.ssh/META-INF/SOURCE-MANIFEST.MF
index a7ca633..27d1737 100644
--- a/org.eclipse.jgit.junit.ssh/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.junit.ssh/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@
Bundle-Name: org.eclipse.jgit.junit.ssh - Sources
Bundle-SymbolicName: org.eclipse.jgit.junit.ssh.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.0.0.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.junit.ssh;version="7.0.0.qualifier";roots="."
+Bundle-Version: 7.3.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.junit.ssh;version="7.3.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.junit.ssh/pom.xml b/org.eclipse.jgit.junit.ssh/pom.xml
index 2a885f2..cbdfa31 100644
--- a/org.eclipse.jgit.junit.ssh/pom.xml
+++ b/org.eclipse.jgit.junit.ssh/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.junit.ssh</artifactId>
diff --git a/org.eclipse.jgit.junit/.settings/.api_filters b/org.eclipse.jgit.junit/.settings/.api_filters
new file mode 100644
index 0000000..2781530
--- /dev/null
+++ b/org.eclipse.jgit.junit/.settings/.api_filters
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<component id="org.eclipse.jgit.junit" version="2">
+ <resource path="src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java" type="org.eclipse.jgit.junit.LocalDiskRepositoryTestCase">
+ <filter id="336658481">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.junit.LocalDiskRepositoryTestCase"/>
+ <message_argument value="testRoot"/>
+ </message_arguments>
+ </filter>
+ </resource>
+</component>
diff --git a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
index af1cbf2..5f0546e 100644
--- a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
@@ -3,36 +3,36 @@
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.junit
Bundle-SymbolicName: org.eclipse.jgit.junit
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-Localization: OSGI-INF/l10n/plugin
Bundle-Vendor: %Bundle-Vendor
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: JavaSE-17
-Import-Package: org.eclipse.jgit.annotations;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.api;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.api.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.dircache;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.file;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.pack;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.util;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.merge;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.revwalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.storage.file;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport;version="7.0.0",
- org.eclipse.jgit.treewalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.treewalk.filter;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util.io;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util.time;version="[7.0.0,7.1.0)",
+Import-Package: org.eclipse.jgit.annotations;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.api;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.api.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.dircache;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.util;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.merge;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.revwalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport;version="7.3.0",
+ org.eclipse.jgit.treewalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.treewalk.filter;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util.io;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util.time;version="[7.3.0,7.4.0)",
org.junit;version="[4.13,5.0.0)",
org.junit.rules;version="[4.13,5.0.0)",
org.junit.runner;version="[4.13,5.0.0)",
org.junit.runners;version="[4.13,5.0.0)",
org.junit.runners.model;version="[4.13,5.0.0)",
org.slf4j;version="[1.7.0,3.0.0)"
-Export-Package: org.eclipse.jgit.junit;version="7.0.0";
+Export-Package: org.eclipse.jgit.junit;version="7.3.0";
uses:="org.eclipse.jgit.dircache,
org.eclipse.jgit.lib,
org.eclipse.jgit.revwalk,
@@ -45,4 +45,4 @@
org.junit.runners.model,
org.junit.runner,
org.eclipse.jgit.util.time",
- org.eclipse.jgit.junit.time;version="7.0.0";uses:="org.eclipse.jgit.util.time"
+ org.eclipse.jgit.junit.time;version="7.3.0";uses:="org.eclipse.jgit.util.time"
diff --git a/org.eclipse.jgit.junit/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.junit/META-INF/SOURCE-MANIFEST.MF
index 149ad00..4e0108a 100644
--- a/org.eclipse.jgit.junit/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.junit/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@
Bundle-Name: org.eclipse.jgit.junit - Sources
Bundle-SymbolicName: org.eclipse.jgit.junit.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.0.0.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.junit;version="7.0.0.qualifier";roots="."
+Bundle-Version: 7.3.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.junit;version="7.3.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.junit/pom.xml b/org.eclipse.jgit.junit/pom.xml
index c33cb29..dbb8b06 100644
--- a/org.eclipse.jgit.junit/pom.xml
+++ b/org.eclipse.jgit.junit/pom.xml
@@ -19,7 +19,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.junit</artifactId>
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/FakeIndexFactory.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/FakeIndexFactory.java
new file mode 100644
index 0000000..eb23bec
--- /dev/null
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/FakeIndexFactory.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2025, Google Inc.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.junit;
+
+import static java.util.function.Function.identity;
+import static java.util.stream.Collectors.toMap;
+import static java.util.stream.Collectors.toUnmodifiableList;
+
+import java.io.IOException;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.internal.storage.file.PackIndex;
+import org.eclipse.jgit.internal.storage.file.PackIndex.EntriesIterator;
+import org.eclipse.jgit.internal.storage.file.PackReverseIndex;
+import org.eclipse.jgit.lib.AbbreviatedObjectId;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+
+/**
+ * Create indexes with predefined data
+ *
+ * @since 7.2
+ */
+public class FakeIndexFactory {
+
+ /**
+ * An object for the fake index
+ *
+ * @param name
+ * a sha1
+ * @param offset
+ * the (fake) position of the object in the pack
+ */
+ public record IndexObject(String name, long offset) {
+ /**
+ * Name (sha1) as an objectId
+ *
+ * @return name (a sha1) as an objectId.
+ */
+ public ObjectId getObjectId() {
+ return ObjectId.fromString(name);
+ }
+ }
+
+ /**
+ * Return an index populated with these objects
+ *
+ * @param objs
+ * objects to be indexed
+ * @return a PackIndex implementation
+ */
+ public static PackIndex indexOf(List<IndexObject> objs) {
+ return new FakePackIndex(objs);
+ }
+
+ /**
+ * Return a reverse pack index with these objects
+ *
+ * @param objs
+ * objects to be indexed
+ * @return a PackReverseIndex implementation
+ */
+ public static PackReverseIndex reverseIndexOf(List<IndexObject> objs) {
+ return new FakeReverseIndex(objs);
+ }
+
+ private FakeIndexFactory() {
+ }
+
+ private static class FakePackIndex implements PackIndex {
+ private static final Comparator<IndexObject> SHA1_COMPARATOR = (o1,
+ o2) -> String.CASE_INSENSITIVE_ORDER.compare(o1.name(),
+ o2.name());
+
+ private final Map<String, IndexObject> idx;
+
+ private final List<IndexObject> sha1Ordered;
+
+ private final long offset64count;
+
+ FakePackIndex(List<IndexObject> objs) {
+ sha1Ordered = objs.stream().sorted(SHA1_COMPARATOR)
+ .collect(toUnmodifiableList());
+ idx = objs.stream().collect(toMap(IndexObject::name, identity()));
+ offset64count = objs.stream()
+ .filter(o -> o.offset > Integer.MAX_VALUE).count();
+ }
+
+ @Override
+ public Iterator<MutableEntry> iterator() {
+ return new FakeEntriesIterator(sha1Ordered);
+ }
+
+ @Override
+ public long getObjectCount() {
+ return sha1Ordered.size();
+ }
+
+ @Override
+ public long getOffset64Count() {
+ return offset64count;
+ }
+
+ @Override
+ public ObjectId getObjectId(long nthPosition) {
+ return ObjectId
+ .fromString(sha1Ordered.get((int) nthPosition).name());
+ }
+
+ @Override
+ public long getOffset(long nthPosition) {
+ return sha1Ordered.get((int) nthPosition).offset();
+ }
+
+ @Override
+ public long findOffset(AnyObjectId objId) {
+ IndexObject o = idx.get(objId.name());
+ if (o == null) {
+ return -1;
+ }
+ return o.offset();
+ }
+
+ @Override
+ public int findPosition(AnyObjectId objId) {
+ IndexObject o = idx.get(objId.name());
+ if (o == null) {
+ return -1;
+ }
+ return sha1Ordered.indexOf(o);
+ }
+
+ @Override
+ public long findCRC32(AnyObjectId objId) throws MissingObjectException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean hasCRC32Support() {
+ return false;
+ }
+
+ @Override
+ public void resolve(Set<ObjectId> matches, AbbreviatedObjectId id,
+ int matchLimit) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public byte[] getChecksum() {
+ return new byte[0];
+ }
+ }
+
+ private static class FakeReverseIndex implements PackReverseIndex {
+ private static final Comparator<IndexObject> OFFSET_COMPARATOR = Comparator
+ .comparingLong(IndexObject::offset);
+
+ private final List<IndexObject> byOffset;
+
+ private final Map<Long, IndexObject> ridx;
+
+ FakeReverseIndex(List<IndexObject> objs) {
+ byOffset = objs.stream().sorted(OFFSET_COMPARATOR)
+ .collect(toUnmodifiableList());
+ ridx = byOffset.stream()
+ .collect(toMap(IndexObject::offset, identity()));
+ }
+
+ @Override
+ public void verifyPackChecksum(String packFilePath) {
+ // Do nothing
+ }
+
+ @Override
+ public ObjectId findObject(long offset) {
+ IndexObject indexObject = ridx.get(offset);
+ if (indexObject == null) {
+ return null;
+ }
+ return ObjectId.fromString(indexObject.name());
+ }
+
+ @Override
+ public long findNextOffset(long offset, long maxOffset)
+ throws CorruptObjectException {
+ IndexObject o = ridx.get(offset);
+ if (o == null) {
+ throw new CorruptObjectException("Invalid offset"); //$NON-NLS-1$
+ }
+ int pos = byOffset.indexOf(o);
+ if (pos == byOffset.size() - 1) {
+ return maxOffset;
+ }
+ return byOffset.get(pos + 1).offset();
+ }
+
+ @Override
+ public int findPosition(long offset) {
+ IndexObject indexObject = ridx.get(offset);
+ return byOffset.indexOf(indexObject);
+ }
+
+ @Override
+ public ObjectId findObjectByPosition(int nthPosition) {
+ return byOffset.get(nthPosition).getObjectId();
+ }
+ }
+
+ private static class FakeEntriesIterator extends EntriesIterator {
+
+ private static final byte[] buffer = new byte[Constants.OBJECT_ID_LENGTH];
+
+ private final Iterator<IndexObject> it;
+
+ FakeEntriesIterator(List<IndexObject> objs) {
+ super(objs.size());
+ it = objs.iterator();
+ }
+
+ @Override
+ protected void readNext() {
+ IndexObject next = it.next();
+ next.getObjectId().copyRawTo(buffer, 0);
+ setIdBuffer(buffer, 0);
+ setOffset(next.offset());
+ }
+ }
+}
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java
index 407290a..0d20f64 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java
@@ -20,19 +20,19 @@
import java.io.IOException;
import java.io.PrintStream;
import java.time.Instant;
+import java.time.ZoneId;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Random;
import java.util.Set;
import java.util.TreeSet;
-import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.internal.storage.file.FileRepository;
-import org.eclipse.jgit.internal.util.ShutdownHook;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
@@ -47,6 +47,7 @@
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
+import org.junit.rules.TemporaryFolder;
import org.junit.rules.TestName;
/**
@@ -84,6 +85,16 @@ public abstract class LocalDiskRepositoryTestCase {
protected MockSystemReader mockSystemReader;
private final Set<Repository> toClose = new HashSet<>();
+
+ /**
+ * Temporary test root directory for files created by tests.
+ * @since 7.2
+ */
+ @Rule
+ public TemporaryFolder testRoot = new TemporaryFolder();
+
+ Random rand = new Random();
+
private File tmp;
private File homeDir;
@@ -114,11 +125,8 @@ private String getTestName() {
*/
@Before
public void setUp() throws Exception {
- tmp = File.createTempFile("jgit_" + getTestName() + '_', "_tmp");
- Cleanup.deleteOnShutdown(tmp);
- if (!tmp.delete() || !tmp.mkdir()) {
- throw new IOException("Cannot create " + tmp);
- }
+ tmp = testRoot.newFolder(getTestName() + rand.nextInt());
+
mockSystemReader = new MockSystemReader();
SystemReader.setInstance(mockSystemReader);
@@ -219,12 +227,6 @@ public void tearDown() throws Exception {
System.gc();
}
FS.DETECTED.setUserHome(homeDir);
- if (tmp != null) {
- recursiveDelete(tmp, false, true);
- }
- if (tmp != null && !tmp.exists()) {
- Cleanup.removed(tmp);
- }
SystemReader.setInstance(null);
}
@@ -233,8 +235,8 @@ public void tearDown() throws Exception {
*/
protected void tick() {
mockSystemReader.tick(5 * 60);
- final long now = mockSystemReader.getCurrentTime();
- final int tz = mockSystemReader.getTimezone(now);
+ Instant now = mockSystemReader.now();
+ ZoneId tz = mockSystemReader.getTimeZoneId();
author = new PersonIdent(author, now, tz);
committer = new PersonIdent(committer, now, tz);
@@ -623,41 +625,4 @@ protected String read(File f) throws IOException {
private static HashMap<String, String> cloneEnv() {
return new HashMap<>(System.getenv());
}
-
- private static final class Cleanup {
- private static final Cleanup INSTANCE = new Cleanup();
-
- static {
- ShutdownHook.INSTANCE.register(() -> INSTANCE.onShutdown());
- }
-
- private final Set<File> toDelete = ConcurrentHashMap.newKeySet();
-
- private Cleanup() {
- // empty
- }
-
- static void deleteOnShutdown(File tmp) {
- INSTANCE.toDelete.add(tmp);
- }
-
- static void removed(File tmp) {
- INSTANCE.toDelete.remove(tmp);
- }
-
- private void onShutdown() {
- // On windows accidentally open files or memory
- // mapped regions may prevent files from being deleted.
- // Suggesting a GC increases the likelihood that our
- // test repositories actually get removed after the
- // tests, even in the case of failure.
- System.gc();
- synchronized (this) {
- boolean silent = false;
- boolean failOnError = false;
- for (File tmp : toDelete)
- recursiveDelete(tmp, silent, failOnError);
- }
- }
- }
}
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java
index 419fdb1..38f0d0b 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java
@@ -18,6 +18,8 @@
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.Duration;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
@@ -242,6 +244,11 @@ public TimeZone getTimeZone() {
}
@Override
+ public ZoneId getTimeZoneId() {
+ return ZoneOffset.ofHoursMinutes(-3, -30);
+ }
+
+ @Override
public Locale getLocale() {
return Locale.US;
}
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/SeparateClassloaderTestRunner.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/SeparateClassloaderTestRunner.java
index c8c56b2..2a482df 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/SeparateClassloaderTestRunner.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/SeparateClassloaderTestRunner.java
@@ -44,7 +44,7 @@ private static Class<?> loadNewClass(Class<?> klass)
try {
String pathSeparator = System.getProperty("path.separator");
String[] classPathEntries = System.getProperty("java.class.path")
- .split(pathSeparator);
+ .split(pathSeparator, -1);
URL[] urls = new URL[classPathEntries.length];
for (int i = 0; i < classPathEntries.length; i++) {
urls[i] = Paths.get(classPathEntries[i]).toUri().toURL();
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
index a2e0a57..2d00a85 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
@@ -21,6 +21,7 @@
import java.io.OutputStream;
import java.security.MessageDigest;
import java.time.Instant;
+import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -30,6 +31,7 @@
import java.util.Set;
import java.util.TimeZone;
+import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheBuilder;
@@ -157,8 +159,8 @@ public TestRepository(R db, RevWalk rw, MockSystemReader reader)
this.pool = rw;
this.inserter = db.newObjectInserter();
this.mockSystemReader = reader;
- long now = mockSystemReader.getCurrentTime();
- int tz = mockSystemReader.getTimezone(now);
+ Instant now = mockSystemReader.now();
+ ZoneId tz = mockSystemReader.getTimeZoneAt(now);
defaultAuthor = new PersonIdent(AUTHOR, AUTHOR_EMAIL, now, tz);
defaultCommitter = new PersonIdent(COMMITTER, COMMITTER_EMAIL, now, tz);
}
@@ -197,7 +199,9 @@ public Git git() {
*
* @return current date.
* @since 4.2
+ * @deprecated Use {@link #getInstant()} instead.
*/
+ @Deprecated(since = "7.2")
public Date getDate() {
return new Date(mockSystemReader.getCurrentTime());
}
@@ -209,18 +213,31 @@ public Date getDate() {
* @since 6.8
*/
public Instant getInstant() {
- return Instant.ofEpochMilli(mockSystemReader.getCurrentTime());
+ return mockSystemReader.now();
}
/**
* Get timezone
*
* @return timezone used for default identities.
+ * @deprecated Use {@link #getTimeZoneId()} instead.
*/
+ @Deprecated(since = "7.2")
public TimeZone getTimeZone() {
return mockSystemReader.getTimeZone();
}
+
+ /**
+ * Get timezone
+ *
+ * @return timezone used for default identities.
+ * @since 7.2
+ */
+ public ZoneId getTimeZoneId() {
+ return mockSystemReader.getTimeZoneId();
+ }
+
/**
* Adjust the current time that will used by the next commit.
*
@@ -232,14 +249,14 @@ public void tick(int secDelta) {
}
/**
- * Set the author and committer using {@link #getDate()}.
+ * Set the author and committer using {@link #getInstant()}.
*
* @param c
* the commit builder to store.
*/
public void setAuthorAndCommitter(org.eclipse.jgit.lib.CommitBuilder c) {
- c.setAuthor(new PersonIdent(defaultAuthor, getDate()));
- c.setCommitter(new PersonIdent(defaultCommitter, getDate()));
+ c.setAuthor(new PersonIdent(defaultAuthor, getInstant()));
+ c.setCommitter(new PersonIdent(defaultCommitter, getInstant()));
}
/**
@@ -487,8 +504,8 @@ public ObjectId unparsedCommit(final int secDelta, final RevTree tree,
c = new org.eclipse.jgit.lib.CommitBuilder();
c.setTreeId(tree);
c.setParentIds(parents);
- c.setAuthor(new PersonIdent(defaultAuthor, getDate()));
- c.setCommitter(new PersonIdent(defaultCommitter, getDate()));
+ c.setAuthor(new PersonIdent(defaultAuthor, getInstant()));
+ c.setCommitter(new PersonIdent(defaultCommitter, getInstant()));
c.setMessage("");
ObjectId id;
try (ObjectInserter ins = inserter) {
@@ -528,7 +545,7 @@ public RevTag tag(String name, RevObject dst) throws Exception {
final TagBuilder t = new TagBuilder();
t.setObjectId(dst);
t.setTag(name);
- t.setTagger(new PersonIdent(defaultCommitter, getDate()));
+ t.setTagger(new PersonIdent(defaultCommitter, getInstant()));
t.setMessage("");
ObjectId id;
try (ObjectInserter ins = inserter) {
@@ -797,7 +814,7 @@ public RevCommit cherryPick(AnyObjectId id) throws Exception {
b.setParentId(head);
b.setTreeId(merger.getResultTreeId());
b.setAuthor(commit.getAuthorIdent());
- b.setCommitter(new PersonIdent(defaultCommitter, getDate()));
+ b.setCommitter(new PersonIdent(defaultCommitter, getInstant()));
b.setMessage(commit.getFullMessage());
ObjectId result;
try (ObjectInserter ins = inserter) {
@@ -1019,7 +1036,8 @@ public void close() {
private static void prunePacked(ObjectDirectory odb) throws IOException {
for (Pack p : odb.getPacks()) {
for (MutableEntry e : p)
- FileUtils.delete(odb.fileFor(e.toObjectId()));
+ FileUtils.delete(odb.fileFor(e.toObjectId()),
+ FileUtils.SKIP_MISSING);
}
}
@@ -1144,15 +1162,18 @@ public class CommitBuilder {
}
/**
- * set parent commit
+ * Set parent commit
*
* @param p
- * parent commit
+ * parent commit, can be {@code null}
* @return this commit builder
* @throws Exception
* if an error occurred
*/
- public CommitBuilder parent(RevCommit p) throws Exception {
+ public CommitBuilder parent(@Nullable RevCommit p) throws Exception {
+ if (p == null) {
+ return this;
+ }
if (parents.isEmpty()) {
DirCacheBuilder b = tree.builder();
parseBody(p);
@@ -1403,7 +1424,7 @@ public RevCommit create() throws Exception {
c.setAuthor(author);
if (committer != null) {
if (updateCommitterTime)
- committer = new PersonIdent(committer, getDate());
+ committer = new PersonIdent(committer, getInstant());
c.setCommitter(committer);
}
diff --git a/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF
index 8e41ee3..8feb8ef 100644
--- a/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.lfs.server.test
Bundle-SymbolicName: org.eclipse.jgit.lfs.server.test
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-Vendor: %Bundle-Vendor
Bundle-Localization: plugin
Bundle-RequiredExecutionEnvironment: JavaSE-17
@@ -26,24 +26,24 @@
org.eclipse.jetty.util.component;version="[12.0.0,13.0.0)",
org.eclipse.jetty.util.security;version="[12.0.0,13.0.0)",
org.eclipse.jetty.util.thread;version="[12.0.0,13.0.0)",
- org.eclipse.jgit.api;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.api.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.file;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.junit;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.junit.http;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lfs;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lfs.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lfs.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lfs.server;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lfs.server.fs;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lfs.test;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.revwalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.storage.file;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.treewalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.treewalk.filter;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)",
+ org.eclipse.jgit.api;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.api.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.junit;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.junit.http;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lfs;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lfs.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lfs.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lfs.server;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lfs.server.fs;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lfs.test;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.revwalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.treewalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.treewalk.filter;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.0)",
org.hamcrest.core;version="[1.1.0,3.0.0)",
org.junit;version="[4.13,5.0.0)",
org.junit.rules;version="[4.13,5.0.0)",
diff --git a/org.eclipse.jgit.lfs.server.test/pom.xml b/org.eclipse.jgit.lfs.server.test/pom.xml
index 49c1023..176f4af 100644
--- a/org.eclipse.jgit.lfs.server.test/pom.xml
+++ b/org.eclipse.jgit.lfs.server.test/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.lfs.server.test</artifactId>
diff --git a/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF
index a59c82e..ed8cfff 100644
--- a/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF
@@ -3,19 +3,19 @@
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.lfs.server
Bundle-SymbolicName: org.eclipse.jgit.lfs.server
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-Localization: OSGI-INF/l10n/plugin
Bundle-Vendor: %Bundle-Vendor
-Export-Package: org.eclipse.jgit.lfs.server;version="7.0.0";
+Export-Package: org.eclipse.jgit.lfs.server;version="7.3.0";
uses:="jakarta.servlet.http,
org.eclipse.jgit.lfs.lib",
- org.eclipse.jgit.lfs.server.fs;version="7.0.0";
+ org.eclipse.jgit.lfs.server.fs;version="7.3.0";
uses:="jakarta.servlet,
jakarta.servlet.http,
org.eclipse.jgit.lfs.server,
org.eclipse.jgit.lfs.lib",
- org.eclipse.jgit.lfs.server.internal;version="7.0.0";x-internal:=true,
- org.eclipse.jgit.lfs.server.s3;version="7.0.0";
+ org.eclipse.jgit.lfs.server.internal;version="7.3.0";x-internal:=true,
+ org.eclipse.jgit.lfs.server.s3;version="7.3.0";
uses:="org.eclipse.jgit.lfs.server,
org.eclipse.jgit.lfs.lib"
Bundle-RequiredExecutionEnvironment: JavaSE-17
@@ -24,15 +24,15 @@
jakarta.servlet.annotation;version="[6.0.0,7.0.0)",
jakarta.servlet.http;version="[6.0.0,7.0.0)",
org.apache.http;version="[4.3.0,5.0.0)",
- org.eclipse.jgit.annotations;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.file;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lfs.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lfs.internal;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lfs.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.nls;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport.http;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport.http.apache;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)",
+ org.eclipse.jgit.annotations;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lfs.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lfs.internal;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lfs.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.nls;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport.http;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport.http.apache;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.0)",
org.slf4j;version="[1.7.0,3.0.0)"
diff --git a/org.eclipse.jgit.lfs.server/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.lfs.server/META-INF/SOURCE-MANIFEST.MF
index 2cf86bc..7e26500 100644
--- a/org.eclipse.jgit.lfs.server/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.lfs.server/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@
Bundle-Name: org.eclipse.jgit.lfs.server - Sources
Bundle-SymbolicName: org.eclipse.jgit.lfs.server.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.0.0.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.lfs.server;version="7.0.0.qualifier";roots="."
+Bundle-Version: 7.3.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.lfs.server;version="7.3.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.lfs.server/pom.xml b/org.eclipse.jgit.lfs.server/pom.xml
index befaa36..ebb815f 100644
--- a/org.eclipse.jgit.lfs.server/pom.xml
+++ b/org.eclipse.jgit.lfs.server/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.lfs.server</artifactId>
diff --git a/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF
index 96c8953..b8a2ca7 100644
--- a/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF
@@ -3,27 +3,28 @@
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.lfs.test
Bundle-SymbolicName: org.eclipse.jgit.lfs.test
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-Vendor: %Bundle-Vendor
Bundle-Localization: plugin
Bundle-RequiredExecutionEnvironment: JavaSE-17
-Import-Package: org.eclipse.jgit.api;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.attributes;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.junit;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lfs;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lfs.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lfs.internal;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lfs.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.revwalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport.http;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.treewalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.treewalk.filter;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)",
+Import-Package: org.eclipse.jgit.api;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.attributes;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.junit;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lfs;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lfs.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lfs.internal;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lfs.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.revwalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport.http;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.treewalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.treewalk.filter;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.0)",
org.hamcrest.core;version="[1.1.0,3.0.0)",
org.junit;version="[4.13,5.0.0)",
org.junit.runner;version="[4.13,5.0.0)",
org.junit.runners;version="[4.13,5.0.0)"
-Export-Package: org.eclipse.jgit.lfs.test;version="7.0.0";x-friends:="org.eclipse.jgit.lfs.server.test"
+Export-Package: org.eclipse.jgit.lfs.test;version="7.3.0";x-friends:="org.eclipse.jgit.lfs.server.test"
diff --git a/org.eclipse.jgit.lfs.test/pom.xml b/org.eclipse.jgit.lfs.test/pom.xml
index 2c109c9..c9591b6 100644
--- a/org.eclipse.jgit.lfs.test/pom.xml
+++ b/org.eclipse.jgit.lfs.test/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.lfs.test</artifactId>
diff --git a/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/internal/LfsConnectionFactoryTest.java b/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/internal/LfsConnectionFactoryTest.java
index badcb7d..ee8e893 100644
--- a/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/internal/LfsConnectionFactoryTest.java
+++ b/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/internal/LfsConnectionFactoryTest.java
@@ -97,7 +97,8 @@ public void lfsUrlFromRemoteUrlWithoutDotGit() throws Exception {
public void lfsUrlFromLocalConfig() throws Exception {
addRemoteUrl("https://localhost/repo");
- StoredConfig cfg = ((Repository) db).getConfig();
+ @SuppressWarnings("restriction")
+ StoredConfig cfg = db.getConfig();
cfg.setString(ConfigConstants.CONFIG_SECTION_LFS,
null,
ConfigConstants.CONFIG_KEY_URL,
@@ -111,7 +112,8 @@ public void lfsUrlFromLocalConfig() throws Exception {
public void lfsUrlFromOriginConfig() throws Exception {
addRemoteUrl("https://localhost/repo");
- StoredConfig cfg = ((Repository) db).getConfig();
+ @SuppressWarnings("restriction")
+ StoredConfig cfg = db.getConfig();
cfg.setString(ConfigConstants.CONFIG_SECTION_LFS,
org.eclipse.jgit.lib.Constants.DEFAULT_REMOTE_NAME,
ConfigConstants.CONFIG_KEY_URL,
diff --git a/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF
index f4c1e56..5338f8b 100644
--- a/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF
@@ -3,32 +3,32 @@
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.lfs
Bundle-SymbolicName: org.eclipse.jgit.lfs
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-Localization: OSGI-INF/l10n/plugin
Bundle-Vendor: %Bundle-Vendor
-Export-Package: org.eclipse.jgit.lfs;version="7.0.0",
- org.eclipse.jgit.lfs.errors;version="7.0.0",
- org.eclipse.jgit.lfs.internal;version="7.0.0";x-friends:="org.eclipse.jgit.lfs.test,org.eclipse.jgit.lfs.server.fs,org.eclipse.jgit.lfs.server",
- org.eclipse.jgit.lfs.lib;version="7.0.0"
+Export-Package: org.eclipse.jgit.lfs;version="7.3.0",
+ org.eclipse.jgit.lfs.errors;version="7.3.0",
+ org.eclipse.jgit.lfs.internal;version="7.3.0";x-friends:="org.eclipse.jgit.lfs.test,org.eclipse.jgit.lfs.server.fs,org.eclipse.jgit.lfs.server",
+ org.eclipse.jgit.lfs.lib;version="7.3.0"
Bundle-RequiredExecutionEnvironment: JavaSE-17
Import-Package: com.google.gson;version="[2.8.2,3.0.0)",
com.google.gson.stream;version="[2.8.2,3.0.0)",
- org.eclipse.jgit.annotations;version="[7.0.0,7.1.0)";resolution:=optional,
- org.eclipse.jgit.api.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.attributes;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.diff;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.dircache;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.hooks;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.file;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.nls;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.revwalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.storage.file;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.storage.pack;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport.http;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.treewalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.treewalk.filter;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util.io;version="[7.0.0,7.1.0)"
+ org.eclipse.jgit.annotations;version="[7.3.0,7.4.0)";resolution:=optional,
+ org.eclipse.jgit.api.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.attributes;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.diff;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.dircache;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.hooks;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.nls;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.revwalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.storage.pack;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport.http;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.treewalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.treewalk.filter;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util.io;version="[7.3.0,7.4.0)"
diff --git a/org.eclipse.jgit.lfs/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.lfs/META-INF/SOURCE-MANIFEST.MF
index a048aa1..fc12482 100644
--- a/org.eclipse.jgit.lfs/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.lfs/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@
Bundle-Name: org.eclipse.jgit.lfs - Sources
Bundle-SymbolicName: org.eclipse.jgit.lfs.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.0.0.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.lfs;version="7.0.0.qualifier";roots="."
+Bundle-Version: 7.3.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.lfs;version="7.3.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.lfs/pom.xml b/org.eclipse.jgit.lfs/pom.xml
index 62508e2..d336f16 100644
--- a/org.eclipse.jgit.lfs/pom.xml
+++ b/org.eclipse.jgit.lfs/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.lfs</artifactId>
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/AnyLongObjectId.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/AnyLongObjectId.java
index 75d500e..a13a60c 100644
--- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/AnyLongObjectId.java
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/AnyLongObjectId.java
@@ -41,24 +41,6 @@ public abstract class AnyLongObjectId implements Comparable<AnyLongObjectId> {
* @param secondObjectId
* the second identifier to compare. Must not be null.
* @return true if the two identifiers are the same.
- * @deprecated use {@link #isEqual(AnyLongObjectId, AnyLongObjectId)}
- * instead.
- */
- @Deprecated
- @SuppressWarnings("AmbiguousMethodReference")
- public static boolean equals(final AnyLongObjectId firstObjectId,
- final AnyLongObjectId secondObjectId) {
- return isEqual(firstObjectId, secondObjectId);
- }
-
- /**
- * Compare two object identifier byte sequences for equality.
- *
- * @param firstObjectId
- * the first identifier to compare. Must not be null.
- * @param secondObjectId
- * the second identifier to compare. Must not be null.
- * @return true if the two identifiers are the same.
* @since 5.4
*/
public static boolean isEqual(final AnyLongObjectId firstObjectId,
@@ -263,7 +245,7 @@ public final int hashCode() {
*/
@SuppressWarnings({ "NonOverridingEquals", "AmbiguousMethodReference" })
public final boolean equals(AnyLongObjectId other) {
- return other != null ? equals(this, other) : false;
+ return other != null ? isEqual(this, other) : false;
}
@Override
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml
index 3dd7b10..adea43b 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml
@@ -2,7 +2,7 @@
<feature
id="org.eclipse.jgit"
label="%featureName"
- version="7.0.0.qualifier"
+ version="7.3.0.qualifier"
provider-name="%providerName">
<description url="http://www.eclipse.org/jgit/">
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml
index ea66a23..734b634 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.gpg.bc.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.gpg.bc.feature/feature.xml
index 23fdd23..f86652f 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.gpg.bc.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.gpg.bc.feature/feature.xml
@@ -2,7 +2,7 @@
<feature
id="org.eclipse.jgit.gpg.bc"
label="%featureName"
- version="7.0.0.qualifier"
+ version="7.3.0.qualifier"
provider-name="%providerName">
<description url="http://www.eclipse.org/jgit/">
@@ -23,7 +23,7 @@
</url>
<requires>
- <import plugin="org.eclipse.jgit" version="7.0.0" match="equivalent"/>
+ <import plugin="org.eclipse.jgit" version="7.3.0" match="equivalent"/>
</requires>
<plugin
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.gpg.bc.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.gpg.bc.feature/pom.xml
index fc1bf49..a756a8d 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.gpg.bc.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.gpg.bc.feature/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml
index 92262a0..d999c15 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml
@@ -2,7 +2,7 @@
<feature
id="org.eclipse.jgit.http.apache"
label="%featureName"
- version="7.0.0.qualifier"
+ version="7.3.0.qualifier"
provider-name="%providerName">
<description url="http://www.eclipse.org/jgit/">
@@ -23,7 +23,7 @@
</url>
<requires>
- <import plugin="org.eclipse.jgit" version="7.0.0" match="equivalent"/>
+ <import plugin="org.eclipse.jgit" version="7.3.0" match="equivalent"/>
</requires>
<plugin
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml
index 3344dcb..c2ca95a 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml
index 6f03419..922622c 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml
@@ -2,7 +2,7 @@
<feature
id="org.eclipse.jgit.junit"
label="%featureName"
- version="7.0.0.qualifier"
+ version="7.3.0.qualifier"
provider-name="%providerName">
<description url="http://www.eclipse.org/jgit/">
@@ -24,7 +24,7 @@
<requires>
<import plugin="com.jcraft.jsch"/>
- <import plugin="org.eclipse.jgit" version="7.0.0" match="equivalent"/>
+ <import plugin="org.eclipse.jgit" version="7.3.0" match="equivalent"/>
</requires>
<plugin
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml
index 45162c9..71e7373 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml
index 8eacae1..53cb4bac 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml
@@ -2,7 +2,7 @@
<feature
id="org.eclipse.jgit.lfs"
label="%featureName"
- version="7.0.0.qualifier"
+ version="7.3.0.qualifier"
provider-name="%providerName">
<description url="http://www.eclipse.org/jgit/">
@@ -23,7 +23,7 @@
</url>
<requires>
- <import feature="org.eclipse.jgit" version="7.0.0" match="equivalent"/>
+ <import feature="org.eclipse.jgit" version="7.3.0" match="equivalent"/>
</requires>
<plugin
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml
index 287a723..31ba89f 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml
index 179eab8..2d7ee4e 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml
@@ -2,7 +2,7 @@
<feature
id="org.eclipse.jgit.pgm"
label="%featureName"
- version="7.0.0.qualifier"
+ version="7.3.0.qualifier"
provider-name="%providerName">
<description url="http://www.eclipse.org/jgit/">
@@ -35,9 +35,9 @@
version="0.0.0"/>
<requires>
- <import feature="org.eclipse.jgit" version="7.0.0" match="equivalent"/>
- <import feature="org.eclipse.jgit.lfs" version="7.0.0" match="equivalent"/>
- <import feature="org.eclipse.jgit.ssh.apache" version="7.0.0" match="equivalent"/>
+ <import feature="org.eclipse.jgit" version="7.3.0" match="equivalent"/>
+ <import feature="org.eclipse.jgit.lfs" version="7.3.0" match="equivalent"/>
+ <import feature="org.eclipse.jgit.ssh.apache" version="7.3.0" match="equivalent"/>
</requires>
<plugin
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml
index cbb8e4f..2c80319 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml
index f46e08d..eef699c 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml
@@ -123,12 +123,6 @@
<bundle id="org.eclipse.jetty.util.ajax.source">
<category name="JGit-dependency-bundles"/>
</bundle>
- <bundle id="net.i2p.crypto.eddsa">
- <category name="JGit-dependency-bundles"/>
- </bundle>
- <bundle id="net.i2p.crypto.eddsa.source">
- <category name="JGit-dependency-bundles"/>
- </bundle>
<bundle id="org.apache.ant">
<category name="JGit-dependency-bundles"/>
</bundle>
@@ -147,10 +141,13 @@
<bundle id="org.apache.commons.commons-compress.source">
<category name="JGit-dependency-bundles"/>
</bundle>
- <bundle id="org.apache.commons.logging">
+ <bundle id="org.apache.commons.lang3">
<category name="JGit-dependency-bundles"/>
</bundle>
- <bundle id="org.apache.commons.logging.source">
+ <bundle id="org.apache.commons.lang3.source">
+ <category name="JGit-dependency-bundles"/>
+ </bundle>
+ <bundle id="org.apache.commons.logging">
<category name="JGit-dependency-bundles"/>
</bundle>
<bundle id="org.apache.httpcomponents.httpclient">
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml
index 61b5dd6..032072f 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.repository</artifactId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml
index 4c0068b..139569d 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml
@@ -2,7 +2,7 @@
<feature
id="org.eclipse.jgit.source"
label="%featureName"
- version="7.0.0.qualifier"
+ version="7.3.0.qualifier"
provider-name="%providerName">
<description url="http://www.eclipse.org/jgit/">
@@ -23,7 +23,7 @@
</url>
<requires>
- <import feature="org.eclipse.jgit" version="7.0.0" match="equivalent"/>
+ <import feature="org.eclipse.jgit" version="7.3.0" match="equivalent"/>
</requires>
<plugin
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml
index 12ab069..43568f8 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<groupId>org.eclipse.jgit.feature</groupId>
@@ -30,7 +30,7 @@
<dependency>
<groupId>org.eclipse.jgit.feature</groupId>
<artifactId>org.eclipse.jgit</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</dependency>
</dependencies>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/feature.xml
index 209274f..7f80bbf 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/feature.xml
@@ -2,7 +2,7 @@
<feature
id="org.eclipse.jgit.ssh.apache"
label="%featureName"
- version="7.0.0.qualifier"
+ version="7.3.0.qualifier"
provider-name="%providerName">
<description url="http://www.eclipse.org/jgit/">
@@ -23,7 +23,7 @@
</url>
<requires>
- <import feature="org.eclipse.jgit" version="7.0.0" match="equivalent"/>
+ <import feature="org.eclipse.jgit" version="7.3.0" match="equivalent"/>
</requires>
<plugin
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/pom.xml
index 1d82d4a..fb9f73b 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.apache.feature/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.jsch.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.jsch.feature/feature.xml
index ff46d9e..5eaa3b7 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.jsch.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.jsch.feature/feature.xml
@@ -2,7 +2,7 @@
<feature
id="org.eclipse.jgit.ssh.jsch"
label="%featureName"
- version="7.0.0.qualifier"
+ version="7.3.0.qualifier"
provider-name="%providerName">
<description url="http://www.eclipse.org/jgit/">
@@ -23,7 +23,7 @@
</url>
<requires>
- <import plugin="org.eclipse.jgit" version="7.0.0" match="equivalent"/>
+ <import plugin="org.eclipse.jgit" version="7.3.0" match="equivalent"/>
</requires>
<plugin
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.jsch.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.jsch.feature/pom.xml
index c4d4fcf..d5da669 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.jsch.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.ssh.jsch.feature/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target
deleted file mode 100644
index 8899b51..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target
+++ /dev/null
@@ -1,284 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<?pde?>
-<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.17" sequenceNumber="1715125111">
- <locations>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
- <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
- <unit id="net.i2p.crypto.eddsa" version="0.3.0"/>
- <unit id="net.i2p.crypto.eddsa.source" version="0.3.0"/>
- <unit id="org.apache.ant" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.ant.source" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
- <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.junit" version="4.13.2.v20230809-1000"/>
- <unit id="org.junit.source" version="4.13.2.v20230809-1000"/>
- <unit id="org.objenesis" version="3.3.0"/>
- <unit id="org.objenesis.source" version="3.3.0"/>
- <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
- <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
- <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023-12"/>
- </location>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="org.eclipse.osgi" version="0.0.0"/>
- <repository location="https://download.eclipse.org/releases/2020-09/"/>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
- <dependencies>
- <dependency>
- <groupId>org.tukaani</groupId>
- <artifactId>xz</artifactId>
- <version>1.9</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j">
- <dependencies>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-simple</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd">
- <dependencies>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-osgi</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-sftp</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito">
- <dependencies>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <version>5.10.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna">
- <dependencies>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna-platform</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty">
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jetty.ee10</groupId>
- <artifactId>jetty-ee10-servlet</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-http</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-io</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-security</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-server</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-session</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util-ajax</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>jakarta.servlet</groupId>
- <artifactId>jakarta.servlet-api</artifactId>
- <version>6.0.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah">
- <dependencies>
- <dependency>
- <groupId>com.googlecode.javaewah</groupId>
- <artifactId>JavaEWAH</artifactId>
- <version>1.2.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest">
- <dependencies>
- <dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest</artifactId>
- <version>2.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson">
- <dependencies>
- <dependency>
- <groupId>com.google.code.gson</groupId>
- <artifactId>gson</artifactId>
- <version>2.10.1</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy">
- <dependencies>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy-agent</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle">
- <dependencies>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpg-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpkix-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcutil-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj">
- <dependencies>
- <dependency>
- <groupId>org.assertj</groupId>
- <artifactId>assertj-core</artifactId>
- <version>3.25.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j">
- <dependencies>
- <dependency>
- <groupId>args4j</groupId>
- <artifactId>args4j</artifactId>
- <version>2.33</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache">
- <dependencies>
- <dependency>
- <groupId>commons-codec</groupId>
- <artifactId>commons-codec</artifactId>
- <version>1.16.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-compress</artifactId>
- <version>1.26.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- <version>2.15.1</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- </locations>
-</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.tpd
deleted file mode 100644
index b3ff205..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.tpd
+++ /dev/null
@@ -1,8 +0,0 @@
-target "jgit-4.17" with source configurePhase
-
-include "orbit/orbit-4.30.tpd"
-include "maven/dependencies.tpd"
-
-location "https://download.eclipse.org/releases/2020-09/" {
- org.eclipse.osgi lazy
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.target
deleted file mode 100644
index ee56a72..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.target
+++ /dev/null
@@ -1,284 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<?pde?>
-<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.18" sequenceNumber="1715125111">
- <locations>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
- <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
- <unit id="net.i2p.crypto.eddsa" version="0.3.0"/>
- <unit id="net.i2p.crypto.eddsa.source" version="0.3.0"/>
- <unit id="org.apache.ant" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.ant.source" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
- <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.junit" version="4.13.2.v20230809-1000"/>
- <unit id="org.junit.source" version="4.13.2.v20230809-1000"/>
- <unit id="org.objenesis" version="3.3.0"/>
- <unit id="org.objenesis.source" version="3.3.0"/>
- <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
- <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
- <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023-12"/>
- </location>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="org.eclipse.osgi" version="0.0.0"/>
- <repository location="https://download.eclipse.org/releases/2020-12/"/>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
- <dependencies>
- <dependency>
- <groupId>org.tukaani</groupId>
- <artifactId>xz</artifactId>
- <version>1.9</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j">
- <dependencies>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-simple</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd">
- <dependencies>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-osgi</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-sftp</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito">
- <dependencies>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <version>5.10.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna">
- <dependencies>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna-platform</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty">
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jetty.ee10</groupId>
- <artifactId>jetty-ee10-servlet</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-http</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-io</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-security</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-server</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-session</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util-ajax</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>jakarta.servlet</groupId>
- <artifactId>jakarta.servlet-api</artifactId>
- <version>6.0.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah">
- <dependencies>
- <dependency>
- <groupId>com.googlecode.javaewah</groupId>
- <artifactId>JavaEWAH</artifactId>
- <version>1.2.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest">
- <dependencies>
- <dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest</artifactId>
- <version>2.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson">
- <dependencies>
- <dependency>
- <groupId>com.google.code.gson</groupId>
- <artifactId>gson</artifactId>
- <version>2.10.1</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy">
- <dependencies>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy-agent</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle">
- <dependencies>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpg-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpkix-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcutil-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj">
- <dependencies>
- <dependency>
- <groupId>org.assertj</groupId>
- <artifactId>assertj-core</artifactId>
- <version>3.25.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j">
- <dependencies>
- <dependency>
- <groupId>args4j</groupId>
- <artifactId>args4j</artifactId>
- <version>2.33</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache">
- <dependencies>
- <dependency>
- <groupId>commons-codec</groupId>
- <artifactId>commons-codec</artifactId>
- <version>1.16.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-compress</artifactId>
- <version>1.26.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- <version>2.15.1</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- </locations>
-</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.tpd
deleted file mode 100644
index 719476a..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.tpd
+++ /dev/null
@@ -1,8 +0,0 @@
-target "jgit-4.18" with source configurePhase
-
-include "orbit/orbit-4.30.tpd"
-include "maven/dependencies.tpd"
-
-location "https://download.eclipse.org/releases/2020-12/" {
- org.eclipse.osgi lazy
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.target
deleted file mode 100644
index ad35e58..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.target
+++ /dev/null
@@ -1,284 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<?pde?>
-<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.19-staging" sequenceNumber="1715125111">
- <locations>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
- <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
- <unit id="net.i2p.crypto.eddsa" version="0.3.0"/>
- <unit id="net.i2p.crypto.eddsa.source" version="0.3.0"/>
- <unit id="org.apache.ant" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.ant.source" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
- <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.junit" version="4.13.2.v20230809-1000"/>
- <unit id="org.junit.source" version="4.13.2.v20230809-1000"/>
- <unit id="org.objenesis" version="3.3.0"/>
- <unit id="org.objenesis.source" version="3.3.0"/>
- <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
- <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
- <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023-12"/>
- </location>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="org.eclipse.osgi" version="0.0.0"/>
- <repository location="https://download.eclipse.org/releases/2021-03/"/>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
- <dependencies>
- <dependency>
- <groupId>org.tukaani</groupId>
- <artifactId>xz</artifactId>
- <version>1.9</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j">
- <dependencies>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-simple</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd">
- <dependencies>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-osgi</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-sftp</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito">
- <dependencies>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <version>5.10.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna">
- <dependencies>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna-platform</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty">
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jetty.ee10</groupId>
- <artifactId>jetty-ee10-servlet</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-http</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-io</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-security</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-server</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-session</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util-ajax</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>jakarta.servlet</groupId>
- <artifactId>jakarta.servlet-api</artifactId>
- <version>6.0.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah">
- <dependencies>
- <dependency>
- <groupId>com.googlecode.javaewah</groupId>
- <artifactId>JavaEWAH</artifactId>
- <version>1.2.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest">
- <dependencies>
- <dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest</artifactId>
- <version>2.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson">
- <dependencies>
- <dependency>
- <groupId>com.google.code.gson</groupId>
- <artifactId>gson</artifactId>
- <version>2.10.1</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy">
- <dependencies>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy-agent</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle">
- <dependencies>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpg-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpkix-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcutil-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj">
- <dependencies>
- <dependency>
- <groupId>org.assertj</groupId>
- <artifactId>assertj-core</artifactId>
- <version>3.25.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j">
- <dependencies>
- <dependency>
- <groupId>args4j</groupId>
- <artifactId>args4j</artifactId>
- <version>2.33</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache">
- <dependencies>
- <dependency>
- <groupId>commons-codec</groupId>
- <artifactId>commons-codec</artifactId>
- <version>1.16.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-compress</artifactId>
- <version>1.26.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- <version>2.15.1</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- </locations>
-</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.tpd
deleted file mode 100644
index 9eb4436..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.tpd
+++ /dev/null
@@ -1,8 +0,0 @@
-target "jgit-4.19-staging" with source configurePhase
-
-include "orbit/orbit-4.30.tpd"
-include "maven/dependencies.tpd"
-
-location "https://download.eclipse.org/releases/2021-03/" {
- org.eclipse.osgi lazy
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.target
deleted file mode 100644
index 4031c04..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.target
+++ /dev/null
@@ -1,284 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<?pde?>
-<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.20" sequenceNumber="1715125111">
- <locations>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
- <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
- <unit id="net.i2p.crypto.eddsa" version="0.3.0"/>
- <unit id="net.i2p.crypto.eddsa.source" version="0.3.0"/>
- <unit id="org.apache.ant" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.ant.source" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
- <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.junit" version="4.13.2.v20230809-1000"/>
- <unit id="org.junit.source" version="4.13.2.v20230809-1000"/>
- <unit id="org.objenesis" version="3.3.0"/>
- <unit id="org.objenesis.source" version="3.3.0"/>
- <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
- <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
- <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023-12"/>
- </location>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="org.eclipse.osgi" version="0.0.0"/>
- <repository location="https://download.eclipse.org/releases/2021-06/"/>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
- <dependencies>
- <dependency>
- <groupId>org.tukaani</groupId>
- <artifactId>xz</artifactId>
- <version>1.9</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j">
- <dependencies>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-simple</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd">
- <dependencies>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-osgi</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-sftp</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito">
- <dependencies>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <version>5.10.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna">
- <dependencies>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna-platform</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty">
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jetty.ee10</groupId>
- <artifactId>jetty-ee10-servlet</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-http</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-io</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-security</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-server</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-session</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util-ajax</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>jakarta.servlet</groupId>
- <artifactId>jakarta.servlet-api</artifactId>
- <version>6.0.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah">
- <dependencies>
- <dependency>
- <groupId>com.googlecode.javaewah</groupId>
- <artifactId>JavaEWAH</artifactId>
- <version>1.2.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest">
- <dependencies>
- <dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest</artifactId>
- <version>2.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson">
- <dependencies>
- <dependency>
- <groupId>com.google.code.gson</groupId>
- <artifactId>gson</artifactId>
- <version>2.10.1</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy">
- <dependencies>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy-agent</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle">
- <dependencies>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpg-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpkix-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcutil-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj">
- <dependencies>
- <dependency>
- <groupId>org.assertj</groupId>
- <artifactId>assertj-core</artifactId>
- <version>3.25.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j">
- <dependencies>
- <dependency>
- <groupId>args4j</groupId>
- <artifactId>args4j</artifactId>
- <version>2.33</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache">
- <dependencies>
- <dependency>
- <groupId>commons-codec</groupId>
- <artifactId>commons-codec</artifactId>
- <version>1.16.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-compress</artifactId>
- <version>1.26.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- <version>2.15.1</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- </locations>
-</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.tpd
deleted file mode 100644
index 264c040..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.tpd
+++ /dev/null
@@ -1,8 +0,0 @@
-target "jgit-4.20" with source configurePhase
-
-include "orbit/orbit-4.30.tpd"
-include "maven/dependencies.tpd"
-
-location "https://download.eclipse.org/releases/2021-06/" {
- org.eclipse.osgi lazy
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.target
deleted file mode 100644
index 1a119f2..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.target
+++ /dev/null
@@ -1,284 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<?pde?>
-<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.21" sequenceNumber="1715125111">
- <locations>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
- <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
- <unit id="net.i2p.crypto.eddsa" version="0.3.0"/>
- <unit id="net.i2p.crypto.eddsa.source" version="0.3.0"/>
- <unit id="org.apache.ant" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.ant.source" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
- <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.junit" version="4.13.2.v20230809-1000"/>
- <unit id="org.junit.source" version="4.13.2.v20230809-1000"/>
- <unit id="org.objenesis" version="3.3.0"/>
- <unit id="org.objenesis.source" version="3.3.0"/>
- <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
- <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
- <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023-12"/>
- </location>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="org.eclipse.osgi" version="0.0.0"/>
- <repository location="https://download.eclipse.org/releases/2021-09/"/>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
- <dependencies>
- <dependency>
- <groupId>org.tukaani</groupId>
- <artifactId>xz</artifactId>
- <version>1.9</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j">
- <dependencies>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-simple</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd">
- <dependencies>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-osgi</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-sftp</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito">
- <dependencies>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <version>5.10.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna">
- <dependencies>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna-platform</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty">
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jetty.ee10</groupId>
- <artifactId>jetty-ee10-servlet</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-http</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-io</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-security</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-server</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-session</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util-ajax</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>jakarta.servlet</groupId>
- <artifactId>jakarta.servlet-api</artifactId>
- <version>6.0.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah">
- <dependencies>
- <dependency>
- <groupId>com.googlecode.javaewah</groupId>
- <artifactId>JavaEWAH</artifactId>
- <version>1.2.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest">
- <dependencies>
- <dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest</artifactId>
- <version>2.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson">
- <dependencies>
- <dependency>
- <groupId>com.google.code.gson</groupId>
- <artifactId>gson</artifactId>
- <version>2.10.1</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy">
- <dependencies>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy-agent</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle">
- <dependencies>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpg-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpkix-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcutil-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj">
- <dependencies>
- <dependency>
- <groupId>org.assertj</groupId>
- <artifactId>assertj-core</artifactId>
- <version>3.25.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j">
- <dependencies>
- <dependency>
- <groupId>args4j</groupId>
- <artifactId>args4j</artifactId>
- <version>2.33</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache">
- <dependencies>
- <dependency>
- <groupId>commons-codec</groupId>
- <artifactId>commons-codec</artifactId>
- <version>1.16.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-compress</artifactId>
- <version>1.26.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- <version>2.15.1</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- </locations>
-</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.tpd
deleted file mode 100644
index 5c7a112..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.tpd
+++ /dev/null
@@ -1,8 +0,0 @@
-target "jgit-4.21" with source configurePhase
-
-include "orbit/orbit-4.30.tpd"
-include "maven/dependencies.tpd"
-
-location "https://download.eclipse.org/releases/2021-09/" {
- org.eclipse.osgi lazy
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.22.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.22.target
deleted file mode 100644
index 1f6ee4e..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.22.target
+++ /dev/null
@@ -1,284 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<?pde?>
-<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.22" sequenceNumber="1715125110">
- <locations>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
- <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
- <unit id="net.i2p.crypto.eddsa" version="0.3.0"/>
- <unit id="net.i2p.crypto.eddsa.source" version="0.3.0"/>
- <unit id="org.apache.ant" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.ant.source" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
- <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.junit" version="4.13.2.v20230809-1000"/>
- <unit id="org.junit.source" version="4.13.2.v20230809-1000"/>
- <unit id="org.objenesis" version="3.3.0"/>
- <unit id="org.objenesis.source" version="3.3.0"/>
- <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
- <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
- <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023-12"/>
- </location>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="org.eclipse.osgi" version="0.0.0"/>
- <repository location="https://download.eclipse.org/releases/2021-12/"/>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
- <dependencies>
- <dependency>
- <groupId>org.tukaani</groupId>
- <artifactId>xz</artifactId>
- <version>1.9</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j">
- <dependencies>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-simple</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd">
- <dependencies>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-osgi</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-sftp</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito">
- <dependencies>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <version>5.10.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna">
- <dependencies>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna-platform</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty">
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jetty.ee10</groupId>
- <artifactId>jetty-ee10-servlet</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-http</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-io</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-security</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-server</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-session</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util-ajax</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>jakarta.servlet</groupId>
- <artifactId>jakarta.servlet-api</artifactId>
- <version>6.0.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah">
- <dependencies>
- <dependency>
- <groupId>com.googlecode.javaewah</groupId>
- <artifactId>JavaEWAH</artifactId>
- <version>1.2.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest">
- <dependencies>
- <dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest</artifactId>
- <version>2.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson">
- <dependencies>
- <dependency>
- <groupId>com.google.code.gson</groupId>
- <artifactId>gson</artifactId>
- <version>2.10.1</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy">
- <dependencies>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy-agent</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle">
- <dependencies>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpg-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpkix-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcutil-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj">
- <dependencies>
- <dependency>
- <groupId>org.assertj</groupId>
- <artifactId>assertj-core</artifactId>
- <version>3.25.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j">
- <dependencies>
- <dependency>
- <groupId>args4j</groupId>
- <artifactId>args4j</artifactId>
- <version>2.33</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache">
- <dependencies>
- <dependency>
- <groupId>commons-codec</groupId>
- <artifactId>commons-codec</artifactId>
- <version>1.16.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-compress</artifactId>
- <version>1.26.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- <version>2.15.1</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- </locations>
-</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.22.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.22.tpd
deleted file mode 100644
index ecc776e..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.22.tpd
+++ /dev/null
@@ -1,8 +0,0 @@
-target "jgit-4.22" with source configurePhase
-
-include "orbit/orbit-4.30.tpd"
-include "maven/dependencies.tpd"
-
-location "https://download.eclipse.org/releases/2021-12/" {
- org.eclipse.osgi lazy
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.23.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.23.target
deleted file mode 100644
index d0a0420..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.23.target
+++ /dev/null
@@ -1,284 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<?pde?>
-<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.23" sequenceNumber="1715125110">
- <locations>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
- <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
- <unit id="net.i2p.crypto.eddsa" version="0.3.0"/>
- <unit id="net.i2p.crypto.eddsa.source" version="0.3.0"/>
- <unit id="org.apache.ant" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.ant.source" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
- <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.junit" version="4.13.2.v20230809-1000"/>
- <unit id="org.junit.source" version="4.13.2.v20230809-1000"/>
- <unit id="org.objenesis" version="3.3.0"/>
- <unit id="org.objenesis.source" version="3.3.0"/>
- <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
- <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
- <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023-12"/>
- </location>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="org.eclipse.osgi" version="0.0.0"/>
- <repository location="https://download.eclipse.org/releases/2022-03/"/>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
- <dependencies>
- <dependency>
- <groupId>org.tukaani</groupId>
- <artifactId>xz</artifactId>
- <version>1.9</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j">
- <dependencies>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-simple</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd">
- <dependencies>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-osgi</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-sftp</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito">
- <dependencies>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <version>5.10.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna">
- <dependencies>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna-platform</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty">
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jetty.ee10</groupId>
- <artifactId>jetty-ee10-servlet</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-http</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-io</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-security</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-server</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-session</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util-ajax</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>jakarta.servlet</groupId>
- <artifactId>jakarta.servlet-api</artifactId>
- <version>6.0.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah">
- <dependencies>
- <dependency>
- <groupId>com.googlecode.javaewah</groupId>
- <artifactId>JavaEWAH</artifactId>
- <version>1.2.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest">
- <dependencies>
- <dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest</artifactId>
- <version>2.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson">
- <dependencies>
- <dependency>
- <groupId>com.google.code.gson</groupId>
- <artifactId>gson</artifactId>
- <version>2.10.1</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy">
- <dependencies>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy-agent</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle">
- <dependencies>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpg-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpkix-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcutil-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj">
- <dependencies>
- <dependency>
- <groupId>org.assertj</groupId>
- <artifactId>assertj-core</artifactId>
- <version>3.25.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j">
- <dependencies>
- <dependency>
- <groupId>args4j</groupId>
- <artifactId>args4j</artifactId>
- <version>2.33</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache">
- <dependencies>
- <dependency>
- <groupId>commons-codec</groupId>
- <artifactId>commons-codec</artifactId>
- <version>1.16.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-compress</artifactId>
- <version>1.26.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- <version>2.15.1</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- </locations>
-</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.23.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.23.tpd
deleted file mode 100644
index 16efb40..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.23.tpd
+++ /dev/null
@@ -1,8 +0,0 @@
-target "jgit-4.23" with source configurePhase
-
-include "orbit/orbit-4.30.tpd"
-include "maven/dependencies.tpd"
-
-location "https://download.eclipse.org/releases/2022-03/" {
- org.eclipse.osgi lazy
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.24.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.24.target
deleted file mode 100644
index dc9e090..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.24.target
+++ /dev/null
@@ -1,284 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<?pde?>
-<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.24" sequenceNumber="1715125110">
- <locations>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
- <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
- <unit id="net.i2p.crypto.eddsa" version="0.3.0"/>
- <unit id="net.i2p.crypto.eddsa.source" version="0.3.0"/>
- <unit id="org.apache.ant" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.ant.source" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
- <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.junit" version="4.13.2.v20230809-1000"/>
- <unit id="org.junit.source" version="4.13.2.v20230809-1000"/>
- <unit id="org.objenesis" version="3.3.0"/>
- <unit id="org.objenesis.source" version="3.3.0"/>
- <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
- <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
- <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023-12"/>
- </location>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="org.eclipse.osgi" version="0.0.0"/>
- <repository location="https://download.eclipse.org/releases/2022-06/"/>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
- <dependencies>
- <dependency>
- <groupId>org.tukaani</groupId>
- <artifactId>xz</artifactId>
- <version>1.9</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j">
- <dependencies>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-simple</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd">
- <dependencies>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-osgi</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-sftp</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito">
- <dependencies>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <version>5.10.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna">
- <dependencies>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna-platform</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty">
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jetty.ee10</groupId>
- <artifactId>jetty-ee10-servlet</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-http</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-io</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-security</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-server</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-session</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util-ajax</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>jakarta.servlet</groupId>
- <artifactId>jakarta.servlet-api</artifactId>
- <version>6.0.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah">
- <dependencies>
- <dependency>
- <groupId>com.googlecode.javaewah</groupId>
- <artifactId>JavaEWAH</artifactId>
- <version>1.2.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest">
- <dependencies>
- <dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest</artifactId>
- <version>2.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson">
- <dependencies>
- <dependency>
- <groupId>com.google.code.gson</groupId>
- <artifactId>gson</artifactId>
- <version>2.10.1</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy">
- <dependencies>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy-agent</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle">
- <dependencies>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpg-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpkix-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcutil-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj">
- <dependencies>
- <dependency>
- <groupId>org.assertj</groupId>
- <artifactId>assertj-core</artifactId>
- <version>3.25.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j">
- <dependencies>
- <dependency>
- <groupId>args4j</groupId>
- <artifactId>args4j</artifactId>
- <version>2.33</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache">
- <dependencies>
- <dependency>
- <groupId>commons-codec</groupId>
- <artifactId>commons-codec</artifactId>
- <version>1.16.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-compress</artifactId>
- <version>1.26.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- <version>2.15.1</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- </locations>
-</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.24.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.24.tpd
deleted file mode 100644
index d0f8e8d..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.24.tpd
+++ /dev/null
@@ -1,8 +0,0 @@
-target "jgit-4.24" with source configurePhase
-
-include "orbit/orbit-4.30.tpd"
-include "maven/dependencies.tpd"
-
-location "https://download.eclipse.org/releases/2022-06/" {
- org.eclipse.osgi lazy
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.25.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.25.target
deleted file mode 100644
index 53dcb36..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.25.target
+++ /dev/null
@@ -1,284 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<?pde?>
-<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.25" sequenceNumber="1715125110">
- <locations>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
- <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
- <unit id="net.i2p.crypto.eddsa" version="0.3.0"/>
- <unit id="net.i2p.crypto.eddsa.source" version="0.3.0"/>
- <unit id="org.apache.ant" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.ant.source" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
- <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.junit" version="4.13.2.v20230809-1000"/>
- <unit id="org.junit.source" version="4.13.2.v20230809-1000"/>
- <unit id="org.objenesis" version="3.3.0"/>
- <unit id="org.objenesis.source" version="3.3.0"/>
- <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
- <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
- <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023-12"/>
- </location>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="org.eclipse.osgi" version="0.0.0"/>
- <repository location="https://download.eclipse.org/releases/2022-09/"/>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
- <dependencies>
- <dependency>
- <groupId>org.tukaani</groupId>
- <artifactId>xz</artifactId>
- <version>1.9</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j">
- <dependencies>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-simple</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd">
- <dependencies>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-osgi</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-sftp</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito">
- <dependencies>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <version>5.10.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna">
- <dependencies>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna-platform</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty">
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jetty.ee10</groupId>
- <artifactId>jetty-ee10-servlet</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-http</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-io</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-security</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-server</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-session</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util-ajax</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>jakarta.servlet</groupId>
- <artifactId>jakarta.servlet-api</artifactId>
- <version>6.0.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah">
- <dependencies>
- <dependency>
- <groupId>com.googlecode.javaewah</groupId>
- <artifactId>JavaEWAH</artifactId>
- <version>1.2.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest">
- <dependencies>
- <dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest</artifactId>
- <version>2.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson">
- <dependencies>
- <dependency>
- <groupId>com.google.code.gson</groupId>
- <artifactId>gson</artifactId>
- <version>2.10.1</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy">
- <dependencies>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy-agent</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle">
- <dependencies>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpg-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpkix-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcutil-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj">
- <dependencies>
- <dependency>
- <groupId>org.assertj</groupId>
- <artifactId>assertj-core</artifactId>
- <version>3.25.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j">
- <dependencies>
- <dependency>
- <groupId>args4j</groupId>
- <artifactId>args4j</artifactId>
- <version>2.33</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache">
- <dependencies>
- <dependency>
- <groupId>commons-codec</groupId>
- <artifactId>commons-codec</artifactId>
- <version>1.16.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-compress</artifactId>
- <version>1.26.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- <version>2.15.1</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- </locations>
-</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.25.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.25.tpd
deleted file mode 100644
index be37c10..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.25.tpd
+++ /dev/null
@@ -1,8 +0,0 @@
-target "jgit-4.25" with source configurePhase
-
-include "orbit/orbit-4.30.tpd"
-include "maven/dependencies.tpd"
-
-location "https://download.eclipse.org/releases/2022-09/" {
- org.eclipse.osgi lazy
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.26.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.26.target
deleted file mode 100644
index 822c7cf..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.26.target
+++ /dev/null
@@ -1,284 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<?pde?>
-<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.26" sequenceNumber="1715125110">
- <locations>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
- <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
- <unit id="net.i2p.crypto.eddsa" version="0.3.0"/>
- <unit id="net.i2p.crypto.eddsa.source" version="0.3.0"/>
- <unit id="org.apache.ant" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.ant.source" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
- <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.junit" version="4.13.2.v20230809-1000"/>
- <unit id="org.junit.source" version="4.13.2.v20230809-1000"/>
- <unit id="org.objenesis" version="3.3.0"/>
- <unit id="org.objenesis.source" version="3.3.0"/>
- <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
- <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
- <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023-12"/>
- </location>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="org.eclipse.osgi" version="0.0.0"/>
- <repository location="https://download.eclipse.org/releases/2022-12/"/>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
- <dependencies>
- <dependency>
- <groupId>org.tukaani</groupId>
- <artifactId>xz</artifactId>
- <version>1.9</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j">
- <dependencies>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-simple</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd">
- <dependencies>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-osgi</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-sftp</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito">
- <dependencies>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <version>5.10.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna">
- <dependencies>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna-platform</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty">
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jetty.ee10</groupId>
- <artifactId>jetty-ee10-servlet</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-http</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-io</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-security</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-server</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-session</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util-ajax</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>jakarta.servlet</groupId>
- <artifactId>jakarta.servlet-api</artifactId>
- <version>6.0.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah">
- <dependencies>
- <dependency>
- <groupId>com.googlecode.javaewah</groupId>
- <artifactId>JavaEWAH</artifactId>
- <version>1.2.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest">
- <dependencies>
- <dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest</artifactId>
- <version>2.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson">
- <dependencies>
- <dependency>
- <groupId>com.google.code.gson</groupId>
- <artifactId>gson</artifactId>
- <version>2.10.1</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy">
- <dependencies>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy-agent</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle">
- <dependencies>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpg-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpkix-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcutil-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj">
- <dependencies>
- <dependency>
- <groupId>org.assertj</groupId>
- <artifactId>assertj-core</artifactId>
- <version>3.25.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j">
- <dependencies>
- <dependency>
- <groupId>args4j</groupId>
- <artifactId>args4j</artifactId>
- <version>2.33</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache">
- <dependencies>
- <dependency>
- <groupId>commons-codec</groupId>
- <artifactId>commons-codec</artifactId>
- <version>1.16.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-compress</artifactId>
- <version>1.26.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- <version>2.15.1</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- </locations>
-</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.26.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.26.tpd
deleted file mode 100644
index e269919..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.26.tpd
+++ /dev/null
@@ -1,8 +0,0 @@
-target "jgit-4.26" with source configurePhase
-
-include "orbit/orbit-4.30.tpd"
-include "maven/dependencies.tpd"
-
-location "https://download.eclipse.org/releases/2022-12/" {
- org.eclipse.osgi lazy
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.27.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.27.target
deleted file mode 100644
index 9a0cab4..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.27.target
+++ /dev/null
@@ -1,284 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<?pde?>
-<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.27" sequenceNumber="1715125110">
- <locations>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
- <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
- <unit id="net.i2p.crypto.eddsa" version="0.3.0"/>
- <unit id="net.i2p.crypto.eddsa.source" version="0.3.0"/>
- <unit id="org.apache.ant" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.ant.source" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
- <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.junit" version="4.13.2.v20230809-1000"/>
- <unit id="org.junit.source" version="4.13.2.v20230809-1000"/>
- <unit id="org.objenesis" version="3.3.0"/>
- <unit id="org.objenesis.source" version="3.3.0"/>
- <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
- <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
- <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023-12"/>
- </location>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="org.eclipse.osgi" version="0.0.0"/>
- <repository location="https://download.eclipse.org/releases/2023-03/"/>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
- <dependencies>
- <dependency>
- <groupId>org.tukaani</groupId>
- <artifactId>xz</artifactId>
- <version>1.9</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j">
- <dependencies>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-simple</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd">
- <dependencies>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-osgi</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-sftp</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito">
- <dependencies>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <version>5.10.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna">
- <dependencies>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna-platform</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty">
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jetty.ee10</groupId>
- <artifactId>jetty-ee10-servlet</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-http</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-io</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-security</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-server</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-session</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util-ajax</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>jakarta.servlet</groupId>
- <artifactId>jakarta.servlet-api</artifactId>
- <version>6.0.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah">
- <dependencies>
- <dependency>
- <groupId>com.googlecode.javaewah</groupId>
- <artifactId>JavaEWAH</artifactId>
- <version>1.2.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest">
- <dependencies>
- <dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest</artifactId>
- <version>2.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson">
- <dependencies>
- <dependency>
- <groupId>com.google.code.gson</groupId>
- <artifactId>gson</artifactId>
- <version>2.10.1</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy">
- <dependencies>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy-agent</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle">
- <dependencies>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpg-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpkix-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcutil-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj">
- <dependencies>
- <dependency>
- <groupId>org.assertj</groupId>
- <artifactId>assertj-core</artifactId>
- <version>3.25.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j">
- <dependencies>
- <dependency>
- <groupId>args4j</groupId>
- <artifactId>args4j</artifactId>
- <version>2.33</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache">
- <dependencies>
- <dependency>
- <groupId>commons-codec</groupId>
- <artifactId>commons-codec</artifactId>
- <version>1.16.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-compress</artifactId>
- <version>1.26.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- <version>2.15.1</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- </locations>
-</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.27.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.27.tpd
deleted file mode 100644
index b67718a..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.27.tpd
+++ /dev/null
@@ -1,8 +0,0 @@
-target "jgit-4.27" with source configurePhase
-
-include "orbit/orbit-4.30.tpd"
-include "maven/dependencies.tpd"
-
-location "https://download.eclipse.org/releases/2023-03/" {
- org.eclipse.osgi lazy
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.28.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.28.target
deleted file mode 100644
index 22aa30e..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.28.target
+++ /dev/null
@@ -1,284 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<?pde?>
-<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.28" sequenceNumber="1715125110">
- <locations>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
- <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
- <unit id="net.i2p.crypto.eddsa" version="0.3.0"/>
- <unit id="net.i2p.crypto.eddsa.source" version="0.3.0"/>
- <unit id="org.apache.ant" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.ant.source" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
- <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.junit" version="4.13.2.v20230809-1000"/>
- <unit id="org.junit.source" version="4.13.2.v20230809-1000"/>
- <unit id="org.objenesis" version="3.3.0"/>
- <unit id="org.objenesis.source" version="3.3.0"/>
- <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
- <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
- <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023-12"/>
- </location>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="org.eclipse.osgi" version="0.0.0"/>
- <repository location="https://download.eclipse.org/releases/2023-06"/>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
- <dependencies>
- <dependency>
- <groupId>org.tukaani</groupId>
- <artifactId>xz</artifactId>
- <version>1.9</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j">
- <dependencies>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-simple</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd">
- <dependencies>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-osgi</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-sftp</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito">
- <dependencies>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <version>5.10.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna">
- <dependencies>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna-platform</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty">
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jetty.ee10</groupId>
- <artifactId>jetty-ee10-servlet</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-http</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-io</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-security</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-server</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-session</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util-ajax</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>jakarta.servlet</groupId>
- <artifactId>jakarta.servlet-api</artifactId>
- <version>6.0.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah">
- <dependencies>
- <dependency>
- <groupId>com.googlecode.javaewah</groupId>
- <artifactId>JavaEWAH</artifactId>
- <version>1.2.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest">
- <dependencies>
- <dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest</artifactId>
- <version>2.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson">
- <dependencies>
- <dependency>
- <groupId>com.google.code.gson</groupId>
- <artifactId>gson</artifactId>
- <version>2.10.1</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy">
- <dependencies>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy-agent</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle">
- <dependencies>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpg-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpkix-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcutil-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj">
- <dependencies>
- <dependency>
- <groupId>org.assertj</groupId>
- <artifactId>assertj-core</artifactId>
- <version>3.25.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j">
- <dependencies>
- <dependency>
- <groupId>args4j</groupId>
- <artifactId>args4j</artifactId>
- <version>2.33</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache">
- <dependencies>
- <dependency>
- <groupId>commons-codec</groupId>
- <artifactId>commons-codec</artifactId>
- <version>1.16.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-compress</artifactId>
- <version>1.26.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- <version>2.15.1</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- </locations>
-</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.28.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.28.tpd
deleted file mode 100644
index 1a9a22a..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.28.tpd
+++ /dev/null
@@ -1,8 +0,0 @@
-target "jgit-4.28" with source configurePhase
-
-include "orbit/orbit-4.30.tpd"
-include "maven/dependencies.tpd"
-
-location "https://download.eclipse.org/releases/2023-06" {
- org.eclipse.osgi lazy
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.29.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.29.target
deleted file mode 100644
index f7fb7c9..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.29.target
+++ /dev/null
@@ -1,284 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<?pde?>
-<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.29" sequenceNumber="1715125110">
- <locations>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
- <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
- <unit id="net.i2p.crypto.eddsa" version="0.3.0"/>
- <unit id="net.i2p.crypto.eddsa.source" version="0.3.0"/>
- <unit id="org.apache.ant" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.ant.source" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
- <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.junit" version="4.13.2.v20230809-1000"/>
- <unit id="org.junit.source" version="4.13.2.v20230809-1000"/>
- <unit id="org.objenesis" version="3.3.0"/>
- <unit id="org.objenesis.source" version="3.3.0"/>
- <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
- <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
- <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023-12"/>
- </location>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="org.eclipse.osgi" version="0.0.0"/>
- <repository location="https://download.eclipse.org/releases/2023-09"/>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
- <dependencies>
- <dependency>
- <groupId>org.tukaani</groupId>
- <artifactId>xz</artifactId>
- <version>1.9</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j">
- <dependencies>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-simple</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd">
- <dependencies>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-osgi</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-sftp</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito">
- <dependencies>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <version>5.10.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna">
- <dependencies>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna-platform</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty">
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jetty.ee10</groupId>
- <artifactId>jetty-ee10-servlet</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-http</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-io</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-security</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-server</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-session</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util-ajax</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>jakarta.servlet</groupId>
- <artifactId>jakarta.servlet-api</artifactId>
- <version>6.0.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah">
- <dependencies>
- <dependency>
- <groupId>com.googlecode.javaewah</groupId>
- <artifactId>JavaEWAH</artifactId>
- <version>1.2.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest">
- <dependencies>
- <dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest</artifactId>
- <version>2.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson">
- <dependencies>
- <dependency>
- <groupId>com.google.code.gson</groupId>
- <artifactId>gson</artifactId>
- <version>2.10.1</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy">
- <dependencies>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy-agent</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle">
- <dependencies>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpg-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpkix-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcutil-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj">
- <dependencies>
- <dependency>
- <groupId>org.assertj</groupId>
- <artifactId>assertj-core</artifactId>
- <version>3.25.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j">
- <dependencies>
- <dependency>
- <groupId>args4j</groupId>
- <artifactId>args4j</artifactId>
- <version>2.33</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache">
- <dependencies>
- <dependency>
- <groupId>commons-codec</groupId>
- <artifactId>commons-codec</artifactId>
- <version>1.16.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-compress</artifactId>
- <version>1.26.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- <version>2.15.1</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- </locations>
-</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.29.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.29.tpd
deleted file mode 100644
index 4e34280..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.29.tpd
+++ /dev/null
@@ -1,8 +0,0 @@
-target "jgit-4.29" with source configurePhase
-
-include "orbit/orbit-4.30.tpd"
-include "maven/dependencies.tpd"
-
-location "https://download.eclipse.org/releases/2023-09" {
- org.eclipse.osgi lazy
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.30.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.30.target
deleted file mode 100644
index 733559d..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.30.target
+++ /dev/null
@@ -1,284 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<?pde?>
-<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.30" sequenceNumber="1715125110">
- <locations>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
- <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
- <unit id="net.i2p.crypto.eddsa" version="0.3.0"/>
- <unit id="net.i2p.crypto.eddsa.source" version="0.3.0"/>
- <unit id="org.apache.ant" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.ant.source" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
- <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.junit" version="4.13.2.v20230809-1000"/>
- <unit id="org.junit.source" version="4.13.2.v20230809-1000"/>
- <unit id="org.objenesis" version="3.3.0"/>
- <unit id="org.objenesis.source" version="3.3.0"/>
- <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
- <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
- <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023-12"/>
- </location>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="org.eclipse.osgi" version="0.0.0"/>
- <repository location="https://download.eclipse.org/staging/2023-12/"/>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
- <dependencies>
- <dependency>
- <groupId>org.tukaani</groupId>
- <artifactId>xz</artifactId>
- <version>1.9</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j">
- <dependencies>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-simple</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd">
- <dependencies>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-osgi</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-sftp</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito">
- <dependencies>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <version>5.10.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna">
- <dependencies>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna-platform</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty">
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jetty.ee10</groupId>
- <artifactId>jetty-ee10-servlet</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-http</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-io</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-security</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-server</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-session</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util-ajax</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>jakarta.servlet</groupId>
- <artifactId>jakarta.servlet-api</artifactId>
- <version>6.0.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah">
- <dependencies>
- <dependency>
- <groupId>com.googlecode.javaewah</groupId>
- <artifactId>JavaEWAH</artifactId>
- <version>1.2.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest">
- <dependencies>
- <dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest</artifactId>
- <version>2.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson">
- <dependencies>
- <dependency>
- <groupId>com.google.code.gson</groupId>
- <artifactId>gson</artifactId>
- <version>2.10.1</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy">
- <dependencies>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy-agent</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle">
- <dependencies>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpg-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpkix-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcutil-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj">
- <dependencies>
- <dependency>
- <groupId>org.assertj</groupId>
- <artifactId>assertj-core</artifactId>
- <version>3.25.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j">
- <dependencies>
- <dependency>
- <groupId>args4j</groupId>
- <artifactId>args4j</artifactId>
- <version>2.33</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache">
- <dependencies>
- <dependency>
- <groupId>commons-codec</groupId>
- <artifactId>commons-codec</artifactId>
- <version>1.16.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-compress</artifactId>
- <version>1.26.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- <version>2.15.1</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- </locations>
-</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.30.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.30.tpd
deleted file mode 100644
index dfb4474..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.30.tpd
+++ /dev/null
@@ -1,8 +0,0 @@
-target "jgit-4.30" with source configurePhase
-
-include "orbit/orbit-4.30.tpd"
-include "maven/dependencies.tpd"
-
-location "https://download.eclipse.org/staging/2023-12/" {
- org.eclipse.osgi lazy
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.31.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.31.target
deleted file mode 100644
index 78ef168..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.31.target
+++ /dev/null
@@ -1,284 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<?pde?>
-<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.31" sequenceNumber="1715125110">
- <locations>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
- <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
- <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
- <unit id="net.i2p.crypto.eddsa" version="0.3.0"/>
- <unit id="net.i2p.crypto.eddsa.source" version="0.3.0"/>
- <unit id="org.apache.ant" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.ant.source" version="1.10.14.v20230922-1200"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
- <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
- <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
- <unit id="org.junit" version="4.13.2.v20230809-1000"/>
- <unit id="org.junit.source" version="4.13.2.v20230809-1000"/>
- <unit id="org.objenesis" version="3.3.0"/>
- <unit id="org.objenesis.source" version="3.3.0"/>
- <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
- <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
- <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023-12"/>
- </location>
- <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="org.eclipse.osgi" version="0.0.0"/>
- <repository location="https://download.eclipse.org/staging/2024-03/"/>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
- <dependencies>
- <dependency>
- <groupId>org.tukaani</groupId>
- <artifactId>xz</artifactId>
- <version>1.9</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j">
- <dependencies>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-simple</artifactId>
- <version>1.7.36</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd">
- <dependencies>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-osgi</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-sftp</artifactId>
- <version>2.12.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito">
- <dependencies>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <version>5.10.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna">
- <dependencies>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.java.dev.jna</groupId>
- <artifactId>jna-platform</artifactId>
- <version>5.14.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty">
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jetty.ee10</groupId>
- <artifactId>jetty-ee10-servlet</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-http</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-io</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-security</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-server</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-session</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util-ajax</artifactId>
- <version>12.0.9</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>jakarta.servlet</groupId>
- <artifactId>jakarta.servlet-api</artifactId>
- <version>6.0.0</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah">
- <dependencies>
- <dependency>
- <groupId>com.googlecode.javaewah</groupId>
- <artifactId>JavaEWAH</artifactId>
- <version>1.2.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest">
- <dependencies>
- <dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest</artifactId>
- <version>2.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson">
- <dependencies>
- <dependency>
- <groupId>com.google.code.gson</groupId>
- <artifactId>gson</artifactId>
- <version>2.10.1</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy">
- <dependencies>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>net.bytebuddy</groupId>
- <artifactId>byte-buddy-agent</artifactId>
- <version>1.14.12</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle">
- <dependencies>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpg-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpkix-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcutil-jdk18on</artifactId>
- <version>1.77</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj">
- <dependencies>
- <dependency>
- <groupId>org.assertj</groupId>
- <artifactId>assertj-core</artifactId>
- <version>3.25.3</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j">
- <dependencies>
- <dependency>
- <groupId>args4j</groupId>
- <artifactId>args4j</artifactId>
- <version>2.33</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache">
- <dependencies>
- <dependency>
- <groupId>commons-codec</groupId>
- <artifactId>commons-codec</artifactId>
- <version>1.16.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-compress</artifactId>
- <version>1.26.0</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- <version>2.15.1</version>
- <type>jar</type>
- </dependency>
- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.2</version>
- <type>jar</type>
- </dependency>
- </dependencies>
- </location>
- </locations>
-</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.31.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.31.tpd
deleted file mode 100644
index 58491c8..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.31.tpd
+++ /dev/null
@@ -1,8 +0,0 @@
-target "jgit-4.31" with source configurePhase
-
-include "orbit/orbit-4.31.tpd"
-include "maven/dependencies.tpd"
-
-location "https://download.eclipse.org/staging/2024-03/" {
- org.eclipse.osgi lazy
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.32.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.32.target
new file mode 100644
index 0000000..60baf0b
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.32.target
@@ -0,0 +1,288 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?pde?>
+<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
+<target name="jgit-4.32" sequenceNumber="1740521280">
+ <locations>
+ <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+ <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
+ <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
+ <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
+ <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
+ <unit id="org.apache.ant" version="1.10.14.v20230922-1200"/>
+ <unit id="org.apache.ant.source" version="1.10.14.v20230922-1200"/>
+ <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
+ <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
+ <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
+ <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
+ <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
+ <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
+ <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
+ <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
+ <unit id="org.junit" version="4.13.2.v20230809-1000"/>
+ <unit id="org.junit.source" version="4.13.2.v20230809-1000"/>
+ <unit id="org.objenesis" version="3.4.0"/>
+ <unit id="org.objenesis.source" version="3.4.0"/>
+ <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
+ <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
+ <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2024-06"/>
+ </location>
+ <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+ <unit id="org.eclipse.osgi" version="0.0.0"/>
+ <repository location="https://download.eclipse.org/staging/2024-06/"/>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
+ <dependencies>
+ <dependency>
+ <groupId>org.tukaani</groupId>
+ <artifactId>xz</artifactId>
+ <version>1.10</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j">
+ <dependencies>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>1.7.36</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ <version>1.7.36</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd">
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-osgi</artifactId>
+ <version>2.15.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-sftp</artifactId>
+ <version>2.15.0</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito">
+ <dependencies>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <version>5.15.2</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna">
+ <dependencies>
+ <dependency>
+ <groupId>net.java.dev.jna</groupId>
+ <artifactId>jna</artifactId>
+ <version>5.16.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>net.java.dev.jna</groupId>
+ <artifactId>jna-platform</artifactId>
+ <version>5.16.0</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty">
+ <dependencies>
+ <dependency>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-servlet</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-http</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-io</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-security</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-server</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-session</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-util</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-util-ajax</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>jakarta.servlet</groupId>
+ <artifactId>jakarta.servlet-api</artifactId>
+ <version>6.1.0</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah">
+ <dependencies>
+ <dependency>
+ <groupId>com.googlecode.javaewah</groupId>
+ <artifactId>JavaEWAH</artifactId>
+ <version>1.2.3</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest">
+ <dependencies>
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest</artifactId>
+ <version>2.2</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson">
+ <dependencies>
+ <dependency>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ <version>2.12.1</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy">
+ <dependencies>
+ <dependency>
+ <groupId>net.bytebuddy</groupId>
+ <artifactId>byte-buddy</artifactId>
+ <version>1.17.1</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>net.bytebuddy</groupId>
+ <artifactId>byte-buddy-agent</artifactId>
+ <version>1.17.1</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle">
+ <dependencies>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpg-jdk18on</artifactId>
+ <version>1.80</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk18on</artifactId>
+ <version>1.80</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpkix-jdk18on</artifactId>
+ <version>1.80</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcutil-jdk18on</artifactId>
+ <version>1.80</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj">
+ <dependencies>
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <version>3.27.3</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j">
+ <dependencies>
+ <dependency>
+ <groupId>args4j</groupId>
+ <artifactId>args4j</artifactId>
+ <version>2.37</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache">
+ <dependencies>
+ <dependency>
+ <groupId>commons-codec</groupId>
+ <artifactId>commons-codec</artifactId>
+ <version>1.18.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-compress</artifactId>
+ <version>1.27.1</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ <version>3.17.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>2.18.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ <version>1.3.5</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ </locations>
+</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.32.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.32.tpd
new file mode 100644
index 0000000..b8574c7
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.32.tpd
@@ -0,0 +1,8 @@
+target "jgit-4.32" with source configurePhase
+
+include "orbit/orbit-4.32.tpd"
+include "maven/dependencies.tpd"
+
+location "https://download.eclipse.org/staging/2024-06/" {
+ org.eclipse.osgi lazy
+}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.33.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.33.target
new file mode 100644
index 0000000..1558ad6
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.33.target
@@ -0,0 +1,288 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?pde?>
+<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
+<target name="jgit-4.33" sequenceNumber="1740521283">
+ <locations>
+ <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+ <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
+ <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
+ <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
+ <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
+ <unit id="org.apache.ant" version="1.10.14.v20230922-1200"/>
+ <unit id="org.apache.ant.source" version="1.10.14.v20230922-1200"/>
+ <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
+ <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
+ <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
+ <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
+ <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
+ <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
+ <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
+ <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
+ <unit id="org.junit" version="4.13.2.v20230809-1000"/>
+ <unit id="org.junit.source" version="4.13.2.v20230809-1000"/>
+ <unit id="org.objenesis" version="3.4.0"/>
+ <unit id="org.objenesis.source" version="3.4.0"/>
+ <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
+ <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
+ <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2024-09"/>
+ </location>
+ <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+ <unit id="org.eclipse.osgi" version="0.0.0"/>
+ <repository location="https://download.eclipse.org/releases/2024-09/"/>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
+ <dependencies>
+ <dependency>
+ <groupId>org.tukaani</groupId>
+ <artifactId>xz</artifactId>
+ <version>1.10</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j">
+ <dependencies>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>1.7.36</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ <version>1.7.36</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd">
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-osgi</artifactId>
+ <version>2.15.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-sftp</artifactId>
+ <version>2.15.0</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito">
+ <dependencies>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <version>5.15.2</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna">
+ <dependencies>
+ <dependency>
+ <groupId>net.java.dev.jna</groupId>
+ <artifactId>jna</artifactId>
+ <version>5.16.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>net.java.dev.jna</groupId>
+ <artifactId>jna-platform</artifactId>
+ <version>5.16.0</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty">
+ <dependencies>
+ <dependency>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-servlet</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-http</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-io</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-security</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-server</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-session</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-util</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-util-ajax</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>jakarta.servlet</groupId>
+ <artifactId>jakarta.servlet-api</artifactId>
+ <version>6.1.0</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah">
+ <dependencies>
+ <dependency>
+ <groupId>com.googlecode.javaewah</groupId>
+ <artifactId>JavaEWAH</artifactId>
+ <version>1.2.3</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest">
+ <dependencies>
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest</artifactId>
+ <version>2.2</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson">
+ <dependencies>
+ <dependency>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ <version>2.12.1</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy">
+ <dependencies>
+ <dependency>
+ <groupId>net.bytebuddy</groupId>
+ <artifactId>byte-buddy</artifactId>
+ <version>1.17.1</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>net.bytebuddy</groupId>
+ <artifactId>byte-buddy-agent</artifactId>
+ <version>1.17.1</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle">
+ <dependencies>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpg-jdk18on</artifactId>
+ <version>1.80</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk18on</artifactId>
+ <version>1.80</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpkix-jdk18on</artifactId>
+ <version>1.80</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcutil-jdk18on</artifactId>
+ <version>1.80</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj">
+ <dependencies>
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <version>3.27.3</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j">
+ <dependencies>
+ <dependency>
+ <groupId>args4j</groupId>
+ <artifactId>args4j</artifactId>
+ <version>2.37</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache">
+ <dependencies>
+ <dependency>
+ <groupId>commons-codec</groupId>
+ <artifactId>commons-codec</artifactId>
+ <version>1.18.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-compress</artifactId>
+ <version>1.27.1</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ <version>3.17.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>2.18.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ <version>1.3.5</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ </locations>
+</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.33.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.33.tpd
new file mode 100644
index 0000000..74c6878
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.33.tpd
@@ -0,0 +1,8 @@
+target "jgit-4.33" with source configurePhase
+
+include "orbit/orbit-4.33.tpd"
+include "maven/dependencies.tpd"
+
+location "https://download.eclipse.org/releases/2024-09/" {
+ org.eclipse.osgi lazy
+}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.34.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.34.target
new file mode 100644
index 0000000..bc35d2c
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.34.target
@@ -0,0 +1,288 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?pde?>
+<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
+<target name="jgit-4.34" sequenceNumber="1740521284">
+ <locations>
+ <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+ <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
+ <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
+ <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
+ <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
+ <unit id="org.apache.ant" version="1.10.15.v20240901-1000"/>
+ <unit id="org.apache.ant.source" version="1.10.15.v20240901-1000"/>
+ <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
+ <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
+ <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
+ <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
+ <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
+ <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
+ <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
+ <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
+ <unit id="org.junit" version="4.13.2.v20240929-1000"/>
+ <unit id="org.junit.source" version="4.13.2.v20240929-1000"/>
+ <unit id="org.objenesis" version="3.4.0"/>
+ <unit id="org.objenesis.source" version="3.4.0"/>
+ <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
+ <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
+ <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2024-12"/>
+ </location>
+ <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+ <unit id="org.eclipse.osgi" version="0.0.0"/>
+ <repository location="https://download.eclipse.org/staging/2024-12/"/>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
+ <dependencies>
+ <dependency>
+ <groupId>org.tukaani</groupId>
+ <artifactId>xz</artifactId>
+ <version>1.10</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j">
+ <dependencies>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>1.7.36</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ <version>1.7.36</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd">
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-osgi</artifactId>
+ <version>2.15.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-sftp</artifactId>
+ <version>2.15.0</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito">
+ <dependencies>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <version>5.15.2</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna">
+ <dependencies>
+ <dependency>
+ <groupId>net.java.dev.jna</groupId>
+ <artifactId>jna</artifactId>
+ <version>5.16.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>net.java.dev.jna</groupId>
+ <artifactId>jna-platform</artifactId>
+ <version>5.16.0</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty">
+ <dependencies>
+ <dependency>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-servlet</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-http</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-io</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-security</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-server</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-session</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-util</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-util-ajax</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>jakarta.servlet</groupId>
+ <artifactId>jakarta.servlet-api</artifactId>
+ <version>6.1.0</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah">
+ <dependencies>
+ <dependency>
+ <groupId>com.googlecode.javaewah</groupId>
+ <artifactId>JavaEWAH</artifactId>
+ <version>1.2.3</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest">
+ <dependencies>
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest</artifactId>
+ <version>2.2</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson">
+ <dependencies>
+ <dependency>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ <version>2.12.1</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy">
+ <dependencies>
+ <dependency>
+ <groupId>net.bytebuddy</groupId>
+ <artifactId>byte-buddy</artifactId>
+ <version>1.17.1</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>net.bytebuddy</groupId>
+ <artifactId>byte-buddy-agent</artifactId>
+ <version>1.17.1</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle">
+ <dependencies>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpg-jdk18on</artifactId>
+ <version>1.80</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk18on</artifactId>
+ <version>1.80</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpkix-jdk18on</artifactId>
+ <version>1.80</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcutil-jdk18on</artifactId>
+ <version>1.80</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj">
+ <dependencies>
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <version>3.27.3</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j">
+ <dependencies>
+ <dependency>
+ <groupId>args4j</groupId>
+ <artifactId>args4j</artifactId>
+ <version>2.37</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache">
+ <dependencies>
+ <dependency>
+ <groupId>commons-codec</groupId>
+ <artifactId>commons-codec</artifactId>
+ <version>1.18.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-compress</artifactId>
+ <version>1.27.1</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ <version>3.17.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>2.18.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ <version>1.3.5</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ </locations>
+</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.34.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.34.tpd
new file mode 100644
index 0000000..4c38371
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.34.tpd
@@ -0,0 +1,8 @@
+target "jgit-4.34" with source configurePhase
+
+include "orbit/orbit-4.34.tpd"
+include "maven/dependencies.tpd"
+
+location "https://download.eclipse.org/staging/2024-12/" {
+ org.eclipse.osgi lazy
+}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.35.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.35.target
new file mode 100644
index 0000000..15cabc3
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.35.target
@@ -0,0 +1,288 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?pde?>
+<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
+<target name="jgit-4.35" sequenceNumber="1740521286">
+ <locations>
+ <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+ <unit id="com.jcraft.jsch" version="0.1.55.v20230916-1400"/>
+ <unit id="com.jcraft.jsch.source" version="0.1.55.v20230916-1400"/>
+ <unit id="com.jcraft.jzlib" version="1.1.3.v20230916-1400"/>
+ <unit id="com.jcraft.jzlib.source" version="1.1.3.v20230916-1400"/>
+ <unit id="org.apache.ant" version="1.10.15.v20240901-1000"/>
+ <unit id="org.apache.ant.source" version="1.10.15.v20240901-1000"/>
+ <unit id="org.apache.httpcomponents.httpclient" version="4.5.14"/>
+ <unit id="org.apache.httpcomponents.httpclient.source" version="4.5.14"/>
+ <unit id="org.apache.httpcomponents.httpcore" version="4.4.16"/>
+ <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.16"/>
+ <unit id="org.hamcrest.core" version="1.3.0.v20230809-1000"/>
+ <unit id="org.hamcrest.core.source" version="1.3.0.v20230809-1000"/>
+ <unit id="org.hamcrest.library" version="1.3.0.v20230809-1000"/>
+ <unit id="org.hamcrest.library.source" version="1.3.0.v20230809-1000"/>
+ <unit id="org.junit" version="4.13.2.v20240929-1000"/>
+ <unit id="org.junit.source" version="4.13.2.v20240929-1000"/>
+ <unit id="org.objenesis" version="3.4.0"/>
+ <unit id="org.objenesis.source" version="3.4.0"/>
+ <unit id="org.osgi.service.cm" version="1.6.1.202109301733"/>
+ <unit id="org.osgi.service.cm.source" version="1.6.1.202109301733"/>
+ <repository location="https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2025-03"/>
+ </location>
+ <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+ <unit id="org.eclipse.osgi" version="0.0.0"/>
+ <repository location="https://download.eclipse.org/staging/2025-03/"/>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="xz">
+ <dependencies>
+ <dependency>
+ <groupId>org.tukaani</groupId>
+ <artifactId>xz</artifactId>
+ <version>1.10</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="slf4j">
+ <dependencies>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>1.7.36</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ <version>1.7.36</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="sshd">
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-osgi</artifactId>
+ <version>2.15.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-sftp</artifactId>
+ <version>2.15.0</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="mockito">
+ <dependencies>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <version>5.15.2</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jna">
+ <dependencies>
+ <dependency>
+ <groupId>net.java.dev.jna</groupId>
+ <artifactId>jna</artifactId>
+ <version>5.16.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>net.java.dev.jna</groupId>
+ <artifactId>jna-platform</artifactId>
+ <version>5.16.0</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="jetty">
+ <dependencies>
+ <dependency>
+ <groupId>org.eclipse.jetty.ee10</groupId>
+ <artifactId>jetty-ee10-servlet</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-http</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-io</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-security</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-server</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-session</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-util</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-util-ajax</artifactId>
+ <version>12.0.16</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>jakarta.servlet</groupId>
+ <artifactId>jakarta.servlet-api</artifactId>
+ <version>6.1.0</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="javaewah">
+ <dependencies>
+ <dependency>
+ <groupId>com.googlecode.javaewah</groupId>
+ <artifactId>JavaEWAH</artifactId>
+ <version>1.2.3</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="hamcrest">
+ <dependencies>
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest</artifactId>
+ <version>2.2</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="gson">
+ <dependencies>
+ <dependency>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ <version>2.12.1</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bytebuddy">
+ <dependencies>
+ <dependency>
+ <groupId>net.bytebuddy</groupId>
+ <artifactId>byte-buddy</artifactId>
+ <version>1.17.1</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>net.bytebuddy</groupId>
+ <artifactId>byte-buddy-agent</artifactId>
+ <version>1.17.1</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="bouncycastle">
+ <dependencies>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpg-jdk18on</artifactId>
+ <version>1.80</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk18on</artifactId>
+ <version>1.80</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpkix-jdk18on</artifactId>
+ <version>1.80</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcutil-jdk18on</artifactId>
+ <version>1.80</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="assertj">
+ <dependencies>
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <version>3.27.3</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="args4j">
+ <dependencies>
+ <dependency>
+ <groupId>args4j</groupId>
+ <artifactId>args4j</artifactId>
+ <version>2.37</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ <location includeDependencyDepth="none" includeDependencyScopes="compile" includeSource="true" missingManifest="error" type="Maven" label="apache">
+ <dependencies>
+ <dependency>
+ <groupId>commons-codec</groupId>
+ <artifactId>commons-codec</artifactId>
+ <version>1.18.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-compress</artifactId>
+ <version>1.27.1</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ <version>3.17.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>2.18.0</version>
+ <type>jar</type>
+ </dependency>
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ <version>1.3.5</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+ </location>
+ </locations>
+</target>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.35.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.35.tpd
new file mode 100644
index 0000000..3c0646e
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.35.tpd
@@ -0,0 +1,8 @@
+target "jgit-4.35" with source configurePhase
+
+include "orbit/orbit-4.35.tpd"
+include "maven/dependencies.tpd"
+
+location "https://download.eclipse.org/staging/2025-03/" {
+ org.eclipse.osgi lazy
+}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/maven/dependencies.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/maven/dependencies.tpd
index a25f9c9..b292cf5 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/maven/dependencies.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/maven/dependencies.tpd
@@ -10,22 +10,27 @@
dependency {
groupId = "commons-codec"
artifactId = "commons-codec"
- version = "1.16.0"
+ version = "1.18.0"
}
dependency {
groupId = "org.apache.commons"
artifactId = "commons-compress"
- version = "1.26.0"
+ version = "1.27.1"
+ }
+ dependency {
+ groupId = "org.apache.commons"
+ artifactId = "commons-lang3"
+ version = "3.17.0"
}
dependency {
groupId = "commons-io"
artifactId = "commons-io"
- version = "2.15.1"
+ version = "2.18.0"
}
dependency {
groupId = "commons-logging"
artifactId = "commons-logging"
- version = "1.2"
+ version = "1.3.5"
}
}
@@ -38,7 +43,7 @@
dependency {
groupId = "args4j"
artifactId = "args4j"
- version = "2.33"
+ version = "2.37"
}
}
@@ -51,7 +56,7 @@
dependency {
groupId = "org.assertj"
artifactId = "assertj-core"
- version = "3.25.3"
+ version = "3.27.3"
}
}
@@ -64,22 +69,22 @@
dependency {
groupId = "org.bouncycastle"
artifactId = "bcpg-jdk18on"
- version = "1.77"
+ version = "1.80"
}
dependency {
groupId = "org.bouncycastle"
artifactId = "bcprov-jdk18on"
- version = "1.77"
+ version = "1.80"
}
dependency {
groupId = "org.bouncycastle"
artifactId = "bcpkix-jdk18on"
- version = "1.77"
+ version = "1.80"
}
dependency {
groupId = "org.bouncycastle"
artifactId = "bcutil-jdk18on"
- version = "1.77"
+ version = "1.80"
}
}
@@ -92,12 +97,12 @@
dependency {
groupId = "net.bytebuddy"
artifactId = "byte-buddy"
- version = "1.14.12"
+ version = "1.17.1"
}
dependency {
groupId = "net.bytebuddy"
artifactId = "byte-buddy-agent"
- version = "1.14.12"
+ version = "1.17.1"
}
}
@@ -110,7 +115,7 @@
dependency {
groupId = "com.google.code.gson"
artifactId = "gson"
- version = "2.10.1"
+ version = "2.12.1"
}
}
@@ -149,47 +154,47 @@
dependency {
groupId = "org.eclipse.jetty.ee10"
artifactId = "jetty-ee10-servlet"
- version = "12.0.9"
+ version = "12.0.16"
}
dependency {
groupId = "org.eclipse.jetty"
artifactId = "jetty-http"
- version = "12.0.9"
+ version = "12.0.16"
}
dependency {
groupId = "org.eclipse.jetty"
artifactId = "jetty-io"
- version = "12.0.9"
+ version = "12.0.16"
}
dependency {
groupId = "org.eclipse.jetty"
artifactId = "jetty-security"
- version = "12.0.9"
+ version = "12.0.16"
}
dependency {
groupId = "org.eclipse.jetty"
artifactId = "jetty-server"
- version = "12.0.9"
+ version = "12.0.16"
}
dependency {
groupId = "org.eclipse.jetty"
artifactId = "jetty-session"
- version = "12.0.9"
+ version = "12.0.16"
}
dependency {
groupId = "org.eclipse.jetty"
artifactId = "jetty-util"
- version = "12.0.9"
+ version = "12.0.16"
}
dependency {
groupId = "org.eclipse.jetty"
artifactId = "jetty-util-ajax"
- version = "12.0.9"
+ version = "12.0.16"
}
dependency {
groupId = "jakarta.servlet"
artifactId = "jakarta.servlet-api"
- version = "6.0.0"
+ version = "6.1.0"
}
}
@@ -202,12 +207,12 @@
dependency {
groupId = "net.java.dev.jna"
artifactId = "jna"
- version = "5.14.0"
+ version = "5.16.0"
}
dependency {
groupId = "net.java.dev.jna"
artifactId = "jna-platform"
- version = "5.14.0"
+ version = "5.16.0"
}
}
@@ -220,7 +225,7 @@
dependency {
groupId = "org.mockito"
artifactId = "mockito-core"
- version = "5.10.0"
+ version = "5.15.2"
}
}
@@ -233,12 +238,12 @@
dependency {
groupId = "org.apache.sshd"
artifactId = "sshd-osgi"
- version = "2.12.0"
+ version = "2.15.0"
}
dependency {
groupId = "org.apache.sshd"
artifactId = "sshd-sftp"
- version = "2.12.0"
+ version = "2.15.0"
}
}
@@ -269,6 +274,6 @@
dependency {
groupId = "org.tukaani"
artifactId = "xz"
- version = "1.9"
+ version = "1.10"
}
}
\ No newline at end of file
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20200831200620-2020-09.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20200831200620-2020-09.tpd
deleted file mode 100644
index 22e2b01..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20200831200620-2020-09.tpd
+++ /dev/null
@@ -1,66 +0,0 @@
-target "R20200831200620-2020-09" with source configurePhase
-// see https://download.eclipse.org/tools/orbit/downloads/
-
-location "https://download.eclipse.org/tools/orbit/downloads/drops/R20200831200620/repository" {
- com.google.gson [2.8.2.v20180104-1110,2.8.2.v20180104-1110]
- com.google.gson.source [2.8.2.v20180104-1110,2.8.2.v20180104-1110]
- com.jcraft.jsch [0.1.55.v20190404-1902,0.1.55.v20190404-1902]
- com.jcraft.jsch.source [0.1.55.v20190404-1902,0.1.55.v20190404-1902]
- com.jcraft.jzlib [1.1.1.v201205102305,1.1.1.v201205102305]
- com.jcraft.jzlib.source [1.1.1.v201205102305,1.1.1.v201205102305]
- javaewah [1.1.7.v20200107-0831,1.1.7.v20200107-0831]
- javaewah.source [1.1.7.v20200107-0831,1.1.7.v20200107-0831]
- javax.servlet [3.1.0.v201410161800,3.1.0.v201410161800]
- javax.servlet.source [3.1.0.v201410161800,3.1.0.v201410161800]
- net.bytebuddy.byte-buddy [1.9.0.v20181107-1410,1.9.0.v20181107-1410]
- net.bytebuddy.byte-buddy-agent [1.9.0.v20181106-1534,1.9.0.v20181106-1534]
- net.bytebuddy.byte-buddy-agent.source [1.9.0.v20181106-1534,1.9.0.v20181106-1534]
- net.bytebuddy.byte-buddy.source [1.9.0.v20181107-1410,1.9.0.v20181107-1410]
- net.i2p.crypto.eddsa [0.3.0.v20181102-1323,0.3.0.v20181102-1323]
- net.i2p.crypto.eddsa.source [0.3.0.v20181102-1323,0.3.0.v20181102-1323]
- org.apache.ant [1.10.8.v20200515-1239,1.10.8.v20200515-1239]
- org.apache.ant.source [1.10.8.v20200515-1239,1.10.8.v20200515-1239]
- org.apache.commons.codec [1.14.0.v20200818-1422,1.14.0.v20200818-1422]
- org.apache.commons.codec.source [1.14.0.v20200818-1422,1.14.0.v20200818-1422]
- org.apache.commons.compress [1.19.0.v20200106-2343,1.19.0.v20200106-2343]
- org.apache.commons.compress.source [1.19.0.v20200106-2343,1.19.0.v20200106-2343]
- org.apache.commons.logging [1.2.0.v20180409-1502,1.2.0.v20180409-1502]
- org.apache.commons.logging.source [1.2.0.v20180409-1502,1.2.0.v20180409-1502]
- org.apache.httpcomponents.httpclient [4.5.10.v20200830-2311,4.5.10.v20200830-2311]
- org.apache.httpcomponents.httpclient.source [4.5.10.v20200830-2311,4.5.10.v20200830-2311]
- org.apache.httpcomponents.httpcore [4.4.12.v20200108-1212,4.4.12.v20200108-1212]
- org.apache.httpcomponents.httpcore.source [4.4.12.v20200108-1212,4.4.12.v20200108-1212]
- org.apache.log4j [1.2.15.v201012070815,1.2.15.v201012070815]
- org.apache.log4j.source [1.2.15.v201012070815,1.2.15.v201012070815]
- org.apache.sshd.osgi [2.4.0.v20200318-1614,2.4.0.v20200318-1614]
- org.apache.sshd.osgi.source [2.4.0.v20200318-1614,2.4.0.v20200318-1614]
- org.apache.sshd.sftp [2.4.0.v20200319-1547,2.4.0.v20200319-1547]
- org.apache.sshd.sftp.source [2.4.0.v20200319-1547,2.4.0.v20200319-1547]
- org.assertj [3.14.0.v20200120-1926,3.14.0.v20200120-1926]
- org.assertj.source [3.14.0.v20200120-1926,3.14.0.v20200120-1926]
- org.bouncycastle.bcpg [1.65.0.v20200527-1955,1.65.0.v20200527-1955]
- org.bouncycastle.bcpg.source [1.65.0.v20200527-1955,1.65.0.v20200527-1955]
- org.bouncycastle.bcpkix [1.65.0.v20200527-1955,1.65.0.v20200527-1955]
- org.bouncycastle.bcpkix.source [1.65.0.v20200527-1955,1.65.0.v20200527-1955]
- org.bouncycastle.bcprov [1.65.1.v20200529-1514,1.65.1.v20200529-1514]
- org.bouncycastle.bcprov.source [1.65.1.v20200529-1514,1.65.1.v20200529-1514]
- org.hamcrest [1.1.0.v20090501071000,1.1.0.v20090501071000]
- org.hamcrest.core [1.3.0.v20180420-1519,1.3.0.v20180420-1519]
- org.hamcrest.core.source [1.3.0.v20180420-1519,1.3.0.v20180420-1519]
- org.hamcrest.library [1.3.0.v20180524-2246,1.3.0.v20180524-2246]
- org.hamcrest.library.source [1.3.0.v20180524-2246,1.3.0.v20180524-2246]
- org.junit [4.13.0.v20200204-1500,4.13.0.v20200204-1500]
- org.junit.source [4.13.0.v20200204-1500,4.13.0.v20200204-1500]
- org.kohsuke.args4j [2.33.0.v20160323-2218,2.33.0.v20160323-2218]
- org.kohsuke.args4j.source [2.33.0.v20160323-2218,2.33.0.v20160323-2218]
- org.mockito [2.23.0.v20200310-1642,2.23.0.v20200310-1642]
- org.mockito.source [2.23.0.v20200310-1642,2.23.0.v20200310-1642]
- org.objenesis [2.6.0.v20180420-1519,2.6.0.v20180420-1519]
- org.objenesis.source [2.6.0.v20180420-1519,2.6.0.v20180420-1519]
- org.slf4j.api [1.7.2.v20121108-1250,1.7.2.v20121108-1250]
- org.slf4j.api.source [1.7.2.v20121108-1250,1.7.2.v20121108-1250]
- org.slf4j.impl.log4j12 [1.7.2.v20131105-2200,1.7.2.v20131105-2200]
- org.slf4j.impl.log4j12.source [1.7.2.v20131105-2200,1.7.2.v20131105-2200]
- org.tukaani.xz [1.8.0.v20180207-1613,1.8.0.v20180207-1613]
- org.tukaani.xz.source [1.8.0.v20180207-1613,1.8.0.v20180207-1613]
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20201130205003-2020-12.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20201130205003-2020-12.tpd
deleted file mode 100644
index 08a0846..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20201130205003-2020-12.tpd
+++ /dev/null
@@ -1,66 +0,0 @@
-target "R20201130205003-2020-12" with source configurePhase
-// see https://download.eclipse.org/tools/orbit/downloads/
-
-location "https://download.eclipse.org/tools/orbit/downloads/drops/R20201130205003/repository" {
- com.google.gson [2.8.2.v20180104-1110,2.8.2.v20180104-1110]
- com.google.gson.source [2.8.2.v20180104-1110,2.8.2.v20180104-1110]
- com.jcraft.jsch [0.1.55.v20190404-1902,0.1.55.v20190404-1902]
- com.jcraft.jsch.source [0.1.55.v20190404-1902,0.1.55.v20190404-1902]
- com.jcraft.jzlib [1.1.1.v201205102305,1.1.1.v201205102305]
- com.jcraft.jzlib.source [1.1.1.v201205102305,1.1.1.v201205102305]
- javaewah [1.1.7.v20200107-0831,1.1.7.v20200107-0831]
- javaewah.source [1.1.7.v20200107-0831,1.1.7.v20200107-0831]
- javax.servlet [3.1.0.v201410161800,3.1.0.v201410161800]
- javax.servlet.source [3.1.0.v201410161800,3.1.0.v201410161800]
- net.bytebuddy.byte-buddy [1.9.0.v20181107-1410,1.9.0.v20181107-1410]
- net.bytebuddy.byte-buddy-agent [1.9.0.v20181106-1534,1.9.0.v20181106-1534]
- net.bytebuddy.byte-buddy-agent.source [1.9.0.v20181106-1534,1.9.0.v20181106-1534]
- net.bytebuddy.byte-buddy.source [1.9.0.v20181107-1410,1.9.0.v20181107-1410]
- net.i2p.crypto.eddsa [0.3.0.v20181102-1323,0.3.0.v20181102-1323]
- net.i2p.crypto.eddsa.source [0.3.0.v20181102-1323,0.3.0.v20181102-1323]
- org.apache.ant [1.10.9.v20201106-1946,1.10.9.v20201106-1946]
- org.apache.ant.source [1.10.9.v20201106-1946,1.10.9.v20201106-1946]
- org.apache.commons.codec [1.14.0.v20200818-1422,1.14.0.v20200818-1422]
- org.apache.commons.codec.source [1.14.0.v20200818-1422,1.14.0.v20200818-1422]
- org.apache.commons.compress [1.19.0.v20200106-2343,1.19.0.v20200106-2343]
- org.apache.commons.compress.source [1.19.0.v20200106-2343,1.19.0.v20200106-2343]
- org.apache.commons.logging [1.2.0.v20180409-1502,1.2.0.v20180409-1502]
- org.apache.commons.logging.source [1.2.0.v20180409-1502,1.2.0.v20180409-1502]
- org.apache.httpcomponents.httpclient [4.5.10.v20200830-2311,4.5.10.v20200830-2311]
- org.apache.httpcomponents.httpclient.source [4.5.10.v20200830-2311,4.5.10.v20200830-2311]
- org.apache.httpcomponents.httpcore [4.4.12.v20200108-1212,4.4.12.v20200108-1212]
- org.apache.httpcomponents.httpcore.source [4.4.12.v20200108-1212,4.4.12.v20200108-1212]
- org.apache.log4j [1.2.15.v201012070815,1.2.15.v201012070815]
- org.apache.log4j.source [1.2.15.v201012070815,1.2.15.v201012070815]
- org.apache.sshd.osgi [2.4.0.v20200318-1614,2.4.0.v20200318-1614]
- org.apache.sshd.osgi.source [2.4.0.v20200318-1614,2.4.0.v20200318-1614]
- org.apache.sshd.sftp [2.4.0.v20200319-1547,2.4.0.v20200319-1547]
- org.apache.sshd.sftp.source [2.4.0.v20200319-1547,2.4.0.v20200319-1547]
- org.assertj [3.14.0.v20200120-1926,3.14.0.v20200120-1926]
- org.assertj.source [3.14.0.v20200120-1926,3.14.0.v20200120-1926]
- org.bouncycastle.bcpg [1.65.0.v20200527-1955,1.65.0.v20200527-1955]
- org.bouncycastle.bcpg.source [1.65.0.v20200527-1955,1.65.0.v20200527-1955]
- org.bouncycastle.bcpkix [1.65.0.v20200527-1955,1.65.0.v20200527-1955]
- org.bouncycastle.bcpkix.source [1.65.0.v20200527-1955,1.65.0.v20200527-1955]
- org.bouncycastle.bcprov [1.65.1.v20200529-1514,1.65.1.v20200529-1514]
- org.bouncycastle.bcprov.source [1.65.1.v20200529-1514,1.65.1.v20200529-1514]
- org.hamcrest [1.1.0.v20090501071000,1.1.0.v20090501071000]
- org.hamcrest.core [1.3.0.v20180420-1519,1.3.0.v20180420-1519]
- org.hamcrest.core.source [1.3.0.v20180420-1519,1.3.0.v20180420-1519]
- org.hamcrest.library [1.3.0.v20180524-2246,1.3.0.v20180524-2246]
- org.hamcrest.library.source [1.3.0.v20180524-2246,1.3.0.v20180524-2246]
- org.junit [4.13.0.v20200204-1500,4.13.0.v20200204-1500]
- org.junit.source [4.13.0.v20200204-1500,4.13.0.v20200204-1500]
- org.kohsuke.args4j [2.33.0.v20160323-2218,2.33.0.v20160323-2218]
- org.kohsuke.args4j.source [2.33.0.v20160323-2218,2.33.0.v20160323-2218]
- org.mockito [2.23.0.v20200310-1642,2.23.0.v20200310-1642]
- org.mockito.source [2.23.0.v20200310-1642,2.23.0.v20200310-1642]
- org.objenesis [2.6.0.v20180420-1519,2.6.0.v20180420-1519]
- org.objenesis.source [2.6.0.v20180420-1519,2.6.0.v20180420-1519]
- org.slf4j.api [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
- org.slf4j.api.source [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
- org.slf4j.binding.log4j12 [1.7.30.v20201108-2042,1.7.30.v20201108-2042]
- org.slf4j.binding.log4j12.source [1.7.30.v20201108-2042,1.7.30.v20201108-2042]
- org.tukaani.xz [1.8.0.v20180207-1613,1.8.0.v20180207-1613]
- org.tukaani.xz.source [1.8.0.v20180207-1613,1.8.0.v20180207-1613]
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20210223232630-2021-03.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20210223232630-2021-03.tpd
deleted file mode 100644
index 605a43b..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20210223232630-2021-03.tpd
+++ /dev/null
@@ -1,66 +0,0 @@
-target "R20210223232630-2021-03" with source configurePhase
-// see https://download.eclipse.org/tools/orbit/downloads/
-
-location "https://download.eclipse.org/tools/orbit/downloads/drops/R20210223232630/repository" {
- com.google.gson [2.8.6.v20201231-1626,2.8.6.v20201231-1626]
- com.google.gson.source [2.8.6.v20201231-1626,2.8.6.v20201231-1626]
- com.jcraft.jsch [0.1.55.v20190404-1902,0.1.55.v20190404-1902]
- com.jcraft.jsch.source [0.1.55.v20190404-1902,0.1.55.v20190404-1902]
- com.jcraft.jzlib [1.1.1.v201205102305,1.1.1.v201205102305]
- com.jcraft.jzlib.source [1.1.1.v201205102305,1.1.1.v201205102305]
- javaewah [1.1.7.v20200107-0831,1.1.7.v20200107-0831]
- javaewah.source [1.1.7.v20200107-0831,1.1.7.v20200107-0831]
- javax.servlet [3.1.0.v201410161800,3.1.0.v201410161800]
- javax.servlet.source [3.1.0.v201410161800,3.1.0.v201410161800]
- net.bytebuddy.byte-buddy [1.9.0.v20181107-1410,1.9.0.v20181107-1410]
- net.bytebuddy.byte-buddy-agent [1.9.0.v20181106-1534,1.9.0.v20181106-1534]
- net.bytebuddy.byte-buddy-agent.source [1.9.0.v20181106-1534,1.9.0.v20181106-1534]
- net.bytebuddy.byte-buddy.source [1.9.0.v20181107-1410,1.9.0.v20181107-1410]
- net.i2p.crypto.eddsa [0.3.0.v20181102-1323,0.3.0.v20181102-1323]
- net.i2p.crypto.eddsa.source [0.3.0.v20181102-1323,0.3.0.v20181102-1323]
- org.apache.ant [1.10.9.v20201106-1946,1.10.9.v20201106-1946]
- org.apache.ant.source [1.10.9.v20201106-1946,1.10.9.v20201106-1946]
- org.apache.commons.codec [1.14.0.v20200818-1422,1.14.0.v20200818-1422]
- org.apache.commons.codec.source [1.14.0.v20200818-1422,1.14.0.v20200818-1422]
- org.apache.commons.compress [1.19.0.v20200106-2343,1.19.0.v20200106-2343]
- org.apache.commons.compress.source [1.19.0.v20200106-2343,1.19.0.v20200106-2343]
- org.apache.commons.logging [1.2.0.v20180409-1502,1.2.0.v20180409-1502]
- org.apache.commons.logging.source [1.2.0.v20180409-1502,1.2.0.v20180409-1502]
- org.apache.httpcomponents.httpclient [4.5.13.v20210128-2225,4.5.13.v20210128-2225]
- org.apache.httpcomponents.httpclient.source [4.5.13.v20210128-2225,4.5.13.v20210128-2225]
- org.apache.httpcomponents.httpcore [4.4.14.v20210128-2225,4.4.14.v20210128-2225]
- org.apache.httpcomponents.httpcore.source [4.4.14.v20210128-2225,4.4.14.v20210128-2225]
- org.apache.log4j [1.2.15.v201012070815,1.2.15.v201012070815]
- org.apache.log4j.source [1.2.15.v201012070815,1.2.15.v201012070815]
- org.apache.sshd.osgi [2.6.0.v20210201-2003,2.6.0.v20210201-2003]
- org.apache.sshd.osgi.source [2.6.0.v20210201-2003,2.6.0.v20210201-2003]
- org.apache.sshd.sftp [2.6.0.v20210201-2003,2.6.0.v20210201-2003]
- org.apache.sshd.sftp.source [2.6.0.v20210201-2003,2.6.0.v20210201-2003]
- org.assertj [3.14.0.v20200120-1926,3.14.0.v20200120-1926]
- org.assertj.source [3.14.0.v20200120-1926,3.14.0.v20200120-1926]
- org.bouncycastle.bcpg [1.65.0.v20200527-1955,1.65.0.v20200527-1955]
- org.bouncycastle.bcpg.source [1.65.0.v20200527-1955,1.65.0.v20200527-1955]
- org.bouncycastle.bcpkix [1.65.0.v20200527-1955,1.65.0.v20200527-1955]
- org.bouncycastle.bcpkix.source [1.65.0.v20200527-1955,1.65.0.v20200527-1955]
- org.bouncycastle.bcprov [1.65.1.v20200529-1514,1.65.1.v20200529-1514]
- org.bouncycastle.bcprov.source [1.65.1.v20200529-1514,1.65.1.v20200529-1514]
- org.hamcrest [1.1.0.v20090501071000,1.1.0.v20090501071000]
- org.hamcrest.core [1.3.0.v20180420-1519,1.3.0.v20180420-1519]
- org.hamcrest.core.source [1.3.0.v20180420-1519,1.3.0.v20180420-1519]
- org.hamcrest.library [1.3.0.v20180524-2246,1.3.0.v20180524-2246]
- org.hamcrest.library.source [1.3.0.v20180524-2246,1.3.0.v20180524-2246]
- org.junit [4.13.0.v20200204-1500,4.13.0.v20200204-1500]
- org.junit.source [4.13.0.v20200204-1500,4.13.0.v20200204-1500]
- org.kohsuke.args4j [2.33.0.v20160323-2218,2.33.0.v20160323-2218]
- org.kohsuke.args4j.source [2.33.0.v20160323-2218,2.33.0.v20160323-2218]
- org.mockito [2.23.0.v20200310-1642,2.23.0.v20200310-1642]
- org.mockito.source [2.23.0.v20200310-1642,2.23.0.v20200310-1642]
- org.objenesis [2.6.0.v20180420-1519,2.6.0.v20180420-1519]
- org.objenesis.source [2.6.0.v20180420-1519,2.6.0.v20180420-1519]
- org.slf4j.api [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
- org.slf4j.api.source [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
- org.slf4j.binding.log4j12 [1.7.30.v20201108-2042,1.7.30.v20201108-2042]
- org.slf4j.binding.log4j12.source [1.7.30.v20201108-2042,1.7.30.v20201108-2042]
- org.tukaani.xz [1.8.0.v20180207-1613,1.8.0.v20180207-1613]
- org.tukaani.xz.source [1.8.0.v20180207-1613,1.8.0.v20180207-1613]
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20210602031627-2021-06.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20210602031627-2021-06.tpd
deleted file mode 100644
index 83b5bb3..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20210602031627-2021-06.tpd
+++ /dev/null
@@ -1,66 +0,0 @@
-target "R20210602031627-2021-06" with source configurePhase
-// see https://download.eclipse.org/tools/orbit/downloads/
-
-location "https://download.eclipse.org/tools/orbit/downloads/drops/R20210602031627/repository" {
- com.google.gson [2.8.6.v20201231-1626,2.8.6.v20201231-1626]
- com.google.gson.source [2.8.6.v20201231-1626,2.8.6.v20201231-1626]
- com.jcraft.jsch [0.1.55.v20190404-1902,0.1.55.v20190404-1902]
- com.jcraft.jsch.source [0.1.55.v20190404-1902,0.1.55.v20190404-1902]
- com.jcraft.jzlib [1.1.1.v201205102305,1.1.1.v201205102305]
- com.jcraft.jzlib.source [1.1.1.v201205102305,1.1.1.v201205102305]
- javaewah [1.1.7.v20200107-0831,1.1.7.v20200107-0831]
- javaewah.source [1.1.7.v20200107-0831,1.1.7.v20200107-0831]
- javax.servlet [3.1.0.v201410161800,3.1.0.v201410161800]
- javax.servlet.source [3.1.0.v201410161800,3.1.0.v201410161800]
- net.bytebuddy.byte-buddy [1.9.0.v20181107-1410,1.9.0.v20181107-1410]
- net.bytebuddy.byte-buddy-agent [1.9.0.v20181106-1534,1.9.0.v20181106-1534]
- net.bytebuddy.byte-buddy-agent.source [1.9.0.v20181106-1534,1.9.0.v20181106-1534]
- net.bytebuddy.byte-buddy.source [1.9.0.v20181107-1410,1.9.0.v20181107-1410]
- net.i2p.crypto.eddsa [0.3.0.v20181102-1323,0.3.0.v20181102-1323]
- net.i2p.crypto.eddsa.source [0.3.0.v20181102-1323,0.3.0.v20181102-1323]
- org.apache.ant [1.10.10.v20210426-1926,1.10.10.v20210426-1926]
- org.apache.ant.source [1.10.10.v20210426-1926,1.10.10.v20210426-1926]
- org.apache.commons.codec [1.14.0.v20200818-1422,1.14.0.v20200818-1422]
- org.apache.commons.codec.source [1.14.0.v20200818-1422,1.14.0.v20200818-1422]
- org.apache.commons.compress [1.19.0.v20200106-2343,1.19.0.v20200106-2343]
- org.apache.commons.compress.source [1.19.0.v20200106-2343,1.19.0.v20200106-2343]
- org.apache.commons.logging [1.2.0.v20180409-1502,1.2.0.v20180409-1502]
- org.apache.commons.logging.source [1.2.0.v20180409-1502,1.2.0.v20180409-1502]
- org.apache.httpcomponents.httpclient [4.5.13.v20210128-2225,4.5.13.v20210128-2225]
- org.apache.httpcomponents.httpclient.source [4.5.13.v20210128-2225,4.5.13.v20210128-2225]
- org.apache.httpcomponents.httpcore [4.4.14.v20210128-2225,4.4.14.v20210128-2225]
- org.apache.httpcomponents.httpcore.source [4.4.14.v20210128-2225,4.4.14.v20210128-2225]
- org.apache.log4j [1.2.15.v201012070815,1.2.15.v201012070815]
- org.apache.log4j.source [1.2.15.v201012070815,1.2.15.v201012070815]
- org.apache.sshd.osgi [2.6.0.v20210201-2003,2.6.0.v20210201-2003]
- org.apache.sshd.osgi.source [2.6.0.v20210201-2003,2.6.0.v20210201-2003]
- org.apache.sshd.sftp [2.6.0.v20210201-2003,2.6.0.v20210201-2003]
- org.apache.sshd.sftp.source [2.6.0.v20210201-2003,2.6.0.v20210201-2003]
- org.assertj [3.14.0.v20200120-1926,3.14.0.v20200120-1926]
- org.assertj.source [3.14.0.v20200120-1926,3.14.0.v20200120-1926]
- org.bouncycastle.bcpg [1.65.0.v20200527-1955,1.65.0.v20200527-1955]
- org.bouncycastle.bcpg.source [1.65.0.v20200527-1955,1.65.0.v20200527-1955]
- org.bouncycastle.bcpkix [1.65.0.v20200527-1955,1.65.0.v20200527-1955]
- org.bouncycastle.bcpkix.source [1.65.0.v20200527-1955,1.65.0.v20200527-1955]
- org.bouncycastle.bcprov [1.65.1.v20200529-1514,1.65.1.v20200529-1514]
- org.bouncycastle.bcprov.source [1.65.1.v20200529-1514,1.65.1.v20200529-1514]
- org.hamcrest [1.1.0.v20090501071000,1.1.0.v20090501071000]
- org.hamcrest.core [1.3.0.v20180420-1519,1.3.0.v20180420-1519]
- org.hamcrest.core.source [1.3.0.v20180420-1519,1.3.0.v20180420-1519]
- org.hamcrest.library [1.3.0.v20180524-2246,1.3.0.v20180524-2246]
- org.hamcrest.library.source [1.3.0.v20180524-2246,1.3.0.v20180524-2246]
- org.junit [4.13.0.v20200204-1500,4.13.0.v20200204-1500]
- org.junit.source [4.13.0.v20200204-1500,4.13.0.v20200204-1500]
- org.kohsuke.args4j [2.33.0.v20160323-2218,2.33.0.v20160323-2218]
- org.kohsuke.args4j.source [2.33.0.v20160323-2218,2.33.0.v20160323-2218]
- org.mockito [2.23.0.v20200310-1642,2.23.0.v20200310-1642]
- org.mockito.source [2.23.0.v20200310-1642,2.23.0.v20200310-1642]
- org.objenesis [2.6.0.v20180420-1519,2.6.0.v20180420-1519]
- org.objenesis.source [2.6.0.v20180420-1519,2.6.0.v20180420-1519]
- org.slf4j.api [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
- org.slf4j.api.source [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
- org.slf4j.binding.log4j12 [1.7.30.v20201108-2042,1.7.30.v20201108-2042]
- org.slf4j.binding.log4j12.source [1.7.30.v20201108-2042,1.7.30.v20201108-2042]
- org.tukaani.xz [1.8.0.v20180207-1613,1.8.0.v20180207-1613]
- org.tukaani.xz.source [1.8.0.v20180207-1613,1.8.0.v20180207-1613]
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20210825222808-2021-09.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20210825222808-2021-09.tpd
deleted file mode 100644
index 99f3520..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20210825222808-2021-09.tpd
+++ /dev/null
@@ -1,73 +0,0 @@
-target "R20210825222808-2021-09" with source configurePhase
-// see https://download.eclipse.org/tools/orbit/downloads/
-
-location "https://download.eclipse.org/tools/orbit/downloads/drops/R20210825222808/repository" {
- com.google.gson [2.8.7.v20210624-1215,2.8.7.v20210624-1215]
- com.google.gson.source [2.8.7.v20210624-1215,2.8.7.v20210624-1215]
- com.jcraft.jsch [0.1.55.v20190404-1902,0.1.55.v20190404-1902]
- com.jcraft.jsch.source [0.1.55.v20190404-1902,0.1.55.v20190404-1902]
- com.jcraft.jzlib [1.1.1.v201205102305,1.1.1.v201205102305]
- com.jcraft.jzlib.source [1.1.1.v201205102305,1.1.1.v201205102305]
- com.sun.jna [5.8.0.v20210503-0343,5.8.0.v20210503-0343]
- com.sun.jna.source [5.8.0.v20210503-0343,5.8.0.v20210503-0343]
- com.sun.jna.platform [5.8.0.v20210406-1004,5.8.0.v20210406-1004]
- com.sun.jna.platform.source [5.8.0.v20210406-1004,5.8.0.v20210406-1004]
- javaewah [1.1.12.v20210622-2206,1.1.12.v20210622-2206]
- javaewah.source [1.1.12.v20210622-2206,1.1.12.v20210622-2206]
- javax.servlet [3.1.0.v201410161800,3.1.0.v201410161800]
- javax.servlet.source [3.1.0.v201410161800,3.1.0.v201410161800]
- net.bytebuddy.byte-buddy [1.9.0.v20181107-1410,1.9.0.v20181107-1410]
- net.bytebuddy.byte-buddy-agent [1.9.0.v20181106-1534,1.9.0.v20181106-1534]
- net.bytebuddy.byte-buddy-agent.source [1.9.0.v20181106-1534,1.9.0.v20181106-1534]
- net.bytebuddy.byte-buddy.source [1.9.0.v20181107-1410,1.9.0.v20181107-1410]
- net.i2p.crypto.eddsa [0.3.0.v20181102-1323,0.3.0.v20181102-1323]
- net.i2p.crypto.eddsa.source [0.3.0.v20181102-1323,0.3.0.v20181102-1323]
- org.apache.ant [1.10.11.v20210720-1445,1.10.11.v20210720-1445]
- org.apache.ant.source [1.10.11.v20210720-1445,1.10.11.v20210720-1445]
- org.apache.commons.codec [1.14.0.v20200818-1422,1.14.0.v20200818-1422]
- org.apache.commons.codec.source [1.14.0.v20200818-1422,1.14.0.v20200818-1422]
- org.apache.commons.compress [1.20.0.v20210713-1928,1.20.0.v20210713-1928]
- org.apache.commons.compress.source [1.20.0.v20210713-1928,1.20.0.v20210713-1928]
- org.apache.commons.logging [1.2.0.v20180409-1502,1.2.0.v20180409-1502]
- org.apache.commons.logging.source [1.2.0.v20180409-1502,1.2.0.v20180409-1502]
- org.apache.httpcomponents.httpclient [4.5.13.v20210128-2225,4.5.13.v20210128-2225]
- org.apache.httpcomponents.httpclient.source [4.5.13.v20210128-2225,4.5.13.v20210128-2225]
- org.apache.httpcomponents.httpcore [4.4.14.v20210128-2225,4.4.14.v20210128-2225]
- org.apache.httpcomponents.httpcore.source [4.4.14.v20210128-2225,4.4.14.v20210128-2225]
- org.apache.log4j [1.2.15.v201012070815,1.2.15.v201012070815]
- org.apache.log4j.source [1.2.15.v201012070815,1.2.15.v201012070815]
- org.apache.sshd.osgi [2.7.0.v20210623-0618,2.7.0.v20210623-0618]
- org.apache.sshd.osgi.source [2.7.0.v20210623-0618,2.7.0.v20210623-0618]
- org.apache.sshd.sftp [2.7.0.v20210623-0618,2.7.0.v20210623-0618]
- org.apache.sshd.sftp.source [2.7.0.v20210623-0618,2.7.0.v20210623-0618]
- org.assertj [3.20.2.v20210706-1104,3.20.2.v20210706-1104]
- org.assertj.source [3.20.2.v20210706-1104,3.20.2.v20210706-1104]
- org.bouncycastle.bcpg [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.bouncycastle.bcpg.source [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.bouncycastle.bcpkix [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.bouncycastle.bcpkix.source [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.bouncycastle.bcprov [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.bouncycastle.bcprov.source [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.bouncycastle.bcutil [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.bouncycastle.bcutil.source [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.hamcrest [2.2.0.v20210711-0821,2.2.0.v20210711-0821]
- org.hamcrest.source [2.2.0.v20210711-0821,2.2.0.v20210711-0821]
- org.hamcrest.core [1.3.0.v20180420-1519,1.3.0.v20180420-1519]
- org.hamcrest.core.source [1.3.0.v20180420-1519,1.3.0.v20180420-1519]
- org.hamcrest.library [1.3.0.v20180524-2246,1.3.0.v20180524-2246]
- org.hamcrest.library.source [1.3.0.v20180524-2246,1.3.0.v20180524-2246]
- org.junit [4.13.0.v20200204-1500,4.13.0.v20200204-1500]
- org.junit.source [4.13.0.v20200204-1500,4.13.0.v20200204-1500]
- org.kohsuke.args4j [2.33.0.v20160323-2218,2.33.0.v20160323-2218]
- org.kohsuke.args4j.source [2.33.0.v20160323-2218,2.33.0.v20160323-2218]
- org.mockito [2.23.0.v20200310-1642,2.23.0.v20200310-1642]
- org.mockito.source [2.23.0.v20200310-1642,2.23.0.v20200310-1642]
- org.objenesis [2.6.0.v20180420-1519,2.6.0.v20180420-1519]
- org.objenesis.source [2.6.0.v20180420-1519,2.6.0.v20180420-1519]
- org.slf4j.api [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
- org.slf4j.api.source [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
- org.slf4j.binding.log4j12 [1.7.30.v20201108-2042,1.7.30.v20201108-2042]
- org.slf4j.binding.log4j12.source [1.7.30.v20201108-2042,1.7.30.v20201108-2042]
- org.tukaani.xz [1.9.0.v20210624-1259,1.9.0.v20210624-1259]
- org.tukaani.xz.source [1.9.0.v20210624-1259,1.9.0.v20210624-1259]
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20211122181901-2021-12.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20211122181901-2021-12.tpd
deleted file mode 100644
index cd1d1c0..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20211122181901-2021-12.tpd
+++ /dev/null
@@ -1,71 +0,0 @@
-target "R20211122181901-2021-12" with source configurePhase
-// see https://download.eclipse.org/tools/orbit/downloads/
-
-location "https://download.eclipse.org/tools/orbit/downloads/drops/R20211122181901/repository" {
- com.google.gson [2.8.8.v20211029-0838,2.8.8.v20211029-0838]
- com.google.gson.source [2.8.8.v20211029-0838,2.8.8.v20211029-0838]
- com.jcraft.jsch [0.1.55.v20190404-1902,0.1.55.v20190404-1902]
- com.jcraft.jsch.source [0.1.55.v20190404-1902,0.1.55.v20190404-1902]
- com.jcraft.jzlib [1.1.1.v201205102305,1.1.1.v201205102305]
- com.jcraft.jzlib.source [1.1.1.v201205102305,1.1.1.v201205102305]
- com.sun.jna [5.8.0.v20210503-0343,5.8.0.v20210503-0343]
- com.sun.jna.source [5.8.0.v20210503-0343,5.8.0.v20210503-0343]
- com.sun.jna.platform [5.8.0.v20210406-1004,5.8.0.v20210406-1004]
- com.sun.jna.platform.source [5.8.0.v20210406-1004,5.8.0.v20210406-1004]
- javaewah [1.1.13.v20211029-0839,1.1.13.v20211029-0839]
- javaewah.source [1.1.13.v20211029-0839,1.1.13.v20211029-0839]
- net.bytebuddy.byte-buddy [1.9.0.v20181107-1410,1.9.0.v20181107-1410]
- net.bytebuddy.byte-buddy-agent [1.9.0.v20181106-1534,1.9.0.v20181106-1534]
- net.bytebuddy.byte-buddy-agent.source [1.9.0.v20181106-1534,1.9.0.v20181106-1534]
- net.bytebuddy.byte-buddy.source [1.9.0.v20181107-1410,1.9.0.v20181107-1410]
- net.i2p.crypto.eddsa [0.3.0.v20210923-1401,0.3.0.v20210923-1401]
- net.i2p.crypto.eddsa.source [0.3.0.v20210923-1401,0.3.0.v20210923-1401]
- org.apache.ant [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
- org.apache.ant.source [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
- org.apache.commons.codec [1.14.0.v20200818-1422,1.14.0.v20200818-1422]
- org.apache.commons.codec.source [1.14.0.v20200818-1422,1.14.0.v20200818-1422]
- org.apache.commons.compress [1.21.0.v20211103-2100,1.21.0.v20211103-2100]
- org.apache.commons.compress.source [1.21.0.v20211103-2100,1.21.0.v20211103-2100]
- org.apache.commons.logging [1.2.0.v20180409-1502,1.2.0.v20180409-1502]
- org.apache.commons.logging.source [1.2.0.v20180409-1502,1.2.0.v20180409-1502]
- org.apache.httpcomponents.httpclient [4.5.13.v20210128-2225,4.5.13.v20210128-2225]
- org.apache.httpcomponents.httpclient.source [4.5.13.v20210128-2225,4.5.13.v20210128-2225]
- org.apache.httpcomponents.httpcore [4.4.14.v20210128-2225,4.4.14.v20210128-2225]
- org.apache.httpcomponents.httpcore.source [4.4.14.v20210128-2225,4.4.14.v20210128-2225]
- org.apache.log4j [1.2.15.v201012070815,1.2.15.v201012070815]
- org.apache.log4j.source [1.2.15.v201012070815,1.2.15.v201012070815]
- org.apache.sshd.osgi [2.7.0.v20210623-0618,2.7.0.v20210623-0618]
- org.apache.sshd.osgi.source [2.7.0.v20210623-0618,2.7.0.v20210623-0618]
- org.apache.sshd.sftp [2.7.0.v20210623-0618,2.7.0.v20210623-0618]
- org.apache.sshd.sftp.source [2.7.0.v20210623-0618,2.7.0.v20210623-0618]
- org.assertj [3.20.2.v20210706-1104,3.20.2.v20210706-1104]
- org.assertj.source [3.20.2.v20210706-1104,3.20.2.v20210706-1104]
- org.bouncycastle.bcpg [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.bouncycastle.bcpg.source [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.bouncycastle.bcpkix [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.bouncycastle.bcpkix.source [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.bouncycastle.bcprov [1.69.0.v20210923-1401,1.69.0.v20210923-1401]
- org.bouncycastle.bcprov.source [1.69.0.v20210923-1401,1.69.0.v20210923-1401]
- org.bouncycastle.bcutil [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.bouncycastle.bcutil.source [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.hamcrest [2.2.0.v20210711-0821,2.2.0.v20210711-0821]
- org.hamcrest.source [2.2.0.v20210711-0821,2.2.0.v20210711-0821]
- org.hamcrest.core [1.3.0.v20180420-1519,1.3.0.v20180420-1519]
- org.hamcrest.core.source [1.3.0.v20180420-1519,1.3.0.v20180420-1519]
- org.hamcrest.library [1.3.0.v20180524-2246,1.3.0.v20180524-2246]
- org.hamcrest.library.source [1.3.0.v20180524-2246,1.3.0.v20180524-2246]
- org.junit [4.13.2.v20211018-1956,4.13.2.v20211018-1956]
- org.junit.source [4.13.2.v20211018-1956,4.13.2.v20211018-1956]
- org.kohsuke.args4j [2.33.0.v20160323-2218,2.33.0.v20160323-2218]
- org.kohsuke.args4j.source [2.33.0.v20160323-2218,2.33.0.v20160323-2218]
- org.mockito [2.23.0.v20200310-1642,2.23.0.v20200310-1642]
- org.mockito.source [2.23.0.v20200310-1642,2.23.0.v20200310-1642]
- org.objenesis [2.6.0.v20180420-1519,2.6.0.v20180420-1519]
- org.objenesis.source [2.6.0.v20180420-1519,2.6.0.v20180420-1519]
- org.slf4j.api [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
- org.slf4j.api.source [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
- org.slf4j.binding.log4j12 [1.7.30.v20201108-2042,1.7.30.v20201108-2042]
- org.slf4j.binding.log4j12.source [1.7.30.v20201108-2042,1.7.30.v20201108-2042]
- org.tukaani.xz [1.9.0.v20210624-1259,1.9.0.v20210624-1259]
- org.tukaani.xz.source [1.9.0.v20210624-1259,1.9.0.v20210624-1259]
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20211213173813-2021-12.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20211213173813-2021-12.tpd
deleted file mode 100644
index 0c7c846..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20211213173813-2021-12.tpd
+++ /dev/null
@@ -1,69 +0,0 @@
-target "R20211213173813-2021-12" with source configurePhase
-// see https://download.eclipse.org/tools/orbit/downloads/
-
-location "https://download.eclipse.org/tools/orbit/downloads/drops/R20211213173813/repository" {
- com.google.gson [2.8.8.v20211029-0838,2.8.8.v20211029-0838]
- com.google.gson.source [2.8.8.v20211029-0838,2.8.8.v20211029-0838]
- com.jcraft.jsch [0.1.55.v20190404-1902,0.1.55.v20190404-1902]
- com.jcraft.jsch.source [0.1.55.v20190404-1902,0.1.55.v20190404-1902]
- com.jcraft.jzlib [1.1.1.v201205102305,1.1.1.v201205102305]
- com.jcraft.jzlib.source [1.1.1.v201205102305,1.1.1.v201205102305]
- com.sun.jna [5.8.0.v20210503-0343,5.8.0.v20210503-0343]
- com.sun.jna.source [5.8.0.v20210503-0343,5.8.0.v20210503-0343]
- com.sun.jna.platform [5.8.0.v20210406-1004,5.8.0.v20210406-1004]
- com.sun.jna.platform.source [5.8.0.v20210406-1004,5.8.0.v20210406-1004]
- javaewah [1.1.13.v20211029-0839,1.1.13.v20211029-0839]
- javaewah.source [1.1.13.v20211029-0839,1.1.13.v20211029-0839]
- net.bytebuddy.byte-buddy [1.9.0.v20181107-1410,1.9.0.v20181107-1410]
- net.bytebuddy.byte-buddy-agent [1.9.0.v20181106-1534,1.9.0.v20181106-1534]
- net.bytebuddy.byte-buddy-agent.source [1.9.0.v20181106-1534,1.9.0.v20181106-1534]
- net.bytebuddy.byte-buddy.source [1.9.0.v20181107-1410,1.9.0.v20181107-1410]
- net.i2p.crypto.eddsa [0.3.0.v20210923-1401,0.3.0.v20210923-1401]
- net.i2p.crypto.eddsa.source [0.3.0.v20210923-1401,0.3.0.v20210923-1401]
- org.apache.ant [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
- org.apache.ant.source [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
- org.apache.commons.codec [1.14.0.v20200818-1422,1.14.0.v20200818-1422]
- org.apache.commons.codec.source [1.14.0.v20200818-1422,1.14.0.v20200818-1422]
- org.apache.commons.compress [1.21.0.v20211103-2100,1.21.0.v20211103-2100]
- org.apache.commons.compress.source [1.21.0.v20211103-2100,1.21.0.v20211103-2100]
- org.apache.commons.logging [1.2.0.v20180409-1502,1.2.0.v20180409-1502]
- org.apache.commons.logging.source [1.2.0.v20180409-1502,1.2.0.v20180409-1502]
- org.apache.httpcomponents.httpclient [4.5.13.v20210128-2225,4.5.13.v20210128-2225]
- org.apache.httpcomponents.httpclient.source [4.5.13.v20210128-2225,4.5.13.v20210128-2225]
- org.apache.httpcomponents.httpcore [4.4.14.v20210128-2225,4.4.14.v20210128-2225]
- org.apache.httpcomponents.httpcore.source [4.4.14.v20210128-2225,4.4.14.v20210128-2225]
- org.apache.sshd.osgi [2.7.0.v20210623-0618,2.7.0.v20210623-0618]
- org.apache.sshd.osgi.source [2.7.0.v20210623-0618,2.7.0.v20210623-0618]
- org.apache.sshd.sftp [2.7.0.v20210623-0618,2.7.0.v20210623-0618]
- org.apache.sshd.sftp.source [2.7.0.v20210623-0618,2.7.0.v20210623-0618]
- org.assertj [3.20.2.v20210706-1104,3.20.2.v20210706-1104]
- org.assertj.source [3.20.2.v20210706-1104,3.20.2.v20210706-1104]
- org.bouncycastle.bcpg [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.bouncycastle.bcpg.source [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.bouncycastle.bcpkix [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.bouncycastle.bcpkix.source [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.bouncycastle.bcprov [1.69.0.v20210923-1401,1.69.0.v20210923-1401]
- org.bouncycastle.bcprov.source [1.69.0.v20210923-1401,1.69.0.v20210923-1401]
- org.bouncycastle.bcutil [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.bouncycastle.bcutil.source [1.69.0.v20210713-1924,1.69.0.v20210713-1924]
- org.hamcrest [2.2.0.v20210711-0821,2.2.0.v20210711-0821]
- org.hamcrest.source [2.2.0.v20210711-0821,2.2.0.v20210711-0821]
- org.hamcrest.core [1.3.0.v20180420-1519,1.3.0.v20180420-1519]
- org.hamcrest.core.source [1.3.0.v20180420-1519,1.3.0.v20180420-1519]
- org.hamcrest.library [1.3.0.v20180524-2246,1.3.0.v20180524-2246]
- org.hamcrest.library.source [1.3.0.v20180524-2246,1.3.0.v20180524-2246]
- org.junit [4.13.2.v20211018-1956,4.13.2.v20211018-1956]
- org.junit.source [4.13.2.v20211018-1956,4.13.2.v20211018-1956]
- org.kohsuke.args4j [2.33.0.v20160323-2218,2.33.0.v20160323-2218]
- org.kohsuke.args4j.source [2.33.0.v20160323-2218,2.33.0.v20160323-2218]
- org.mockito [2.23.0.v20200310-1642,2.23.0.v20200310-1642]
- org.mockito.source [2.23.0.v20200310-1642,2.23.0.v20200310-1642]
- org.objenesis [2.6.0.v20180420-1519,2.6.0.v20180420-1519]
- org.objenesis.source [2.6.0.v20180420-1519,2.6.0.v20180420-1519]
- org.slf4j.api [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
- org.slf4j.api.source [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
- org.slf4j.binding.simple [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
- org.slf4j.binding.simple.source [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
- org.tukaani.xz [1.9.0.v20210624-1259,1.9.0.v20210624-1259]
- org.tukaani.xz.source [1.9.0.v20210624-1259,1.9.0.v20210624-1259]
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20220302172233-2022-03.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20220302172233-2022-03.tpd
deleted file mode 100644
index fafc689..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20220302172233-2022-03.tpd
+++ /dev/null
@@ -1,69 +0,0 @@
-target "R20220302172233" with source configurePhase
-// see https://download.eclipse.org/tools/orbit/downloads/
-
-location "https://download.eclipse.org/tools/orbit/downloads/drops/R20220302172233/repository" {
- com.google.gson [2.8.9.v20220111-1409,2.8.9.v20220111-1409]
- com.google.gson.source [2.8.9.v20220111-1409,2.8.9.v20220111-1409]
- com.jcraft.jsch [0.1.55.v20190404-1902,0.1.55.v20190404-1902]
- com.jcraft.jsch.source [0.1.55.v20190404-1902,0.1.55.v20190404-1902]
- com.jcraft.jzlib [1.1.1.v201205102305,1.1.1.v201205102305]
- com.jcraft.jzlib.source [1.1.1.v201205102305,1.1.1.v201205102305]
- com.sun.jna [5.8.0.v20210503-0343,5.8.0.v20210503-0343]
- com.sun.jna.source [5.8.0.v20210503-0343,5.8.0.v20210503-0343]
- com.sun.jna.platform [5.8.0.v20210406-1004,5.8.0.v20210406-1004]
- com.sun.jna.platform.source [5.8.0.v20210406-1004,5.8.0.v20210406-1004]
- javaewah [1.1.13.v20211029-0839,1.1.13.v20211029-0839]
- javaewah.source [1.1.13.v20211029-0839,1.1.13.v20211029-0839]
- net.bytebuddy.byte-buddy [1.9.0.v20181107-1410,1.9.0.v20181107-1410]
- net.bytebuddy.byte-buddy-agent [1.9.0.v20181106-1534,1.9.0.v20181106-1534]
- net.bytebuddy.byte-buddy-agent.source [1.9.0.v20181106-1534,1.9.0.v20181106-1534]
- net.bytebuddy.byte-buddy.source [1.9.0.v20181107-1410,1.9.0.v20181107-1410]
- net.i2p.crypto.eddsa [0.3.0.v20210923-1401,0.3.0.v20210923-1401]
- net.i2p.crypto.eddsa.source [0.3.0.v20210923-1401,0.3.0.v20210923-1401]
- org.apache.ant [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
- org.apache.ant.source [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
- org.apache.commons.codec [1.14.0.v20200818-1422,1.14.0.v20200818-1422]
- org.apache.commons.codec.source [1.14.0.v20200818-1422,1.14.0.v20200818-1422]
- org.apache.commons.compress [1.21.0.v20211103-2100,1.21.0.v20211103-2100]
- org.apache.commons.compress.source [1.21.0.v20211103-2100,1.21.0.v20211103-2100]
- org.apache.commons.logging [1.2.0.v20180409-1502,1.2.0.v20180409-1502]
- org.apache.commons.logging.source [1.2.0.v20180409-1502,1.2.0.v20180409-1502]
- org.apache.httpcomponents.httpclient [4.5.13.v20210128-2225,4.5.13.v20210128-2225]
- org.apache.httpcomponents.httpclient.source [4.5.13.v20210128-2225,4.5.13.v20210128-2225]
- org.apache.httpcomponents.httpcore [4.4.15.v20220209-2345,4.4.15.v20220209-2345]
- org.apache.httpcomponents.httpcore.source [4.4.15.v20220209-2345,4.4.15.v20220209-2345]
- org.apache.sshd.osgi [2.8.0.v20211227-1750,2.8.0.v20211227-1750]
- org.apache.sshd.osgi.source [2.8.0.v20211227-1750,2.8.0.v20211227-1750]
- org.apache.sshd.sftp [2.8.0.v20211227-1750,2.8.0.v20211227-1750]
- org.apache.sshd.sftp.source [2.8.0.v20211227-1750,2.8.0.v20211227-1750]
- org.assertj [3.20.2.v20210706-1104,3.20.2.v20210706-1104]
- org.assertj.source [3.20.2.v20210706-1104,3.20.2.v20210706-1104]
- org.bouncycastle.bcpg [1.70.0.v20220105-1522,1.70.0.v20220105-1522]
- org.bouncycastle.bcpg.source [1.70.0.v20220105-1522,1.70.0.v20220105-1522]
- org.bouncycastle.bcpkix [1.70.0.v20220105-1522,1.70.0.v20220105-1522]
- org.bouncycastle.bcpkix.source [1.70.0.v20220105-1522,1.70.0.v20220105-1522]
- org.bouncycastle.bcprov [1.70.0.v20220105-1522,1.70.0.v20220105-1522]
- org.bouncycastle.bcprov.source [1.70.0.v20220105-1522,1.70.0.v20220105-1522]
- org.bouncycastle.bcutil [1.70.0.v20220105-1522,1.70.0.v20220105-1522]
- org.bouncycastle.bcutil.source [1.70.0.v20220105-1522,1.70.0.v20220105-1522]
- org.hamcrest [2.2.0.v20210711-0821,2.2.0.v20210711-0821]
- org.hamcrest.source [2.2.0.v20210711-0821,2.2.0.v20210711-0821]
- org.hamcrest.core [1.3.0.v20180420-1519,1.3.0.v20180420-1519]
- org.hamcrest.core.source [1.3.0.v20180420-1519,1.3.0.v20180420-1519]
- org.hamcrest.library [1.3.0.v20180524-2246,1.3.0.v20180524-2246]
- org.hamcrest.library.source [1.3.0.v20180524-2246,1.3.0.v20180524-2246]
- org.junit [4.13.2.v20211018-1956,4.13.2.v20211018-1956]
- org.junit.source [4.13.2.v20211018-1956,4.13.2.v20211018-1956]
- org.kohsuke.args4j [2.33.0.v20160323-2218,2.33.0.v20160323-2218]
- org.kohsuke.args4j.source [2.33.0.v20160323-2218,2.33.0.v20160323-2218]
- org.mockito [2.23.0.v20200310-1642,2.23.0.v20200310-1642]
- org.mockito.source [2.23.0.v20200310-1642,2.23.0.v20200310-1642]
- org.objenesis [2.6.0.v20180420-1519,2.6.0.v20180420-1519]
- org.objenesis.source [2.6.0.v20180420-1519,2.6.0.v20180420-1519]
- org.slf4j.api [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
- org.slf4j.api.source [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
- org.slf4j.binding.simple [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
- org.slf4j.binding.simple.source [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
- org.tukaani.xz [1.9.0.v20210624-1259,1.9.0.v20210624-1259]
- org.tukaani.xz.source [1.9.0.v20210624-1259,1.9.0.v20210624-1259]
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20220531185310-2022-06.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20220531185310-2022-06.tpd
deleted file mode 100644
index 3c74497..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20220531185310-2022-06.tpd
+++ /dev/null
@@ -1,69 +0,0 @@
-target "R20220531185310-2022-06" with source configurePhase
-// see https://download.eclipse.org/tools/orbit/downloads/
-
-location "https://download.eclipse.org/tools/orbit/downloads/drops/R20220531185310/repository" {
- com.google.gson [2.8.9.v20220111-1409,2.8.9.v20220111-1409]
- com.google.gson.source [2.8.9.v20220111-1409,2.8.9.v20220111-1409]
- com.jcraft.jsch [0.1.55.v20190404-1902,0.1.55.v20190404-1902]
- com.jcraft.jsch.source [0.1.55.v20190404-1902,0.1.55.v20190404-1902]
- com.jcraft.jzlib [1.1.3.v20220502-1820,1.1.3.v20220502-1820]
- com.jcraft.jzlib.source [1.1.3.v20220502-1820,1.1.3.v20220502-1820]
- com.sun.jna [5.8.0.v20210503-0343,5.8.0.v20210503-0343]
- com.sun.jna.source [5.8.0.v20210503-0343,5.8.0.v20210503-0343]
- com.sun.jna.platform [5.8.0.v20210406-1004,5.8.0.v20210406-1004]
- com.sun.jna.platform.source [5.8.0.v20210406-1004,5.8.0.v20210406-1004]
- javaewah [1.1.13.v20211029-0839,1.1.13.v20211029-0839]
- javaewah.source [1.1.13.v20211029-0839,1.1.13.v20211029-0839]
- net.bytebuddy.byte-buddy [1.9.0.v20181107-1410,1.9.0.v20181107-1410]
- net.bytebuddy.byte-buddy-agent [1.9.0.v20181106-1534,1.9.0.v20181106-1534]
- net.bytebuddy.byte-buddy-agent.source [1.9.0.v20181106-1534,1.9.0.v20181106-1534]
- net.bytebuddy.byte-buddy.source [1.9.0.v20181107-1410,1.9.0.v20181107-1410]
- net.i2p.crypto.eddsa [0.3.0.v20220506-1020,0.3.0.v20220506-1020]
- net.i2p.crypto.eddsa.source [0.3.0.v20220506-1020,0.3.0.v20220506-1020]
- org.apache.ant [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
- org.apache.ant.source [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
- org.apache.commons.codec [1.14.0.v20200818-1422,1.14.0.v20200818-1422]
- org.apache.commons.codec.source [1.14.0.v20200818-1422,1.14.0.v20200818-1422]
- org.apache.commons.compress [1.21.0.v20211103-2100,1.21.0.v20211103-2100]
- org.apache.commons.compress.source [1.21.0.v20211103-2100,1.21.0.v20211103-2100]
- org.apache.commons.logging [1.2.0.v20180409-1502,1.2.0.v20180409-1502]
- org.apache.commons.logging.source [1.2.0.v20180409-1502,1.2.0.v20180409-1502]
- org.apache.httpcomponents.httpclient [4.5.13.v20210128-2225,4.5.13.v20210128-2225]
- org.apache.httpcomponents.httpclient.source [4.5.13.v20210128-2225,4.5.13.v20210128-2225]
- org.apache.httpcomponents.httpcore [4.4.15.v20220209-2345,4.4.15.v20220209-2345]
- org.apache.httpcomponents.httpcore.source [4.4.15.v20220209-2345,4.4.15.v20220209-2345]
- org.apache.sshd.osgi [2.8.0.v20211227-1750,2.8.0.v20211227-1750]
- org.apache.sshd.osgi.source [2.8.0.v20211227-1750,2.8.0.v20211227-1750]
- org.apache.sshd.sftp [2.8.0.v20211227-1750,2.8.0.v20211227-1750]
- org.apache.sshd.sftp.source [2.8.0.v20211227-1750,2.8.0.v20211227-1750]
- org.assertj [3.20.2.v20210706-1104,3.20.2.v20210706-1104]
- org.assertj.source [3.20.2.v20210706-1104,3.20.2.v20210706-1104]
- org.bouncycastle.bcpg [1.70.0.v20220507-1208,1.70.0.v20220507-1208]
- org.bouncycastle.bcpg.source [1.70.0.v20220507-1208,1.70.0.v20220507-1208]
- org.bouncycastle.bcpkix [1.70.0.v20220105-1522,1.70.0.v20220105-1522]
- org.bouncycastle.bcpkix.source [1.70.0.v20220105-1522,1.70.0.v20220105-1522]
- org.bouncycastle.bcprov [1.70.0.v20220507-1208,1.70.0.v20220507-1208]
- org.bouncycastle.bcprov.source [1.70.0.v20220507-1208,1.70.0.v20220507-1208]
- org.bouncycastle.bcutil [1.70.0.v20220105-1522,1.70.0.v20220105-1522]
- org.bouncycastle.bcutil.source [1.70.0.v20220105-1522,1.70.0.v20220105-1522]
- org.hamcrest [2.2.0.v20210711-0821,2.2.0.v20210711-0821]
- org.hamcrest.source [2.2.0.v20210711-0821,2.2.0.v20210711-0821]
- org.hamcrest.core [1.3.0.v20180420-1519,1.3.0.v20180420-1519]
- org.hamcrest.core.source [1.3.0.v20180420-1519,1.3.0.v20180420-1519]
- org.hamcrest.library [1.3.0.v20180524-2246,1.3.0.v20180524-2246]
- org.hamcrest.library.source [1.3.0.v20180524-2246,1.3.0.v20180524-2246]
- org.junit [4.13.2.v20211018-1956,4.13.2.v20211018-1956]
- org.junit.source [4.13.2.v20211018-1956,4.13.2.v20211018-1956]
- org.kohsuke.args4j [2.33.0.v20160323-2218,2.33.0.v20160323-2218]
- org.kohsuke.args4j.source [2.33.0.v20160323-2218,2.33.0.v20160323-2218]
- org.mockito [2.23.0.v20200310-1642,2.23.0.v20200310-1642]
- org.mockito.source [2.23.0.v20200310-1642,2.23.0.v20200310-1642]
- org.objenesis [2.6.0.v20180420-1519,2.6.0.v20180420-1519]
- org.objenesis.source [2.6.0.v20180420-1519,2.6.0.v20180420-1519]
- org.slf4j.api [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
- org.slf4j.api.source [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
- org.slf4j.binding.simple [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
- org.slf4j.binding.simple.source [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
- org.tukaani.xz [1.9.0.v20210624-1259,1.9.0.v20210624-1259]
- org.tukaani.xz.source [1.9.0.v20210624-1259,1.9.0.v20210624-1259]
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20220830213456-2022-09.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20220830213456-2022-09.tpd
deleted file mode 100644
index 8db1018..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20220830213456-2022-09.tpd
+++ /dev/null
@@ -1,69 +0,0 @@
-target "R20220830213456-2022-09" with source configurePhase
-// see https://download.eclipse.org/tools/orbit/downloads/
-
-location "https://download.eclipse.org/tools/orbit/downloads/drops/R20220830213456/repository" {
- com.google.gson [2.8.9.v20220111-1409,2.8.9.v20220111-1409]
- com.google.gson.source [2.8.9.v20220111-1409,2.8.9.v20220111-1409]
- com.jcraft.jsch [0.1.55.v20190404-1902,0.1.55.v20190404-1902]
- com.jcraft.jsch.source [0.1.55.v20190404-1902,0.1.55.v20190404-1902]
- com.jcraft.jzlib [1.1.3.v20220502-1820,1.1.3.v20220502-1820]
- com.jcraft.jzlib.source [1.1.3.v20220502-1820,1.1.3.v20220502-1820]
- com.sun.jna [5.8.0.v20210503-0343,5.8.0.v20210503-0343]
- com.sun.jna.source [5.8.0.v20210503-0343,5.8.0.v20210503-0343]
- com.sun.jna.platform [5.8.0.v20210406-1004,5.8.0.v20210406-1004]
- com.sun.jna.platform.source [5.8.0.v20210406-1004,5.8.0.v20210406-1004]
- javaewah [1.1.13.v20211029-0839,1.1.13.v20211029-0839]
- javaewah.source [1.1.13.v20211029-0839,1.1.13.v20211029-0839]
- net.bytebuddy.byte-buddy [1.9.0.v20181107-1410,1.9.0.v20181107-1410]
- net.bytebuddy.byte-buddy-agent [1.9.0.v20181106-1534,1.9.0.v20181106-1534]
- net.bytebuddy.byte-buddy-agent.source [1.9.0.v20181106-1534,1.9.0.v20181106-1534]
- net.bytebuddy.byte-buddy.source [1.9.0.v20181107-1410,1.9.0.v20181107-1410]
- net.i2p.crypto.eddsa [0.3.0.v20220506-1020,0.3.0.v20220506-1020]
- net.i2p.crypto.eddsa.source [0.3.0.v20220506-1020,0.3.0.v20220506-1020]
- org.apache.ant [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
- org.apache.ant.source [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
- org.apache.commons.codec [1.14.0.v20200818-1422,1.14.0.v20200818-1422]
- org.apache.commons.codec.source [1.14.0.v20200818-1422,1.14.0.v20200818-1422]
- org.apache.commons.compress [1.21.0.v20211103-2100,1.21.0.v20211103-2100]
- org.apache.commons.compress.source [1.21.0.v20211103-2100,1.21.0.v20211103-2100]
- org.apache.commons.logging [1.2.0.v20180409-1502,1.2.0.v20180409-1502]
- org.apache.commons.logging.source [1.2.0.v20180409-1502,1.2.0.v20180409-1502]
- org.apache.httpcomponents.httpclient [4.5.13.v20210128-2225,4.5.13.v20210128-2225]
- org.apache.httpcomponents.httpclient.source [4.5.13.v20210128-2225,4.5.13.v20210128-2225]
- org.apache.httpcomponents.httpcore [4.4.15.v20220209-2345,4.4.15.v20220209-2345]
- org.apache.httpcomponents.httpcore.source [4.4.15.v20220209-2345,4.4.15.v20220209-2345]
- org.apache.sshd.osgi [2.8.0.v20211227-1750,2.8.0.v20211227-1750]
- org.apache.sshd.osgi.source [2.8.0.v20211227-1750,2.8.0.v20211227-1750]
- org.apache.sshd.sftp [2.8.0.v20211227-1750,2.8.0.v20211227-1750]
- org.apache.sshd.sftp.source [2.8.0.v20211227-1750,2.8.0.v20211227-1750]
- org.assertj [3.20.2.v20210706-1104,3.20.2.v20210706-1104]
- org.assertj.source [3.20.2.v20210706-1104,3.20.2.v20210706-1104]
- org.bouncycastle.bcpg [1.71.0.v20220723-1943,1.71.0.v20220723-1943]
- org.bouncycastle.bcpg.source [1.71.0.v20220723-1943,1.71.0.v20220723-1943]
- org.bouncycastle.bcpkix [1.71.0.v20220723-1943,1.71.0.v20220723-1943]
- org.bouncycastle.bcpkix.source [1.71.0.v20220723-1943,1.71.0.v20220723-1943]
- org.bouncycastle.bcprov [1.71.0.v20220723-1943,1.71.0.v20220723-1943]
- org.bouncycastle.bcprov.source [1.71.0.v20220723-1943,1.71.0.v20220723-1943]
- org.bouncycastle.bcutil [1.71.0.v20220723-1943,1.71.0.v20220723-1943]
- org.bouncycastle.bcutil.source [1.71.0.v20220723-1943,1.71.0.v20220723-1943]
- org.hamcrest [2.2.0.v20210711-0821,2.2.0.v20210711-0821]
- org.hamcrest.source [2.2.0.v20210711-0821,2.2.0.v20210711-0821]
- org.hamcrest.core [1.3.0.v20180420-1519,1.3.0.v20180420-1519]
- org.hamcrest.core.source [1.3.0.v20180420-1519,1.3.0.v20180420-1519]
- org.hamcrest.library [1.3.0.v20180524-2246,1.3.0.v20180524-2246]
- org.hamcrest.library.source [1.3.0.v20180524-2246,1.3.0.v20180524-2246]
- org.junit [4.13.2.v20211018-1956,4.13.2.v20211018-1956]
- org.junit.source [4.13.2.v20211018-1956,4.13.2.v20211018-1956]
- org.kohsuke.args4j [2.33.0.v20160323-2218,2.33.0.v20160323-2218]
- org.kohsuke.args4j.source [2.33.0.v20160323-2218,2.33.0.v20160323-2218]
- org.mockito [2.23.0.v20200310-1642,2.23.0.v20200310-1642]
- org.mockito.source [2.23.0.v20200310-1642,2.23.0.v20200310-1642]
- org.objenesis [2.6.0.v20180420-1519,2.6.0.v20180420-1519]
- org.objenesis.source [2.6.0.v20180420-1519,2.6.0.v20180420-1519]
- org.slf4j.api [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
- org.slf4j.api.source [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
- org.slf4j.binding.simple [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
- org.slf4j.binding.simple.source [1.7.30.v20200204-2150,1.7.30.v20200204-2150]
- org.tukaani.xz [1.9.0.v20210624-1259,1.9.0.v20210624-1259]
- org.tukaani.xz.source [1.9.0.v20210624-1259,1.9.0.v20210624-1259]
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20221123021534-2022-12.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20221123021534-2022-12.tpd
deleted file mode 100644
index 378b848..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20221123021534-2022-12.tpd
+++ /dev/null
@@ -1,69 +0,0 @@
-target "S20230101190934" with source configurePhase
-// see https://download.eclipse.org/tools/orbit/downloads/
-
-location "https://download.eclipse.org/tools/orbit/downloads/drops/R20221123021534/repository" {
- com.google.gson [2.9.1.v20220915-1632,2.9.1.v20220915-1632]
- com.google.gson.source [2.9.1.v20220915-1632,2.9.1.v20220915-1632]
- com.jcraft.jsch [0.1.55.v20221112-0806,0.1.55.v20221112-0806]
- com.jcraft.jsch.source [0.1.55.v20221112-0806,0.1.55.v20221112-0806]
- com.jcraft.jzlib [1.1.3.v20220502-1820,1.1.3.v20220502-1820]
- com.jcraft.jzlib.source [1.1.3.v20220502-1820,1.1.3.v20220502-1820]
- com.sun.jna [5.12.1.v20221103-2317,5.12.1.v20221103-2317]
- com.sun.jna.source [5.12.1.v20221103-2317,5.12.1.v20221103-2317]
- com.sun.jna.platform [5.12.1.v20221103-2317,5.12.1.v20221103-2317]
- com.sun.jna.platform.source [5.12.1.v20221103-2317,5.12.1.v20221103-2317]
- javaewah [1.1.13.v20211029-0839,1.1.13.v20211029-0839]
- javaewah.source [1.1.13.v20211029-0839,1.1.13.v20211029-0839]
- net.bytebuddy.byte-buddy [1.12.18.v20221114-2102,1.12.18.v20221114-2102]
- net.bytebuddy.byte-buddy.source [1.12.18.v20221114-2102,1.12.18.v20221114-2102]
- net.bytebuddy.byte-buddy-agent [1.12.18.v20221114-2102,1.12.18.v20221114-2102]
- net.bytebuddy.byte-buddy-agent.source [1.12.18.v20221114-2102,1.12.18.v20221114-2102]
- net.i2p.crypto.eddsa [0.3.0.v20220506-1020,0.3.0.v20220506-1020]
- net.i2p.crypto.eddsa.source [0.3.0.v20220506-1020,0.3.0.v20220506-1020]
- org.apache.ant [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
- org.apache.ant.source [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
- org.apache.commons.codec [1.14.0.v20221112-0806,1.14.0.v20221112-0806]
- org.apache.commons.codec.source [1.14.0.v20221112-0806,1.14.0.v20221112-0806]
- org.apache.commons.compress [1.21.0.v20211103-2100,1.21.0.v20211103-2100]
- org.apache.commons.compress.source [1.21.0.v20211103-2100,1.21.0.v20211103-2100]
- org.apache.commons.logging [1.2.0.v20180409-1502,1.2.0.v20180409-1502]
- org.apache.commons.logging.source [1.2.0.v20180409-1502,1.2.0.v20180409-1502]
- org.apache.httpcomponents.httpclient [4.5.13.v20221112-0806,4.5.13.v20221112-0806]
- org.apache.httpcomponents.httpclient.source [4.5.13.v20221112-0806,4.5.13.v20221112-0806]
- org.apache.httpcomponents.httpcore [4.4.15.v20220209-2345,4.4.15.v20220209-2345]
- org.apache.httpcomponents.httpcore.source [4.4.15.v20220209-2345,4.4.15.v20220209-2345]
- org.apache.sshd.osgi [2.9.2.v20221117-1942,2.9.2.v20221117-1942]
- org.apache.sshd.osgi.source [2.9.2.v20221117-1942,2.9.2.v20221117-1942]
- org.apache.sshd.sftp [2.9.2.v20221117-1942,2.9.2.v20221117-1942]
- org.apache.sshd.sftp.source [2.9.2.v20221117-1942,2.9.2.v20221117-1942]
- org.assertj [3.20.2.v20210706-1104,3.20.2.v20210706-1104]
- org.assertj.source [3.20.2.v20210706-1104,3.20.2.v20210706-1104]
- org.bouncycastle.bcpg [1.72.0.v20221013-1810,1.72.0.v20221013-1810]
- org.bouncycastle.bcpg.source [1.72.0.v20221013-1810,1.72.0.v20221013-1810]
- org.bouncycastle.bcpkix [1.72.0.v20221013-1810,1.72.0.v20221013-1810]
- org.bouncycastle.bcpkix.source [1.72.0.v20221013-1810,1.72.0.v20221013-1810]
- org.bouncycastle.bcprov [1.72.0.v20221013-1810,1.72.0.v20221013-1810]
- org.bouncycastle.bcprov.source [1.72.0.v20221013-1810,1.72.0.v20221013-1810]
- org.bouncycastle.bcutil [1.72.0.v20221013-1810,1.72.0.v20221013-1810]
- org.bouncycastle.bcutil.source [1.72.0.v20221013-1810,1.72.0.v20221013-1810]
- org.hamcrest [2.2.0.v20210711-0821,2.2.0.v20210711-0821]
- org.hamcrest.source [2.2.0.v20210711-0821,2.2.0.v20210711-0821]
- org.hamcrest.core [1.3.0.v20180420-1519,1.3.0.v20180420-1519]
- org.hamcrest.core.source [1.3.0.v20180420-1519,1.3.0.v20180420-1519]
- org.hamcrest.library [1.3.0.v20180524-2246,1.3.0.v20180524-2246]
- org.hamcrest.library.source [1.3.0.v20180524-2246,1.3.0.v20180524-2246]
- org.junit [4.13.2.v20211018-1956,4.13.2.v20211018-1956]
- org.junit.source [4.13.2.v20211018-1956,4.13.2.v20211018-1956]
- org.kohsuke.args4j [2.33.0.v20160323-2218,2.33.0.v20160323-2218]
- org.kohsuke.args4j.source [2.33.0.v20160323-2218,2.33.0.v20160323-2218]
- org.mockito.mockito-core [4.8.1.v20221103-2317,4.8.1.v20221103-2317]
- org.mockito.mockito-core.source [4.8.1.v20221103-2317,4.8.1.v20221103-2317]
- org.objenesis [3.3.0.v20221103-2317,3.3.0.v20221103-2317]
- org.objenesis.source [3.3.0.v20221103-2317,3.3.0.v20221103-2317]
- org.slf4j.api [1.7.30.v20221112-0806,1.7.30.v20221112-0806]
- org.slf4j.api.source [1.7.30.v20221112-0806,1.7.30.v20221112-0806]
- org.slf4j.binding.simple [1.7.30.v20221112-0806,1.7.30.v20221112-0806]
- org.slf4j.binding.simple.source [1.7.30.v20221112-0806,1.7.30.v20221112-0806]
- org.tukaani.xz [1.9.0.v20210624-1259,1.9.0.v20210624-1259]
- org.tukaani.xz.source [1.9.0.v20210624-1259,1.9.0.v20210624-1259]
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20230302014618-2023-03.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20230302014618-2023-03.tpd
deleted file mode 100644
index 8578b2c..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20230302014618-2023-03.tpd
+++ /dev/null
@@ -1,27 +0,0 @@
-target "R20230302014618-2023-03" with source configurePhase
-// see https://download.eclipse.org/tools/orbit/downloads/
-
-location "https://download.eclipse.org/tools/orbit/downloads/drops/R20230302014618/repository" {
- com.jcraft.jsch [0.1.55.v20221112-0806,0.1.55.v20221112-0806]
- com.jcraft.jsch.source [0.1.55.v20221112-0806,0.1.55.v20221112-0806]
- com.jcraft.jzlib [1.1.3.v20220502-1820,1.1.3.v20220502-1820]
- com.jcraft.jzlib.source [1.1.3.v20220502-1820,1.1.3.v20220502-1820]
- net.i2p.crypto.eddsa [0.3.0.v20220506-1020,0.3.0.v20220506-1020]
- net.i2p.crypto.eddsa.source [0.3.0.v20220506-1020,0.3.0.v20220506-1020]
- org.apache.ant [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
- org.apache.ant.source [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
- org.apache.httpcomponents.httpclient [4.5.14.v20221207-1049,4.5.14.v20221207-1049]
- org.apache.httpcomponents.httpclient.source [4.5.14.v20221207-1049,4.5.14.v20221207-1049]
- org.apache.httpcomponents.httpcore [4.4.16.v20221207-1049,4.4.16.v20221207-1049]
- org.apache.httpcomponents.httpcore.source [4.4.16.v20221207-1049,4.4.16.v20221207-1049]
- org.hamcrest.core [1.3.0.v20180420-1519,1.3.0.v20180420-1519]
- org.hamcrest.core.source [1.3.0.v20180420-1519,1.3.0.v20180420-1519]
- org.hamcrest.library [1.3.0.v20180524-2246,1.3.0.v20180524-2246]
- org.hamcrest.library.source [1.3.0.v20180524-2246,1.3.0.v20180524-2246]
- org.junit [4.13.2.v20211018-1956,4.13.2.v20211018-1956]
- org.junit.source [4.13.2.v20211018-1956,4.13.2.v20211018-1956]
- org.mockito.mockito-core [4.8.1.v20221103-2317,4.8.1.v20221103-2317]
- org.mockito.mockito-core.source [4.8.1.v20221103-2317,4.8.1.v20221103-2317]
- org.objenesis [3.3.0.v20221103-2317,3.3.0.v20221103-2317]
- org.objenesis.source [3.3.0.v20221103-2317,3.3.0.v20221103-2317]
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20230531010532-2023-06.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20230531010532-2023-06.tpd
deleted file mode 100644
index 46055d3..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20230531010532-2023-06.tpd
+++ /dev/null
@@ -1,25 +0,0 @@
-target "R20230531010532-2023-06" with source configurePhase
-// see https://download.eclipse.org/tools/orbit/downloads/
-
-location "https://download.eclipse.org/tools/orbit/downloads/drops/R20230531010532/repository" {
- com.jcraft.jsch [0.1.55.v20221112-0806,0.1.55.v20221112-0806]
- com.jcraft.jsch.source [0.1.55.v20221112-0806,0.1.55.v20221112-0806]
- com.jcraft.jzlib [1.1.3.v20220502-1820,1.1.3.v20220502-1820]
- com.jcraft.jzlib.source [1.1.3.v20220502-1820,1.1.3.v20220502-1820]
- net.i2p.crypto.eddsa [0.3.0.v20220506-1020,0.3.0.v20220506-1020]
- net.i2p.crypto.eddsa.source [0.3.0.v20220506-1020,0.3.0.v20220506-1020]
- org.apache.ant [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
- org.apache.ant.source [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
- org.apache.httpcomponents.httpclient [4.5.14.v20230516-1249,4.5.14.v20230516-1249]
- org.apache.httpcomponents.httpclient.source [4.5.14.v20230516-1249,4.5.14.v20230516-1249]
- org.apache.httpcomponents.httpcore [4.4.16.v20221207-1049,4.4.16.v20221207-1049]
- org.apache.httpcomponents.httpcore.source [4.4.16.v20221207-1049,4.4.16.v20221207-1049]
- org.hamcrest.core [1.3.0.v20180420-1519,1.3.0.v20180420-1519]
- org.hamcrest.core.source [1.3.0.v20180420-1519,1.3.0.v20180420-1519]
- org.hamcrest.library [1.3.0.v20180524-2246,1.3.0.v20180524-2246]
- org.hamcrest.library.source [1.3.0.v20180524-2246,1.3.0.v20180524-2246]
- org.junit [4.13.2.v20211018-1956,4.13.2.v20211018-1956]
- org.junit.source [4.13.2.v20211018-1956,4.13.2.v20211018-1956]
- org.objenesis [3.3.0.v20221103-2317,3.3.0.v20221103-2317]
- org.objenesis.source [3.3.0.v20221103-2317,3.3.0.v20221103-2317]
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.29.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.29.tpd
deleted file mode 100644
index 70a17a1..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.29.tpd
+++ /dev/null
@@ -1,27 +0,0 @@
-target "orbit-4.29" with source configurePhase
-// see https://download.eclipse.org/tools/orbit/downloads/
-
-location "https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/release/4.29.0" {
- com.jcraft.jsch [0.1.55.v20221112-0806,0.1.55.v20221112-0806]
- com.jcraft.jsch.source [0.1.55.v20221112-0806,0.1.55.v20221112-0806]
- com.jcraft.jzlib [1.1.3.v20220502-1820,1.1.3.v20220502-1820]
- com.jcraft.jzlib.source [1.1.3.v20220502-1820,1.1.3.v20220502-1820]
- net.i2p.crypto.eddsa [0.3.0.v20220506-1020,0.3.0.v20220506-1020]
- net.i2p.crypto.eddsa.source [0.3.0.v20220506-1020,0.3.0.v20220506-1020]
- org.apache.ant [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
- org.apache.ant.source [1.10.12.v20211102-1452,1.10.12.v20211102-1452]
- org.apache.httpcomponents.httpclient [4.5.14,4.5.14]
- org.apache.httpcomponents.httpclient.source [4.5.14,4.5.14]
- org.apache.httpcomponents.httpcore [4.4.16,4.4.16]
- org.apache.httpcomponents.httpcore.source [4.4.16,4.4.16]
- org.hamcrest.core [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
- org.hamcrest.core.source [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
- org.hamcrest.library [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
- org.hamcrest.library.source [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
- org.junit [4.13.2.v20230809-1000,4.13.2.v20230809-1000]
- org.junit.source [4.13.2.v20230809-1000,4.13.2.v20230809-1000]
- org.objenesis [3.3,3.3]
- org.objenesis.source [3.3,3.3]
- org.osgi.service.cm [1.6.1.202109301733,1.6.1.202109301733]
- org.osgi.service.cm.source [1.6.1.202109301733,1.6.1.202109301733]
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.31.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.31.tpd
deleted file mode 100644
index 0554a85..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.31.tpd
+++ /dev/null
@@ -1,27 +0,0 @@
-target "orbit-4.30" with source configurePhase
-// see https://download.eclipse.org/tools/orbit/downloads/
-
-location "https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023-12" {
- com.jcraft.jsch [0.1.55.v20230916-1400,0.1.55.v20230916-1400]
- com.jcraft.jsch.source [0.1.55.v20230916-1400,0.1.55.v20230916-1400]
- com.jcraft.jzlib [1.1.3.v20230916-1400,1.1.3.v20230916-1400]
- com.jcraft.jzlib.source [1.1.3.v20230916-1400,1.1.3.v20230916-1400]
- net.i2p.crypto.eddsa [0.3.0,0.3.0]
- net.i2p.crypto.eddsa.source [0.3.0,0.3.0]
- org.apache.ant [1.10.14.v20230922-1200,1.10.14.v20230922-1200]
- org.apache.ant.source [1.10.14.v20230922-1200,1.10.14.v20230922-1200]
- org.apache.httpcomponents.httpclient [4.5.14,4.5.14]
- org.apache.httpcomponents.httpclient.source [4.5.14,4.5.14]
- org.apache.httpcomponents.httpcore [4.4.16,4.4.16]
- org.apache.httpcomponents.httpcore.source [4.4.16,4.4.16]
- org.hamcrest.core [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
- org.hamcrest.core.source [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
- org.hamcrest.library [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
- org.hamcrest.library.source [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
- org.junit [4.13.2.v20230809-1000,4.13.2.v20230809-1000]
- org.junit.source [4.13.2.v20230809-1000,4.13.2.v20230809-1000]
- org.objenesis [3.3,3.3]
- org.objenesis.source [3.3,3.3]
- org.osgi.service.cm [1.6.1.202109301733,1.6.1.202109301733]
- org.osgi.service.cm.source [1.6.1.202109301733,1.6.1.202109301733]
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.30.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.32.tpd
similarity index 85%
copy from org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.30.tpd
copy to org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.32.tpd
index 0554a85..59fcd87 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.30.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.32.tpd
@@ -1,13 +1,11 @@
-target "orbit-4.30" with source configurePhase
+target "orbit-4.32" with source configurePhase
// see https://download.eclipse.org/tools/orbit/downloads/
-location "https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023-12" {
+location "https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2024-06" {
com.jcraft.jsch [0.1.55.v20230916-1400,0.1.55.v20230916-1400]
com.jcraft.jsch.source [0.1.55.v20230916-1400,0.1.55.v20230916-1400]
com.jcraft.jzlib [1.1.3.v20230916-1400,1.1.3.v20230916-1400]
com.jcraft.jzlib.source [1.1.3.v20230916-1400,1.1.3.v20230916-1400]
- net.i2p.crypto.eddsa [0.3.0,0.3.0]
- net.i2p.crypto.eddsa.source [0.3.0,0.3.0]
org.apache.ant [1.10.14.v20230922-1200,1.10.14.v20230922-1200]
org.apache.ant.source [1.10.14.v20230922-1200,1.10.14.v20230922-1200]
org.apache.httpcomponents.httpclient [4.5.14,4.5.14]
@@ -20,8 +18,8 @@
org.hamcrest.library.source [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
org.junit [4.13.2.v20230809-1000,4.13.2.v20230809-1000]
org.junit.source [4.13.2.v20230809-1000,4.13.2.v20230809-1000]
- org.objenesis [3.3,3.3]
- org.objenesis.source [3.3,3.3]
+ org.objenesis [3.4,3.4]
+ org.objenesis.source [3.4,3.4]
org.osgi.service.cm [1.6.1.202109301733,1.6.1.202109301733]
org.osgi.service.cm.source [1.6.1.202109301733,1.6.1.202109301733]
}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.30.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.33.tpd
similarity index 85%
rename from org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.30.tpd
rename to org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.33.tpd
index 0554a85..2cfa0a8 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.30.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.33.tpd
@@ -1,13 +1,11 @@
-target "orbit-4.30" with source configurePhase
+target "orbit-4.33" with source configurePhase
// see https://download.eclipse.org/tools/orbit/downloads/
-location "https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023-12" {
+location "https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2024-09" {
com.jcraft.jsch [0.1.55.v20230916-1400,0.1.55.v20230916-1400]
com.jcraft.jsch.source [0.1.55.v20230916-1400,0.1.55.v20230916-1400]
com.jcraft.jzlib [1.1.3.v20230916-1400,1.1.3.v20230916-1400]
com.jcraft.jzlib.source [1.1.3.v20230916-1400,1.1.3.v20230916-1400]
- net.i2p.crypto.eddsa [0.3.0,0.3.0]
- net.i2p.crypto.eddsa.source [0.3.0,0.3.0]
org.apache.ant [1.10.14.v20230922-1200,1.10.14.v20230922-1200]
org.apache.ant.source [1.10.14.v20230922-1200,1.10.14.v20230922-1200]
org.apache.httpcomponents.httpclient [4.5.14,4.5.14]
@@ -20,8 +18,8 @@
org.hamcrest.library.source [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
org.junit [4.13.2.v20230809-1000,4.13.2.v20230809-1000]
org.junit.source [4.13.2.v20230809-1000,4.13.2.v20230809-1000]
- org.objenesis [3.3,3.3]
- org.objenesis.source [3.3,3.3]
+ org.objenesis [3.4,3.4]
+ org.objenesis.source [3.4,3.4]
org.osgi.service.cm [1.6.1.202109301733,1.6.1.202109301733]
org.osgi.service.cm.source [1.6.1.202109301733,1.6.1.202109301733]
}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.30.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.34.tpd
similarity index 68%
copy from org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.30.tpd
copy to org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.34.tpd
index 0554a85..d3e15bb 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.30.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.34.tpd
@@ -1,15 +1,13 @@
-target "orbit-4.30" with source configurePhase
+target "orbit-4.34" with source configurePhase
// see https://download.eclipse.org/tools/orbit/downloads/
-location "https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023-12" {
+location "https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2024-12" {
com.jcraft.jsch [0.1.55.v20230916-1400,0.1.55.v20230916-1400]
com.jcraft.jsch.source [0.1.55.v20230916-1400,0.1.55.v20230916-1400]
com.jcraft.jzlib [1.1.3.v20230916-1400,1.1.3.v20230916-1400]
com.jcraft.jzlib.source [1.1.3.v20230916-1400,1.1.3.v20230916-1400]
- net.i2p.crypto.eddsa [0.3.0,0.3.0]
- net.i2p.crypto.eddsa.source [0.3.0,0.3.0]
- org.apache.ant [1.10.14.v20230922-1200,1.10.14.v20230922-1200]
- org.apache.ant.source [1.10.14.v20230922-1200,1.10.14.v20230922-1200]
+ org.apache.ant [1.10.15.v20240901-1000,1.10.15.v20240901-1000]
+ org.apache.ant.source [1.10.15.v20240901-1000,1.10.15.v20240901-1000]
org.apache.httpcomponents.httpclient [4.5.14,4.5.14]
org.apache.httpcomponents.httpclient.source [4.5.14,4.5.14]
org.apache.httpcomponents.httpcore [4.4.16,4.4.16]
@@ -18,10 +16,10 @@
org.hamcrest.core.source [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
org.hamcrest.library [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
org.hamcrest.library.source [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
- org.junit [4.13.2.v20230809-1000,4.13.2.v20230809-1000]
- org.junit.source [4.13.2.v20230809-1000,4.13.2.v20230809-1000]
- org.objenesis [3.3,3.3]
- org.objenesis.source [3.3,3.3]
+ org.junit [4.13.2.v20240929-1000,4.13.2.v20240929-1000]
+ org.junit.source [4.13.2.v20240929-1000,4.13.2.v20240929-1000]
+ org.objenesis [3.4,3.4]
+ org.objenesis.source [3.4,3.4]
org.osgi.service.cm [1.6.1.202109301733,1.6.1.202109301733]
org.osgi.service.cm.source [1.6.1.202109301733,1.6.1.202109301733]
}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.30.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.35.tpd
similarity index 68%
copy from org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.30.tpd
copy to org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.35.tpd
index 0554a85..ec6996e 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.30.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/orbit-4.35.tpd
@@ -1,15 +1,13 @@
-target "orbit-4.30" with source configurePhase
+target "orbit-4.35" with source configurePhase
// see https://download.eclipse.org/tools/orbit/downloads/
-location "https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2023-12" {
+location "https://download.eclipse.org/tools/orbit/simrel/orbit-aggregation/2025-03" {
com.jcraft.jsch [0.1.55.v20230916-1400,0.1.55.v20230916-1400]
com.jcraft.jsch.source [0.1.55.v20230916-1400,0.1.55.v20230916-1400]
com.jcraft.jzlib [1.1.3.v20230916-1400,1.1.3.v20230916-1400]
com.jcraft.jzlib.source [1.1.3.v20230916-1400,1.1.3.v20230916-1400]
- net.i2p.crypto.eddsa [0.3.0,0.3.0]
- net.i2p.crypto.eddsa.source [0.3.0,0.3.0]
- org.apache.ant [1.10.14.v20230922-1200,1.10.14.v20230922-1200]
- org.apache.ant.source [1.10.14.v20230922-1200,1.10.14.v20230922-1200]
+ org.apache.ant [1.10.15.v20240901-1000,1.10.15.v20240901-1000]
+ org.apache.ant.source [1.10.15.v20240901-1000,1.10.15.v20240901-1000]
org.apache.httpcomponents.httpclient [4.5.14,4.5.14]
org.apache.httpcomponents.httpclient.source [4.5.14,4.5.14]
org.apache.httpcomponents.httpcore [4.4.16,4.4.16]
@@ -18,10 +16,10 @@
org.hamcrest.core.source [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
org.hamcrest.library [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
org.hamcrest.library.source [1.3.0.v20230809-1000,1.3.0.v20230809-1000]
- org.junit [4.13.2.v20230809-1000,4.13.2.v20230809-1000]
- org.junit.source [4.13.2.v20230809-1000,4.13.2.v20230809-1000]
- org.objenesis [3.3,3.3]
- org.objenesis.source [3.3,3.3]
+ org.junit [4.13.2.v20240929-1000,4.13.2.v20240929-1000]
+ org.junit.source [4.13.2.v20240929-1000,4.13.2.v20240929-1000]
+ org.objenesis [3.4,3.4]
+ org.objenesis.source [3.4,3.4]
org.osgi.service.cm [1.6.1.202109301733,1.6.1.202109301733]
org.osgi.service.cm.source [1.6.1.202109301733,1.6.1.202109301733]
}
diff --git a/org.eclipse.jgit.packaging/pom.xml b/org.eclipse.jgit.packaging/pom.xml
index 06cd76c..6352d0a 100644
--- a/org.eclipse.jgit.packaging/pom.xml
+++ b/org.eclipse.jgit.packaging/pom.xml
@@ -16,7 +16,7 @@
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>JGit Tycho Parent</name>
@@ -30,8 +30,8 @@
<properties>
<java.version>17</java.version>
- <tycho-version>4.0.6</tycho-version>
- <target-platform>jgit-4.17</target-platform>
+ <tycho-version>4.0.11</tycho-version>
+ <target-platform>jgit-4.32</target-platform>
<project.build.outputTimestamp>${git.commit.time}</project.build.outputTimestamp>
</properties>
@@ -174,7 +174,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
- <version>3.4.1</version>
+ <version>3.5.0</version>
<executions>
<execution>
<id>enforce-maven</id>
@@ -184,7 +184,7 @@
<configuration>
<rules>
<requireMavenVersion>
- <version>3.6.3</version>
+ <version>3.9.0</version>
</requireMavenVersion>
</rules>
</configuration>
@@ -204,7 +204,7 @@
<plugin>
<groupId>org.cyclonedx</groupId>
<artifactId>cyclonedx-maven-plugin</artifactId>
- <version>2.7.11</version>
+ <version>2.9.1</version>
<configuration>
<projectType>library</projectType>
<schemaVersion>1.4</schemaVersion>
@@ -233,7 +233,7 @@
<plugin>
<groupId>io.github.git-commit-id</groupId>
<artifactId>git-commit-id-maven-plugin</artifactId>
- <version>7.0.0</version>
+ <version>9.0.1</version>
<executions>
<execution>
<id>get-the-git-infos</id>
@@ -273,7 +273,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
- <version>3.3.0</version>
+ <version>3.4.2</version>
<configuration>
<archive>
<manifestEntries>
@@ -381,36 +381,36 @@
<plugin>
<groupId>org.eclipse.cbi.maven.plugins</groupId>
<artifactId>eclipse-jarsigner-plugin</artifactId>
- <version>1.4.3</version>
+ <version>1.5.2</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
- <version>3.5.0</version>
+ <version>3.6.0</version>
</plugin>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
- <version>3.3.2</version>
+ <version>3.4.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
- <version>3.1.1</version>
+ <version>3.1.3</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
- <version>3.1.1</version>
+ <version>3.1.3</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
- <version>4.0.0-M13</version>
+ <version>4.0.0-M16</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-artifact-plugin</artifactId>
- <version>3.5.0</version>
+ <version>3.6.0</version>
<configuration>
<ignore>**/*cyclonedx.json</ignore>
<reproducible>true</reproducible>
diff --git a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
index 955c5c0..2056f0b 100644
--- a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
@@ -3,30 +3,30 @@
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.pgm.test
Bundle-SymbolicName: org.eclipse.jgit.pgm.test
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-Vendor: %Bundle-Vendor
Bundle-Localization: plugin
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: JavaSE-17
-Import-Package: org.eclipse.jgit.api;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.api.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.diff;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.dircache;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.diffmergetool;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.file;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.junit;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib.internal;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.merge;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.pgm;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.pgm.internal;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.pgm.opt;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.revwalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.storage.file;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.treewalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util.io;version="[7.0.0,7.1.0)",
+Import-Package: org.eclipse.jgit.api;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.api.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.diff;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.dircache;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.diffmergetool;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.junit;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib.internal;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.merge;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.pgm;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.pgm.internal;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.pgm.opt;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.revwalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.treewalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util.io;version="[7.3.0,7.4.0)",
org.hamcrest.core;bundle-version="[1.1.0,3.0.0)",
org.junit;version="[4.13,5.0.0)",
org.junit.rules;version="[4.13,5.0.0)",
diff --git a/org.eclipse.jgit.pgm.test/pom.xml b/org.eclipse.jgit.pgm.test/pom.xml
index 90090d6..24c289c 100644
--- a/org.eclipse.jgit.pgm.test/pom.xml
+++ b/org.eclipse.jgit.pgm.test/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.pgm.test</artifactId>
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/AddTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/AddTest.java
index 6d6374f..a48fcbc 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/AddTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/AddTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Google Inc. and others
+ * Copyright (C) 2012, 2025 Google Inc. and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -12,7 +12,10 @@
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.fail;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.dircache.DirCache;
@@ -32,12 +35,7 @@ public void setUp() throws Exception {
@Test
public void testAddNothing() throws Exception {
- try {
- execute("git add");
- fail("Must die");
- } catch (Die e) {
- // expected, requires argument
- }
+ assertThrows(Die.class, () -> execute("git add"));
}
@Test
@@ -46,6 +44,17 @@ public void testAddUsage() throws Exception {
}
@Test
+ public void testAddInvalidOptionCombinations() throws Exception {
+ writeTrashFile("greeting", "Hello, world!");
+ assertThrows(Die.class, () -> execute("git add -u -A greeting"));
+ assertThrows(Die.class,
+ () -> execute("git add -u --ignore-removed greeting"));
+ // --renormalize implies -u
+ assertThrows(Die.class,
+ () -> execute("git add --renormalize --all greeting"));
+ }
+
+ @Test
public void testAddAFile() throws Exception {
writeTrashFile("greeting", "Hello, world!");
assertArrayEquals(new String[] { "" }, //
@@ -78,4 +87,34 @@ public void testAddAlreadyAdded() throws Exception {
assertNotNull(cache.getEntry("greeting"));
assertEquals(1, cache.getEntryCount());
}
+
+ @Test
+ public void testAddDeleted() throws Exception {
+ File greeting = writeTrashFile("greeting", "Hello, world!");
+ git.add().addFilepattern("greeting").call();
+ DirCache cache = db.readDirCache();
+ assertNotNull(cache.getEntry("greeting"));
+ assertEquals(1, cache.getEntryCount());
+ assertTrue(greeting.delete());
+ assertArrayEquals(new String[] { "" }, //
+ execute("git add greeting"));
+
+ cache = db.readDirCache();
+ assertEquals(0, cache.getEntryCount());
+ }
+
+ @Test
+ public void testAddDeleted2() throws Exception {
+ File greeting = writeTrashFile("greeting", "Hello, world!");
+ git.add().addFilepattern("greeting").call();
+ DirCache cache = db.readDirCache();
+ assertNotNull(cache.getEntry("greeting"));
+ assertEquals(1, cache.getEntryCount());
+ assertTrue(greeting.delete());
+ assertArrayEquals(new String[] { "" }, //
+ execute("git add -A"));
+
+ cache = db.readDirCache();
+ assertEquals(0, cache.getEntryCount());
+ }
}
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CloneTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CloneTest.java
index a1fb9fb..c56cc6b 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CloneTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CloneTest.java
@@ -126,7 +126,7 @@ private RevCommit createSecondCommit() throws Exception {
JGitTestUtil.writeTrashFile(db, "Test.txt", "Some change");
git.add().addFilepattern("Test.txt").call();
return git.commit()
- .setCommitter(new PersonIdent(this.committer, tr.getDate()))
+ .setCommitter(new PersonIdent(this.committer, tr.getInstant()))
.setMessage("Second commit").call();
}
@@ -134,7 +134,7 @@ private RevCommit createThirdCommit() throws Exception {
JGitTestUtil.writeTrashFile(db, "change.txt", "another change");
git.add().addFilepattern("change.txt").call();
return git.commit()
- .setCommitter(new PersonIdent(this.committer, tr.getDate()))
+ .setCommitter(new PersonIdent(this.committer, tr.getInstant()))
.setMessage("Third commit").call();
}
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/DescribeTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/DescribeTest.java
index c785443..595767d 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/DescribeTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/DescribeTest.java
@@ -123,6 +123,15 @@ public void testDescribeCommitMatch2() throws Exception {
}
@Test
+ public void testDescribeExclude() throws Exception {
+ initialCommitAndTag();
+ secondCommit();
+ git.tag().setName("v2.0").call();
+ assertArrayEquals(new String[] { "v1.0-1-g56f6ceb", "" },
+ execute("git describe --exclude v2.*"));
+ }
+
+ @Test
public void testDescribeCommitMultiMatch() throws Exception {
initialCommitAndTag();
secondCommit();
@@ -133,6 +142,17 @@ public void testDescribeCommitMultiMatch() throws Exception {
}
@Test
+ public void testDescribeCommitMultiExclude() throws Exception {
+ initialCommitAndTag();
+ secondCommit();
+ git.tag().setName("v2.0.0").call();
+ git.tag().setName("v2.1.1").call();
+ git.tag().setName("v2.2").call();
+ assertArrayEquals("git yields v2.2", new String[] { "v2.2", "" },
+ execute("git describe --exclude v2.0* --exclude v2.1.*"));
+ }
+
+ @Test
public void testDescribeCommitNoMatch() throws Exception {
initialCommitAndTag();
writeTrashFile("greeting", "Hello, world!");
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/MergeToolTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/MergeToolTest.java
index 54c4f26..6339831 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/MergeToolTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/MergeToolTest.java
@@ -27,6 +27,7 @@
import org.eclipse.jgit.internal.diffmergetool.MergeTools;
import org.eclipse.jgit.lib.StoredConfig;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
/**
@@ -77,6 +78,7 @@ public void testUserToolWithCommandNotFoundError() throws Exception {
+ errorReturnCode);
}
+ @Ignore
@Test
public void testEmptyToolName() throws Exception {
assumeLinuxPlatform();
@@ -91,7 +93,7 @@ public void testEmptyToolName() throws Exception {
createMergeConflict();
- String araxisErrorLine = "compare: unrecognized option `-wait' @ error/compare.c/CompareImageCommand/1123.";
+ String araxisErrorLine = "compare-im6.q16: unrecognized option `-wait' @ error/compare.c/CompareImageCommand/1131.";
String[] expectedErrorOutput = { araxisErrorLine, araxisErrorLine, };
runAndCaptureUsingInitRaw(Arrays.asList(expectedErrorOutput),
MERGE_TOOL, "--no-prompt");
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/PackRefsTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/PackRefsTest.java
new file mode 100644
index 0000000..b4d4ea9
--- /dev/null
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/PackRefsTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.eclipse.jgit.pgm;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.internal.storage.file.FileRepository;
+import org.eclipse.jgit.lib.CLIRepositoryTestCase;
+import org.eclipse.jgit.lib.ConfigConstants;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Ref;
+import org.junit.Before;
+import org.junit.Test;
+
+public class PackRefsTest extends CLIRepositoryTestCase {
+ private Git git;
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ git = new Git(db);
+ git.commit().setMessage("initial commit").call();
+ }
+
+ @Test
+ public void tagPacked() throws Exception {
+ git.tag().setName("test").call();
+ git.packRefs().call();
+ assertEquals(Ref.Storage.PACKED,
+ git.getRepository().exactRef("refs/tags/test").getStorage());
+ }
+
+ @Test
+ public void nonTagRefNotPackedWithoutAll() throws Exception {
+ git.branchCreate().setName("test").call();
+ git.packRefs().call();
+ assertEquals(Ref.Storage.LOOSE,
+ git.getRepository().exactRef("refs/heads/test").getStorage());
+ }
+
+ @Test
+ public void nonTagRefPackedWithAll() throws Exception {
+ git.branchCreate().setName("test").call();
+ git.packRefs().setAll(true).call();
+ assertEquals(Ref.Storage.PACKED,
+ git.getRepository().exactRef("refs/heads/test").getStorage());
+ }
+
+ @Test
+ public void refTableCompacted() throws Exception {
+ ((FileRepository) git.getRepository()).convertRefStorage(
+ ConfigConstants.CONFIG_REF_STORAGE_REFTABLE, false, false);
+
+ git.commit().setMessage("test commit").call();
+ File tableDir = new File(db.getDirectory(), Constants.REFTABLE);
+ File[] reftables = tableDir.listFiles();
+ assertNotNull(reftables);
+ assertTrue(reftables.length > 2);
+
+ git.packRefs().call();
+
+ reftables = tableDir.listFiles();
+ assertNotNull(reftables);
+ assertEquals(2, reftables.length);
+ }
+}
diff --git a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
index bb0c23d..d91efd4 100644
--- a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.pgm
Bundle-SymbolicName: org.eclipse.jgit.pgm
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-Vendor: %Bundle-Vendor
Bundle-Localization: OSGI-INF/l10n/plugin
Bundle-RequiredExecutionEnvironment: JavaSE-17
@@ -14,49 +14,50 @@
org.eclipse.jetty.server.handler;version="[12.0.0,13.0.0)",
org.eclipse.jetty.util;version="[12.0.0,13.0.0)",
org.eclipse.jetty.util.component;version="[12.0.0,13.0.0)",
- org.eclipse.jgit.api;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.api.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.archive;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.awtui;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.blame;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.diff;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.dircache;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.gitrepo;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.file;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.diffmergetool;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.io;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.pack;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.reftable;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lfs;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lfs.server;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lfs.server.fs;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lfs.server.s3;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.merge;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib.internal;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.nls;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.notes;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.revplot;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.revwalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.revwalk.filter;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.storage.file;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.storage.pack;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport.http.apache;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport.resolver;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport.ssh.jsch;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport.sshd;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.treewalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.treewalk.filter;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util.io;version="[7.0.0,7.1.0)",
+ org.eclipse.jgit.api;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.api.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.archive;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.awtui;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.blame;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.diff;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.dircache;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.gitrepo;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.diffmergetool;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.io;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.midx;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.reftable;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lfs;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lfs.server;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lfs.server.fs;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lfs.server.s3;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib.internal;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.merge;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.nls;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.notes;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.revplot;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.revwalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.revwalk.filter;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.storage.pack;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport.http.apache;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport.resolver;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport.ssh.jsch;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport.sshd;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.treewalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.treewalk.filter;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util.io;version="[7.3.0,7.4.0)",
org.kohsuke.args4j;version="[2.33.0,3.0.0)",
org.kohsuke.args4j.spi;version="[2.33.0,3.0.0)"
-Export-Package: org.eclipse.jgit.console;version="7.0.0";
+Export-Package: org.eclipse.jgit.console;version="7.3.0";
uses:="org.eclipse.jgit.transport,
org.eclipse.jgit.util",
- org.eclipse.jgit.pgm;version="7.0.0";
+ org.eclipse.jgit.pgm;version="7.3.0";
uses:="org.eclipse.jgit.transport,
org.eclipse.jgit.util.io,
org.eclipse.jgit.awtui,
@@ -68,14 +69,14 @@
org.eclipse.jgit.treewalk,
org.eclipse.jgit.api,
javax.swing",
- org.eclipse.jgit.pgm.debug;version="7.0.0";
+ org.eclipse.jgit.pgm.debug;version="7.3.0";
uses:="org.eclipse.jgit.util.io,
org.eclipse.jgit.pgm,
org.eclipse.jetty.servlet",
- org.eclipse.jgit.pgm.internal;version="7.0.0";
+ org.eclipse.jgit.pgm.internal;version="7.3.0";
x-friends:="org.eclipse.jgit.pgm.test,
org.eclipse.jgit.test",
- org.eclipse.jgit.pgm.opt;version="7.0.0";
+ org.eclipse.jgit.pgm.opt;version="7.3.0";
uses:="org.kohsuke.args4j,
org.eclipse.jgit.lib,
org.eclipse.jgit.revwalk,
diff --git a/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF
index 8bb1b64..1c4a481 100644
--- a/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@
Bundle-Name: org.eclipse.jgit.pgm - Sources
Bundle-SymbolicName: org.eclipse.jgit.pgm.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.0.0.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="7.0.0.qualifier";roots="."
+Bundle-Version: 7.3.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="7.3.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin b/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin
index 08d3727..6bf88d9 100644
--- a/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin
+++ b/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin
@@ -26,6 +26,8 @@
org.eclipse.jgit.pgm.Merge
org.eclipse.jgit.pgm.MergeBase
org.eclipse.jgit.pgm.MergeTool
+org.eclipse.jgit.pgm.MultiPackIndex
+org.eclipse.jgit.pgm.PackRefs
org.eclipse.jgit.pgm.Push
org.eclipse.jgit.pgm.ReceivePack
org.eclipse.jgit.pgm.Reflog
diff --git a/org.eclipse.jgit.pgm/pom.xml b/org.eclipse.jgit.pgm/pom.xml
index 7cd76c3..5890ce8 100644
--- a/org.eclipse.jgit.pgm/pom.xml
+++ b/org.eclipse.jgit.pgm/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.pgm</artifactId>
diff --git a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
index 50ee809..e9630e9 100644
--- a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
+++ b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
@@ -12,6 +12,7 @@
# default meta variable defined in the org.kohsuke.args4j.spi.OneArgumentOptionHandler
N=N
+addIncompatibleOptions=--update/-u cannot be combined with --all/-A/--no-ignore-removal or --no-all/--ignore-removal. Note that --renormalize implies --update.
alreadyOnBranch=Already on ''{0}''
alreadyUpToDate=Already up-to-date.
answerNo=n
@@ -255,8 +256,11 @@
untrackedFiles=Untracked files:
updating=Updating {0}..{1}
usage_Abbrev=Instead of using the default number of hexadecimal digits (which will vary according to the number of objects in the repository with a default of 7) of the abbreviated object name, use <n> digits, or as many digits as needed to form a unique object name. An <n> of 0 will suppress long format, only showing the closest tag.
-usage_addRenormalize=Apply the "clean" process freshly to tracked files to forcibly add them again to the index. This implies -u.
+usage_addRenormalize=Apply the "clean" process freshly to tracked files to forcibly add them again to the index. This implies --update/-u.
+usage_addStageDeletions=Add, modify, or remove index entries to match the working tree. Cannot be used with --update/-u.
+usage_addDontStageDeletions=Only add or modify index entries, but do not remove index entries for which there is no file. (Don''t stage deletions.) Cannot be used with --update/-u.
usage_Aggressive=This option will cause gc to more aggressively optimize the repository at the expense of taking much more time
+usage_All=Pack all refs, except hidden refs, broken refs, and symbolic refs.
usage_AlwaysFallback=Show uniquely abbreviated commit object as fallback
usage_bareClone=Make a bare Git repository. That is, instead of creating [DIRECTORY] and placing the administrative files in [DIRECTORY]/.git, make the [DIRECTORY] itself the $GIT_DIR.
usage_extraArgument=Pass an extra argument to a merge driver. Currently supported are "-X ours" and "-X theirs".
@@ -278,6 +282,7 @@
usage_Describe=Show the most recent tag that is reachable from a commit
usage_DiffAlgorithms=Test performance of jgit's diff algorithms
usage_DisplayTheVersionOfJgit=Display the version of jgit
+usage_Exclude=Do not consider tags matching the given glob(7) pattern, excluding the "refs/tags/" prefix
usage_Gc=Cleanup unnecessary files and optimize the local repository
usage_Glog=View commit history as a graph
usage_DiffGuiTool=When git-difftool is invoked with the -g or --gui option the default diff tool will be read from the configured diff.guitool variable instead of diff.tool.
@@ -299,7 +304,9 @@
usage_Match=Only consider tags matching the given glob(7) pattern or patterns, excluding the "refs/tags/" prefix.
usage_MergeBase=Find as good common ancestors as possible for a merge
usage_MergesTwoDevelopmentHistories=Merges two development histories
+usage_MultiPackIndex=Operations over the multipack index
usage_PackKeptObjects=Include objects in packs locked by a ".keep" file when repacking
+usage_PackRefs=Pack heads and tags for efficient repository access
usage_PreserveOldPacks=Preserve old pack files by moving them into the preserved subdirectory instead of deleting them after repacking
usage_PrunePreserved=Remove the preserved subdirectory containing previously preserved old pack files before repacking, and before preserving more old pack files
usage_ReadDirCache= Read the DirCache 100 times
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Add.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Add.java
index 2ebab5e..dc9d77d 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Add.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Add.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010, Sasa Zivkov <sasa.zivkov@sap.com> and others
+ * Copyright (C) 2010, 2025 Sasa Zivkov <sasa.zivkov@sap.com> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -16,6 +16,7 @@
import org.eclipse.jgit.api.AddCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.pgm.internal.CLIText;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;
@@ -28,17 +29,33 @@ class Add extends TextBuiltin {
@Option(name = "--update", aliases = { "-u" }, usage = "usage_onlyMatchAgainstAlreadyTrackedFiles")
private boolean update = false;
- @Argument(required = true, metaVar = "metaVar_filepattern", usage = "usage_filesToAddContentFrom")
+ @Option(name = "--all", aliases = { "-A",
+ "--no-ignore-removal" }, usage = "usage_addStageDeletions")
+ private Boolean all;
+
+ @Option(name = "--no-all", aliases = {
+ "--ignore-removal" }, usage = "usage_addDontStageDeletions")
+ private void noAll(@SuppressWarnings("unused") boolean ignored) {
+ all = Boolean.FALSE;
+ }
+
+ @Argument(metaVar = "metaVar_filepattern", usage = "usage_filesToAddContentFrom")
private List<String> filepatterns = new ArrayList<>();
@Override
protected void run() throws Exception {
try (Git git = new Git(db)) {
- AddCommand addCmd = git.add();
if (renormalize) {
update = true;
}
+ if (update && all != null) {
+ throw die(CLIText.get().addIncompatibleOptions);
+ }
+ AddCommand addCmd = git.add();
addCmd.setUpdate(update).setRenormalize(renormalize);
+ if (all != null) {
+ addCmd.setAll(all.booleanValue());
+ }
for (String p : filepatterns) {
addCmd.addFilepattern(p);
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Blame.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Blame.java
index d2285ae..285fe2a 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Blame.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Blame.java
@@ -18,7 +18,7 @@
import java.io.IOException;
import java.text.MessageFormat;
-import java.text.SimpleDateFormat;
+import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -91,7 +91,7 @@ void ignoreAllSpace(@SuppressWarnings("unused") boolean on) {
private final Map<RevCommit, String> abbreviatedCommits = new HashMap<>();
- private SimpleDateFormat dateFmt;
+ private DateTimeFormatter dateFmt;
private int begin;
@@ -125,9 +125,9 @@ protected void run() {
}
if (showRawTimestamp) {
- dateFmt = new SimpleDateFormat("ZZZZ"); //$NON-NLS-1$
+ dateFmt = DateTimeFormatter.ofPattern("ZZ"); //$NON-NLS-1$
} else {
- dateFmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss ZZZZ"); //$NON-NLS-1$
+ dateFmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss ZZ"); //$NON-NLS-1$
}
try (ObjectReader reader = db.newObjectReader();
@@ -335,12 +335,14 @@ private String date(int line) {
if (author == null)
return ""; //$NON-NLS-1$
- dateFmt.setTimeZone(author.getTimeZone());
- if (!showRawTimestamp)
- return dateFmt.format(author.getWhen());
+ if (!showRawTimestamp) {
+ return dateFmt.withZone(author.getZoneId())
+ .format(author.getWhenAsInstant());
+ }
return String.format("%d %s", //$NON-NLS-1$
- Long.valueOf(author.getWhen().getTime() / 1000L),
- dateFmt.format(author.getWhen()));
+ Long.valueOf(author.getWhenAsInstant().getEpochSecond()),
+ dateFmt.withZone(author.getZoneId())
+ .format(author.getWhenAsInstant()));
}
private String abbreviate(ObjectReader reader, RevCommit commit)
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Config.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Config.java
index 52f40c2..f5de704 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Config.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Config.java
@@ -94,7 +94,7 @@ private void list() throws IOException, ConfigInvalidException {
if (global || isListAll())
list(SystemReader.getInstance().openUserConfig(null, fs));
if (local || isListAll())
- list(new FileBasedConfig(fs.resolve(getRepository().getDirectory(),
+ list(new FileBasedConfig(fs.resolve(getRepository().getCommonDirectory(),
Constants.CONFIG), fs));
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Describe.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Describe.java
index 913d7c7..2633336 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Describe.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Describe.java
@@ -44,6 +44,9 @@ class Describe extends TextBuiltin {
@Option(name = "--match", usage = "usage_Match", metaVar = "metaVar_pattern")
private List<String> patterns = new ArrayList<>();
+ @Option(name = "--exclude", usage = "usage_Exclude", metaVar = "metaVar_pattern")
+ private List<String> excludes = new ArrayList<>();
+
@Option(name = "--abbrev", usage = "usage_Abbrev")
private Integer abbrev;
@@ -59,6 +62,7 @@ protected void run() {
cmd.setTags(useTags);
cmd.setAlways(always);
cmd.setMatch(patterns.toArray(new String[0]));
+ cmd.setExclude(excludes.toArray(new String[0]));
if (abbrev != null) {
cmd.setAbbrev(abbrev.intValue());
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Log.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Log.java
index 852a4b3..958e566 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Log.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Log.java
@@ -32,13 +32,12 @@
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.GpgConfig;
-import org.eclipse.jgit.lib.GpgSignatureVerifier;
-import org.eclipse.jgit.lib.GpgSignatureVerifier.SignatureVerification;
-import org.eclipse.jgit.lib.GpgSignatureVerifierFactory;
+import org.eclipse.jgit.lib.SignatureVerifier.SignatureVerification;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.SignatureVerifiers;
import org.eclipse.jgit.notes.NoteMap;
import org.eclipse.jgit.pgm.internal.CLIText;
import org.eclipse.jgit.pgm.internal.VerificationUtils;
@@ -174,8 +173,6 @@ void noPrefix(@SuppressWarnings("unused") boolean on) {
// END -- Options shared with Diff
- private GpgSignatureVerifier verifier;
-
private GpgConfig config;
Log() {
@@ -227,9 +224,6 @@ protected void run() {
throw die(e.getMessage(), e);
} finally {
diffFmt.close();
- if (verifier != null) {
- verifier.clear();
- }
}
}
@@ -293,21 +287,13 @@ private void showSignature(RevCommit c) throws IOException {
if (c.getRawGpgSignature() == null) {
return;
}
- if (verifier == null) {
- GpgSignatureVerifierFactory factory = GpgSignatureVerifierFactory
- .getDefault();
- if (factory == null) {
- throw die(CLIText.get().logNoSignatureVerifier, null);
- }
- verifier = factory.getVerifier();
- }
- SignatureVerification verification = verifier.verifySignature(c,
- config);
+ SignatureVerification verification = SignatureVerifiers.verify(db,
+ config, c);
if (verification == null) {
return;
}
VerificationUtils.writeVerification(outw, verification,
- verifier.getName(), c.getCommitterIdent());
+ verification.verifierName(), c.getCommitterIdent());
}
/**
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MergeBase.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MergeBase.java
index aacde2f..a29c4d9 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MergeBase.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MergeBase.java
@@ -26,11 +26,6 @@ class MergeBase extends TextBuiltin {
private boolean all;
@Argument(index = 0, metaVar = "metaVar_commitish", required = true)
- void commit_0(final RevCommit c) {
- commits.add(c);
- }
-
- @Argument(index = 1, metaVar = "metaVar_commitish", required = true)
private List<RevCommit> commits = new ArrayList<>();
@Override
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MultiPackIndex.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MultiPackIndex.java
new file mode 100644
index 0000000..1844223
--- /dev/null
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MultiPackIndex.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2025, Google LLC.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.eclipse.jgit.pgm;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jgit.internal.storage.file.ObjectDirectory;
+import org.eclipse.jgit.internal.storage.file.Pack;
+import org.eclipse.jgit.internal.storage.file.PackFile;
+import org.eclipse.jgit.internal.storage.file.PackIndex;
+import org.eclipse.jgit.internal.storage.midx.MultiPackIndexPrettyPrinter;
+import org.eclipse.jgit.internal.storage.midx.MultiPackIndexWriter;
+import org.eclipse.jgit.internal.storage.pack.PackExt;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.kohsuke.args4j.Argument;
+import org.kohsuke.args4j.Option;
+
+@Command(common = true, usage = "usage_MultiPackIndex")
+@SuppressWarnings("nls")
+class MultiPackIndex extends TextBuiltin {
+ @Argument(index = 0, required = true, usage = "write, print")
+ private String command;
+
+ @Option(name = "--midx")
+ private String midxPath;
+
+ /** {@inheritDoc} */
+ @Override
+ protected void run() throws IOException {
+ switch (command) {
+ case "print":
+ printMultiPackIndex();
+ break;
+ case "write":
+ writeMultiPackIndex();
+ break;
+ default:
+ outw.println("Unknown command " + command);
+ }
+ }
+
+ private void printMultiPackIndex() {
+ if (midxPath == null || midxPath.isEmpty()) {
+ throw die("'print' requires the path of a multipack "
+ + "index file with --midx option.");
+ }
+
+ try (FileInputStream is = new FileInputStream(midxPath)) {
+ PrintWriter pw = new PrintWriter(outw, true);
+ MultiPackIndexPrettyPrinter.prettyPrint(is.readAllBytes(), pw);
+ } catch (FileNotFoundException e) {
+ throw die(true, e);
+ } catch (IOException e) {
+ throw die(true, e);
+ }
+ }
+
+ private void writeMultiPackIndex() throws IOException {
+ if (!(db.getObjectDatabase() instanceof ObjectDirectory)) {
+ throw die("This repository object db doesn't have packs");
+ }
+
+ File midx;
+ if (midxPath == null || midxPath.isEmpty()) {
+ midx = new File(((ObjectDirectory) db.getObjectDatabase())
+ .getPackDirectory(), "multi-pack-index");
+ } else {
+ midx = new File(midxPath);
+ }
+
+ errw.println("Writing " + midx.getAbsolutePath());
+
+ ObjectDirectory odb = (ObjectDirectory) db.getObjectDatabase();
+
+ Map<String, PackIndex> indexes = new HashMap<>();
+ for (Pack pack : odb.getPacks()) {
+ PackFile packFile = pack.getPackFile().create(PackExt.INDEX);
+ try {
+ indexes.put(packFile.getName(), pack.getIndex());
+ } catch (IOException e) {
+ throw die("Cannot open index in pack", e);
+ }
+ }
+
+ MultiPackIndexWriter writer = new MultiPackIndexWriter();
+ try (FileOutputStream out = new FileOutputStream(midxPath)) {
+ writer.write(NullProgressMonitor.INSTANCE, out, indexes);
+ } catch (IOException e) {
+ throw die("Cannot write midx " + midxPath, e);
+ }
+ }
+}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/PackRefs.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/PackRefs.java
new file mode 100644
index 0000000..ee05f5c
--- /dev/null
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/PackRefs.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.eclipse.jgit.pgm;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.lib.TextProgressMonitor;
+import org.kohsuke.args4j.Option;
+
+@Command(common = true, usage = "usage_PackRefs")
+class PackRefs extends TextBuiltin {
+ @Option(name = "--all", usage = "usage_All")
+ private boolean all;
+
+ @Override
+ protected void run() {
+ Git git = Git.wrap(db);
+ try {
+ git.packRefs().setProgressMonitor(new TextProgressMonitor(errw))
+ .setAll(all).call();
+ } catch (GitAPIException e) {
+ throw die(e.getMessage(), e);
+ }
+ }
+}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Show.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Show.java
index 4feb090..a3a6782 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Show.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Show.java
@@ -14,11 +14,10 @@
import java.io.BufferedOutputStream;
import java.io.IOException;
-import java.text.DateFormat;
import java.text.MessageFormat;
-import java.text.SimpleDateFormat;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
import java.util.Locale;
-import java.util.TimeZone;
import org.eclipse.jgit.diff.DiffFormatter;
import org.eclipse.jgit.diff.RawTextComparator;
@@ -30,12 +29,11 @@
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.GpgConfig;
-import org.eclipse.jgit.lib.GpgSignatureVerifier;
-import org.eclipse.jgit.lib.GpgSignatureVerifierFactory;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.GpgSignatureVerifier.SignatureVerification;
+import org.eclipse.jgit.lib.SignatureVerifier.SignatureVerification;
+import org.eclipse.jgit.lib.SignatureVerifiers;
import org.eclipse.jgit.pgm.internal.CLIText;
import org.eclipse.jgit.pgm.internal.VerificationUtils;
import org.eclipse.jgit.pgm.opt.PathTreeFilterHandler;
@@ -52,9 +50,9 @@
@Command(common = true, usage = "usage_show")
class Show extends TextBuiltin {
- private final TimeZone myTZ = TimeZone.getDefault();
+ private final ZoneId myTZ = ZoneId.systemDefault();
- private final DateFormat fmt;
+ private final DateTimeFormatter fmt;
private DiffFormatter diffFmt;
@@ -158,7 +156,8 @@ void noPrefix(@SuppressWarnings("unused") boolean on) {
// END -- Options shared with Diff
Show() {
- fmt = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy ZZZZZ", Locale.US); //$NON-NLS-1$
+ fmt = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss yyyy ZZ", //$NON-NLS-1$
+ Locale.US);
}
@Override
@@ -233,15 +232,17 @@ private void show(RevTag tag) throws IOException {
outw.print(tag.getTagName());
outw.println();
- final PersonIdent tagger = tag.getTaggerIdent();
+ PersonIdent tagger = tag.getTaggerIdent();
if (tagger != null) {
outw.println(MessageFormat.format(CLIText.get().taggerInfo,
tagger.getName(), tagger.getEmailAddress()));
- final TimeZone taggerTZ = tagger.getTimeZone();
- fmt.setTimeZone(taggerTZ != null ? taggerTZ : myTZ);
+ ZoneId taggerTZ = tagger.getZoneId();
+ String formattedTaggerTime = fmt
+ .withZone(taggerTZ != null ? taggerTZ : myTZ)
+ .format(tagger.getWhenAsInstant());
outw.println(MessageFormat.format(CLIText.get().dateInfo,
- fmt.format(tagger.getWhen())));
+ formattedTaggerTime));
}
outw.println();
@@ -294,10 +295,12 @@ private void show(RevWalk rw, RevCommit c) throws IOException {
outw.println(MessageFormat.format(CLIText.get().authorInfo,
author.getName(), author.getEmailAddress()));
- final TimeZone authorTZ = author.getTimeZone();
- fmt.setTimeZone(authorTZ != null ? authorTZ : myTZ);
+ final ZoneId authorTZ = author.getZoneId();
+ String formattedAuthorTime = fmt
+ .withZone(authorTZ != null ? authorTZ : myTZ)
+ .format(author.getWhenAsInstant());
outw.println(MessageFormat.format(CLIText.get().dateInfo,
- fmt.format(author.getWhen())));
+ formattedAuthorTime));
outw.println();
final String[] lines = c.getFullMessage().split("\n"); //$NON-NLS-1$
@@ -335,23 +338,13 @@ private void showSignature(RevCommit c) throws IOException {
if (c.getRawGpgSignature() == null) {
return;
}
- GpgSignatureVerifierFactory factory = GpgSignatureVerifierFactory
- .getDefault();
- if (factory == null) {
+ GpgConfig config = new GpgConfig(db.getConfig());
+ SignatureVerification verification = SignatureVerifiers.verify(db,
+ config, c);
+ if (verification == null) {
throw die(CLIText.get().logNoSignatureVerifier, null);
}
- GpgSignatureVerifier verifier = factory.getVerifier();
- GpgConfig config = new GpgConfig(db.getConfig());
- try {
- SignatureVerification verification = verifier.verifySignature(c,
- config);
- if (verification == null) {
- return;
- }
- VerificationUtils.writeVerification(outw, verification,
- verifier.getName(), c.getCommitterIdent());
- } finally {
- verifier.clear();
- }
+ VerificationUtils.writeVerification(outw, verification,
+ verification.verifierName(), c.getCommitterIdent());
}
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Tag.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Tag.java
index 4ea67ab..6be30c9 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Tag.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Tag.java
@@ -27,10 +27,10 @@
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.RefAlreadyExistsException;
import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.GpgSignatureVerifier.SignatureVerification;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.SignatureVerifier.SignatureVerification;
import org.eclipse.jgit.pgm.internal.CLIText;
import org.eclipse.jgit.pgm.internal.VerificationUtils;
import org.eclipse.jgit.revwalk.RevCommit;
@@ -106,7 +106,8 @@ protected void run() {
if (error != null) {
throw die(error.getMessage(), error);
}
- writeVerification(verifySig.getVerifier().getName(),
+ writeVerification(
+ verification.getVerification().verifierName(),
(RevTag) verification.getObject(),
verification.getVerification());
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java
index 2f96ef7..22d9e34 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java
@@ -18,8 +18,8 @@
import java.io.IOException;
import java.io.InputStreamReader;
import java.text.MessageFormat;
+import java.time.Instant;
import java.util.ArrayList;
-import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
@@ -166,7 +166,8 @@ private void recreateCommitGraph() throws IOException {
final CommitBuilder newc = new CommitBuilder();
newc.setTreeId(emptyTree);
- newc.setAuthor(new PersonIdent(me, new Date(t.commitTime)));
+ newc.setAuthor(new PersonIdent(me,
+ Instant.ofEpochSecond(t.commitTime)));
newc.setCommitter(newc.getAuthor());
newc.setParentIds(newParents);
newc.setMessage("ORIGINAL " + t.oldId.name() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowPackDelta.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowPackDelta.java
index c95f138..74e322f 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowPackDelta.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowPackDelta.java
@@ -31,6 +31,7 @@
import org.eclipse.jgit.pgm.TextBuiltin;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.pack.PackConfig;
import org.eclipse.jgit.util.TemporaryBuffer;
import org.kohsuke.args4j.Argument;
@@ -68,7 +69,7 @@ protected void run() throws Exception {
ObjectReuseAsIs asis = (ObjectReuseAsIs) reader;
ObjectToPack target = asis.newObjectToPack(obj, obj.getType());
- PackWriter pw = new PackWriter(reader) {
+ PackWriter pw = new PackWriter(new PackConfig(), reader) {
@Override
public void select(ObjectToPack otp, StoredObjectRepresentation next) {
otp.select(next);
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/WriteReftable.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/WriteReftable.java
index faa2bce..7aff2dd 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/WriteReftable.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/WriteReftable.java
@@ -24,6 +24,8 @@
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
+import java.time.Instant;
+import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -209,14 +211,15 @@ private static List<LogEntry> readLog(String logPath)
}
String ref = m.group(1);
double t = Double.parseDouble(m.group(2));
- long time = ((long) t) * 1000L;
+ Instant time = Instant.ofEpochSecond((long) t);
long index = (long) (t * 1e6);
String user = m.group(3);
ObjectId oldId = parseId(m.group(4));
ObjectId newId = parseId(m.group(5));
String msg = m.group(6);
String email = user + "@gerrit"; //$NON-NLS-1$
- PersonIdent who = new PersonIdent(user, email, time, -480);
+ PersonIdent who = new PersonIdent(user, email, time,
+ ZoneOffset.ofHours(-8));
log.add(new LogEntry(ref, index, who, oldId, newId, msg));
}
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java
index b5bf6d2..bb1e950 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2010, 2013 Sasa Zivkov <sasa.zivkov@sap.com>
- * Copyright (C) 2013, 2021 Obeo and others
+ * Copyright (C) 2013, 2025 Obeo and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -91,6 +91,7 @@ public static String fatalError(String message) {
}
// @formatter:off
+ /***/ public String addIncompatibleOptions;
/***/ public String alreadyOnBranch;
/***/ public String alreadyUpToDate;
/***/ public String answerNo;
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/VerificationUtils.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/VerificationUtils.java
index c1f8a86..64ee602 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/VerificationUtils.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/VerificationUtils.java
@@ -11,7 +11,7 @@
import java.io.IOException;
-import org.eclipse.jgit.lib.GpgSignatureVerifier.SignatureVerification;
+import org.eclipse.jgit.lib.SignatureVerifier.SignatureVerification;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.util.GitDateFormatter;
import org.eclipse.jgit.util.SignatureUtils;
diff --git a/org.eclipse.jgit.ssh.apache.agent/META-INF/MANIFEST.MF b/org.eclipse.jgit.ssh.apache.agent/META-INF/MANIFEST.MF
index ad01640..8942a41 100644
--- a/org.eclipse.jgit.ssh.apache.agent/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.apache.agent/META-INF/MANIFEST.MF
@@ -2,16 +2,16 @@
Bundle-ManifestVersion: 2
Bundle-Name: %Bundle-Name
Bundle-SymbolicName: org.eclipse.jgit.ssh.apache.agent;singleton:=true
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-Localization: OSGI-INF/l10n/agent
Bundle-Vendor: %Bundle-Vendor
-Fragment-Host: org.eclipse.jgit.ssh.apache;bundle-version="[7.0.0,7.1.0)"
+Fragment-Host: org.eclipse.jgit.ssh.apache;bundle-version="[7.3.0,7.4.0)"
Bundle-ActivationPolicy: lazy
Automatic-Module-Name: org.eclipse.jgit.ssh.apache.agent
Bundle-RequiredExecutionEnvironment: JavaSE-17
-Import-Package: org.eclipse.jgit.transport.sshd;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.nls;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)"
+Import-Package: org.eclipse.jgit.transport.sshd;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.nls;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.0)"
Require-Bundle: com.sun.jna;bundle-version="[5.8.0,6.0.0)",
com.sun.jna.platform;bundle-version="[5.8.0,6.0.0)"
-Export-Package: org.eclipse.jgit.internal.transport.sshd.agent.connector;version="7.0.0";x-internal:=true
+Export-Package: org.eclipse.jgit.internal.transport.sshd.agent.connector;version="7.3.0";x-internal:=true
diff --git a/org.eclipse.jgit.ssh.apache.agent/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.ssh.apache.agent/META-INF/SOURCE-MANIFEST.MF
index 684ab70..37b442e 100644
--- a/org.eclipse.jgit.ssh.apache.agent/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.apache.agent/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@
Bundle-Name: org.eclipse.jgit.ssh.apache.agent - Sources
Bundle-SymbolicName: org.eclipse.jgit.ssh.apache.agent.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.0.0.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache.agent;version="7.0.0.qualifier";roots="."
+Bundle-Version: 7.3.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache.agent;version="7.3.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.ssh.apache.agent/pom.xml b/org.eclipse.jgit.ssh.apache.agent/pom.xml
index f407ce1..2d34495 100644
--- a/org.eclipse.jgit.ssh.apache.agent/pom.xml
+++ b/org.eclipse.jgit.ssh.apache.agent/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.ssh.apache.agent</artifactId>
diff --git a/org.eclipse.jgit.ssh.apache.test/.classpath b/org.eclipse.jgit.ssh.apache.test/.classpath
index 6fdb99a..5be47af 100644
--- a/org.eclipse.jgit.ssh.apache.test/.classpath
+++ b/org.eclipse.jgit.ssh.apache.test/.classpath
@@ -11,5 +11,10 @@
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
+ <classpathentry kind="src" path="tst-rsrc">
+ <attributes>
+ <attribute name="test" value="true"/>
+ </attributes>
+ </classpathentry>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/org.eclipse.jgit.ssh.apache.test/.gitattributes b/org.eclipse.jgit.ssh.apache.test/.gitattributes
new file mode 100644
index 0000000..b5b9375
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/.gitattributes
@@ -0,0 +1,2 @@
+/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/repo.bundle binary
+/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl* binary
diff --git a/org.eclipse.jgit.ssh.apache.test/BUILD b/org.eclipse.jgit.ssh.apache.test/BUILD
index b384464..bf796c0 100644
--- a/org.eclipse.jgit.ssh.apache.test/BUILD
+++ b/org.eclipse.jgit.ssh.apache.test/BUILD
@@ -1,19 +1,51 @@
load(
+ "@com_googlesource_gerrit_bazlets//tools:genrule2.bzl",
+ "genrule2",
+)
+load(
"@com_googlesource_gerrit_bazlets//tools:junit.bzl",
"junit_tests",
)
+DEPS = [
+ "//lib:bcpkix",
+ "//lib:bcprov",
+ "//lib:bcutil",
+ "//lib:junit",
+ "//lib:slf4j-api",
+ "//lib:sshd-osgi",
+ "//lib:sshd-sftp",
+ "//org.eclipse.jgit:jgit",
+ "//org.eclipse.jgit.junit:junit",
+ "//org.eclipse.jgit.junit.ssh:junit-ssh",
+ "//org.eclipse.jgit.ssh.apache:ssh-apache",
+]
+
+HELPERS = ["tst/org/eclipse/jgit/internal/signing/ssh/AbstractSshSignatureTest.java"]
+
junit_tests(
name = "sshd_apache",
- srcs = glob(["tst/**/*.java"]),
+ srcs = glob(
+ ["tst/**/*.java"],
+ exclude = HELPERS,
+ ),
tags = ["sshd"],
- deps = [
- "//lib:eddsa",
- "//lib:junit",
- "//lib:sshd-osgi",
- "//lib:sshd-sftp",
- "//org.eclipse.jgit:jgit",
- "//org.eclipse.jgit.junit.ssh:junit-ssh",
- "//org.eclipse.jgit.ssh.apache:ssh-apache",
+ runtime_deps = [":tst_rsrc"],
+ deps = DEPS + [
+ ":helpers",
],
)
+
+java_library(
+ name = "helpers",
+ testonly = 1,
+ srcs = HELPERS,
+ deps = DEPS,
+)
+
+genrule2(
+ name = "tst_rsrc",
+ srcs = glob(["tst-rsrc/**"]),
+ outs = ["tst_rsrc.jar"],
+ cmd = "tar cf - $(SRCS) | tar -C $$TMP --strip-components=2 -xf - && cd $$TMP && zip -qr $$ROOT/$@ .",
+)
diff --git a/org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF
index 54d610a..88ab277 100644
--- a/org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.apache.test/META-INF/MANIFEST.MF
@@ -3,35 +3,47 @@
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.ssh.apache.test
Bundle-SymbolicName: org.eclipse.jgit.ssh.apache.test
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-Vendor: %Bundle-Vendor
Bundle-Localization: plugin
Bundle-RequiredExecutionEnvironment: JavaSE-17
Require-Bundle: org.hamcrest.core;bundle-version="[1.3.0,2.0.0)"
-Import-Package: org.apache.sshd.client.config.hosts;version="[2.12.0,2.13.0)",
- org.apache.sshd.common;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.auth;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.config.keys;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.helpers;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.kex;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.keyprovider;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.session;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.signature;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.util.net;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.util.security;version="[2.12.0,2.13.0)",
- org.apache.sshd.core;version="[2.12.0,2.13.0)",
- org.apache.sshd.server;version="[2.12.0,2.13.0)",
- org.apache.sshd.server.forward;version="[2.12.0,2.13.0)",
- org.eclipse.jgit.api;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.api.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.transport.sshd.proxy;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.junit;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.junit.ssh;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport.sshd;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport.sshd.agent;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)",
+Import-Package: org.apache.sshd.certificate;version="[2.15.0,2.16.0)",
+ org.apache.sshd.client.config.hosts;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.auth;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.cipher;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.config.keys;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.helpers;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.kex;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.keyprovider;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.session;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.signature;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.util.buffer;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.util.net;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.util.security;version="[2.15.0,2.16.0)",
+ org.apache.sshd.core;version="[2.15.0,2.16.0)",
+ org.apache.sshd.server;version="[2.15.0,2.16.0)",
+ org.apache.sshd.server.forward;version="[2.15.0,2.16.0)",
+ org.eclipse.jgit.annotations;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.api;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.api.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.signing.ssh;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.transport.sshd;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.transport.sshd.proxy;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.junit;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.junit.ssh;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.revwalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport.sshd;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport.sshd.agent;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.0)",
org.junit;version="[4.13,5.0.0)",
org.junit.experimental.theories;version="[4.13,5.0.0)",
- org.junit.runner;version="[4.13,5.0.0)"
+ org.junit.rules;version="[4.13.0,5.0.0)",
+ org.junit.runner;version="[4.13,5.0.0)",
+ org.junit.runners;version="[4.13.0,5.0.0)",
+ org.slf4j;version="[1.7.0,3.0.0)"
diff --git a/org.eclipse.jgit.ssh.apache.test/pom.xml b/org.eclipse.jgit.ssh.apache.test/pom.xml
index 967b781..b86a560 100644
--- a/org.eclipse.jgit.ssh.apache.test/pom.xml
+++ b/org.eclipse.jgit.ssh.apache.test/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.ssh.apache.test</artifactId>
@@ -91,6 +91,12 @@
<sourceDirectory>src/</sourceDirectory>
<testSourceDirectory>tst/</testSourceDirectory>
+ <testResources>
+ <testResource>
+ <directory>tst-rsrc/</directory>
+ </testResource>
+ </testResources>
+
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/allowed_signers b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/allowed_signers
new file mode 100644
index 0000000..ec74409
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/allowed_signers
@@ -0,0 +1,2 @@
+tester@example.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO
+*@example.com cert-authority ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMdEl+iOTEbf1RC3uicECtid+SaIMsAw7wrlWhOQTyBV
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/ca_key b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/ca_key
new file mode 100644
index 0000000..b8de8c3
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/ca_key
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACDHRJfojkxG39UQt7onBArYnfkmiDLAMO8K5VoTkE8gVQAAAJAhCMgzIQjI
+MwAAAAtzc2gtZWQyNTUxOQAAACDHRJfojkxG39UQt7onBArYnfkmiDLAMO8K5VoTkE8gVQ
+AAAEBmcXpast20+B4IzA0Xex2CKYiiWJj3NFJ5F0kil113vcdEl+iOTEbf1RC3uicECtid
++SaIMsAw7wrlWhOQTyBVAAAADVRIV09AU0VBR044MDA=
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/ca_key.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/ca_key.pub
new file mode 100644
index 0000000..842415b
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/ca_key.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMdEl+iOTEbf1RC3uicECtid+SaIMsAw7wrlWhOQTyBV
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/ca_key2 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/ca_key2
new file mode 100644
index 0000000..a4af047
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/ca_key2
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACA7S4ycIB6oTx4UN8l9N+u016UgMzkrbT7E+2XbG35jgwAAAJAa2jfBGto3
+wQAAAAtzc2gtZWQyNTUxOQAAACA7S4ycIB6oTx4UN8l9N+u016UgMzkrbT7E+2XbG35jgw
+AAAEBothGMqFaA5aTO8MLx9wm1oDRfzQCSsu7uJwrOiUFTTTtLjJwgHqhPHhQ3yX0367TX
+pSAzOSttPsT7ZdsbfmODAAAADVRIV09AU0VBR044MDA=
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/ca_key2.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/ca_key2.pub
new file mode 100644
index 0000000..e46c87e
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/ca_key2.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDtLjJwgHqhPHhQ3yX0367TXpSAzOSttPsT7ZdsbfmOD
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/expired.cert b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/expired.cert
new file mode 100644
index 0000000..9da63ec
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/expired.cert
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAINFZ5NKywAWh1G1P6BiBKArmYKs1BDhJBOawJKlS29VXAAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzOAAAAAAAAAAEAAAABAAAADGV4cGlyZWRfY2VydAAAAAAAAAAAZtOugAAAAABm1QAAAAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAADMAAAALc3NoLWVkMjU1MTkAAAAgx0SX6I5MRt/VELe6JwQK2J35JogywDDvCuVaE5BPIFUAAABTAAAAC3NzaC1lZDI1NTE5AAAAQNf8i5dhRqWRe06epIRrZ5V+QZHq3ZrlJtlx98UJya9GAeCrJ5oHwBjr5O5TL5wNJS5Hz+T1qsJNFU9d1wdcuwI=
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/no_principals.cert b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/no_principals.cert
new file mode 100644
index 0000000..101e374
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/no_principals.cert
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAILzuED1RSloB/enTghTEKSACVOuEARP0f8UVXSRwEXN6AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzOAAAAAAAAAAIAAAABAAAADW5vX3ByaW5jaXBhbHMAAAAAAAAAAGbTroAAAAAAZyLIgAAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIMdEl+iOTEbf1RC3uicECtid+SaIMsAw7wrlWhOQTyBVAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEBwEQ2D0OHn4QDHnsINlgWUWpmhukseQCJu3Adulz28fFtewp1LLqkBy50wR6vJe1ifYbY4hzReXOSyoTmHSXEN
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/other-ca.cert b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/other-ca.cert
new file mode 100644
index 0000000..752fee1
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/other-ca.cert
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIHNW2bzSS61lvgHippv3Ymx4cVEAXBVCb8lFXHnVpsSyAAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzOAAAAAAAAAAYAAAABAAAAB3Rlc3RlcjIAAAAWAAAAEnRlc3RlckBleGFtcGxlLmNvbQAAAABm066AAAAAAGciyIAAAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACA7S4ycIB6oTx4UN8l9N+u016UgMzkrbT7E+2XbG35jgwAAAFMAAAALc3NoLWVkMjU1MTkAAABAuJ8zBazcaYTbUEr9QtoYox0MkVBg+8LANxJxc885M2vmg9yPHpTfV/emupqhBwuYcPJSskTxl7WX4TUNvhMsAA==
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/other.cert b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/other.cert
new file mode 100644
index 0000000..15825f6
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/other.cert
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIGKXzyrvDzj9ObQ4SuzqytK6nomOV8DhgdzODfWuup1sAAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzOAAAAAAAAAAQAAAABAAAABW90aGVyAAAAFQAAABFvdGhlckBleGFtcGxlLmNvbQAAAABm066AAAAAAGciyIAAAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACDHRJfojkxG39UQt7onBArYnfkmiDLAMO8K5VoTkE8gVQAAAFMAAAALc3NoLWVkMjU1MTkAAABA1ycFqWehyC6pIISEkXSTtHbatLWl9HHAoUFouQiDdubAnMDRSkyHipXR62rq+8yEAvtqm1mXBzO8nLalkF9xAA==
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/tester.cert b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/tester.cert
new file mode 100644
index 0000000..a2b241c
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/tester.cert
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAICSl1xsyTWb23YlKo21musxOzj4L4eD2coTkHbBw2uOyAAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzOAAAAAAAAAAMAAAABAAAABnRlc3RlcgAAABYAAAASdGVzdGVyQGV4YW1wbGUuY29tAAAAAGbTroAAAAAAZyLIgAAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIMdEl+iOTEbf1RC3uicECtid+SaIMsAw7wrlWhOQTyBVAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEDyjzq/0Egm1OxwrvqPZKUihE3w357Ji9Nd3j7VnUuvSYTXAdB9P0E+a2hyCcemmsil1MsvWTiCSSOsrHVB6FEO
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/two_principals.cert b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/two_principals.cert
new file mode 100644
index 0000000..5f7164a
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/certs/two_principals.cert
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIFmWKr9gNSQT0vna7k3uOyUF9CTcMGw2zxTFBf2Ev8TzAAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzOAAAAAAAAAAgAAAABAAAABnRlc3RlcgAAACkAAAAPZm9vQGV4YW1wbGUuY29tAAAAEnRlc3RlckBleGFtcGxlLmNvbQAAAABm066AAAAAAGciyIAAAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACDHRJfojkxG39UQt7onBArYnfkmiDLAMO8K5VoTkE8gVQAAAFMAAAALc3NoLWVkMjU1MTkAAABAqlSX2GzLz5U+hN/gF9UUyAkE6h5BgVFYhsyf1MR/B7Hoxa29wGLbJpUplrqEHMxoud2zfH2Nhj00unc3lr5bBA== ./signing_key.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl
new file mode 100644
index 0000000..9469340
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl
Binary files differ
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-all b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-all
new file mode 100644
index 0000000..6f744c3
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-all
Binary files differ
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-ca b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-ca
new file mode 100644
index 0000000..84a8bc6
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-ca
Binary files differ
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-cert b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-cert
new file mode 100644
index 0000000..26f29b2
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-cert
Binary files differ
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-empty b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-empty
new file mode 100644
index 0000000..78e5187
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-empty
Binary files differ
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-hash b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-hash
new file mode 100644
index 0000000..cdd1351
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-hash
Binary files differ
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-keyid b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-keyid
new file mode 100644
index 0000000..1a65243
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-keyid
Binary files differ
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-keyid-wild b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-keyid-wild
new file mode 100644
index 0000000..9ba549f
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-keyid-wild
Binary files differ
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-keys b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-keys
new file mode 100644
index 0000000..8dd496d
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-keys
Binary files differ
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-serial b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-serial
new file mode 100644
index 0000000..9965e2e
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-serial
Binary files differ
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-serial-wild b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-serial-wild
new file mode 100644
index 0000000..aefd2b1
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-serial-wild
Binary files differ
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-sha1 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-sha1
new file mode 100644
index 0000000..3928543
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-sha1
Binary files differ
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-sha256 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-sha256
new file mode 100644
index 0000000..cdd1351
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-sha256
Binary files differ
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-text b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-text
new file mode 100644
index 0000000..77ddd5e
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/krl-text
@@ -0,0 +1,11 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC29QZ72HtyCdaNo6p2GH3fJpUynwkvs8Acwn66G7YTh
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL0ZC4fqgbBgROeX1sOEPr4uMVNfPdJ62bVo/zvSMRQx
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG5IH9T7FY47VJDUoyOlB/iqCN4pO8dgOrxclmKN5R5w
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINjhG4+EnQy8YHLsfE8+IQwNWZVn1GBYX75pwxBCZGmy
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJXm+qgTs+sO+9zvoZBxkQD39R2rQqQCVezxQoGjKui5
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL8+aQKja+SbqxfWR61FCcsbBw2jaF/KHvcqdP2Fbp6Q
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAOL7IULalT2Izo9TgRf1t2HNpZ5WCZJH5oRCd9LK3BN
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGVaSedhPkl2Hrx1nOOKT2E52ADsBebawws87NN1+P6e
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICxyaxj7pRVDeh/gxJem9BLhoUQKGnKXHfDrB/GtC1KB
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID96GKtj4wN+OwvrQsgP37fQVUXThCML796qqFNLVDCA
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEh3BFcTzXmg1Fi5LvWiUDWORsHzVhUCm8ekrEJG6+6A
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0001 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0001
new file mode 100644
index 0000000..893fd5e
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0001
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACAtvUGe9h7cgnWjaOqdhh93yaVMp8JL7PAHMJ+uhu2E4QAAAIhUa4KiVGuC
+ogAAAAtzc2gtZWQyNTUxOQAAACAtvUGe9h7cgnWjaOqdhh93yaVMp8JL7PAHMJ+uhu2E4Q
+AAAECKgy+3FBgpdfxjOtNy9TamhadMWSyPlPiwu06mYVReyS29QZ72HtyCdaNo6p2GH3fJ
+pUynwkvs8Acwn66G7YThAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0001-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0001-cert.pub
new file mode 100644
index 0000000..e2bcd25
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0001-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIE1UUsQ+sncsuST6eGe3B5Se7purqhGcWrkyIwUnQM/jAAAAIC29QZ72HtyCdaNo6p2GH3fJpUynwkvs8Acwn66G7YThAAAAAAAAAAEAAAABAAAACXJldm9rZWQgMQAAAAAAAAAAAAAAAP//////////AAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAADMAAAALc3NoLWVkMjU1MTkAAAAgFtF5l68spzOFrIpQANF0C9nkNdXMAmlCRwHgw91C784AAABTAAAAC3NzaC1lZDI1NTE5AAAAQCjDATJVQs3odl9fsqaxyx/18qrodZEDyYZAsdqg0GMx8CvLYt4xHENyVm7kyBRxOeh3EKfII0WFoYCV4mGZ/wU= ./tst-keys/revoked-0001.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0001.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0001.pub
new file mode 100644
index 0000000..f561982
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0001.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC29QZ72HtyCdaNo6p2GH3fJpUynwkvs8Acwn66G7YTh
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0004 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0004
new file mode 100644
index 0000000..e50a4fe
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0004
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACC9GQuH6oGwYETnl9bDhD6+LjFTXz3Setm1aP870jEUMQAAAIiQzZzikM2c
+4gAAAAtzc2gtZWQyNTUxOQAAACC9GQuH6oGwYETnl9bDhD6+LjFTXz3Setm1aP870jEUMQ
+AAAEBpn5dxbvHhqAsSVN3IqRwzbFFgOhdmpkOP+nvoKq+rSr0ZC4fqgbBgROeX1sOEPr4u
+MVNfPdJ62bVo/zvSMRQxAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0004-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0004-cert.pub
new file mode 100644
index 0000000..8e92fa7
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0004-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIC5jMLPDlEVbyPU/Icb04BF5jxN+OT8kpuO5c0CV6/AYAAAAIL0ZC4fqgbBgROeX1sOEPr4uMVNfPdJ62bVo/zvSMRQxAAAAAAAAAAQAAAABAAAACXJldm9rZWQgNAAAAAAAAAAAAAAAAP//////////AAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAADMAAAALc3NoLWVkMjU1MTkAAAAgFtF5l68spzOFrIpQANF0C9nkNdXMAmlCRwHgw91C784AAABTAAAAC3NzaC1lZDI1NTE5AAAAQOH4yNn7+zyvsCV8BCoop5xYv4uFk27VZRjmscuy3J66KNwLay9XkvkRNArDaWBwH47dmkcU7F6fLLpY4vN2jgM= ./tst-keys/revoked-0004.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0004.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0004.pub
new file mode 100644
index 0000000..1d7fe7f
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0004.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL0ZC4fqgbBgROeX1sOEPr4uMVNfPdJ62bVo/zvSMRQx
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0010 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0010
new file mode 100644
index 0000000..fb457df
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0010
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACBuSB/U+xWOO1SQ1KMjpQf4qgjeKTvHYDq8XJZijeUecAAAAIgvtSiML7Uo
+jAAAAAtzc2gtZWQyNTUxOQAAACBuSB/U+xWOO1SQ1KMjpQf4qgjeKTvHYDq8XJZijeUecA
+AAAECI2si7/SGjMM1UyhrFPXx4laQIfFUsb1+yfXKwQyeOXW5IH9T7FY47VJDUoyOlB/iq
+CN4pO8dgOrxclmKN5R5wAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0010-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0010-cert.pub
new file mode 100644
index 0000000..9492f88
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0010-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIN2arXaBzVIdxAFfU+XU1Uc788HKlDH3tOLdDtcoORLmAAAAIG5IH9T7FY47VJDUoyOlB/iqCN4pO8dgOrxclmKN5R5wAAAAAAAAAAoAAAABAAAACnJldm9rZWQgMTAAAAAAAAAAAAAAAAD//////////wAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIBbReZevLKczhayKUADRdAvZ5DXVzAJpQkcB4MPdQu/OAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEDwhgQsYOG/eKf8EfH+fAmEW+88/ZJCmxAExEFPxkGL59waZcGiOJqquTKiqN5Kod8hpUrvZywrA0tjrRkYw8wH ./tst-keys/revoked-0010.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0010.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0010.pub
new file mode 100644
index 0000000..37a0d84
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0010.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG5IH9T7FY47VJDUoyOlB/iqCN4pO8dgOrxclmKN5R5w
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0050 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0050
new file mode 100644
index 0000000..b02e9df
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0050
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACDY4RuPhJ0MvGBy7HxPPiEMDVmVZ9RgWF++acMQQmRpsgAAAIgCZLe5AmS3
+uQAAAAtzc2gtZWQyNTUxOQAAACDY4RuPhJ0MvGBy7HxPPiEMDVmVZ9RgWF++acMQQmRpsg
+AAAEB9Q6rpWK04mQDoeKSB2I7p/rb8pu00ClhR+vRATl4TYdjhG4+EnQy8YHLsfE8+IQwN
+WZVn1GBYX75pwxBCZGmyAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0050-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0050-cert.pub
new file mode 100644
index 0000000..90bb86f
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0050-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIIecNj2Es6VfyCrhol4swP9lutvphd3seh+/b2LpD0EsAAAAINjhG4+EnQy8YHLsfE8+IQwNWZVn1GBYX75pwxBCZGmyAAAAAAAAADIAAAABAAAACnJldm9rZWQgNTAAAAAAAAAAAAAAAAD//////////wAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIBbReZevLKczhayKUADRdAvZ5DXVzAJpQkcB4MPdQu/OAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEA2q8tCXV8FXkB0QWnFNWfCL7zz5jCXL9ZQADM1DaGi8oUU/dxmlQtWgMxuu5vNuvOYQGPDcBLj+by8VqAdvZMP ./tst-keys/revoked-0050.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0050.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0050.pub
new file mode 100644
index 0000000..f3ad249
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0050.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINjhG4+EnQy8YHLsfE8+IQwNWZVn1GBYX75pwxBCZGmy
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0090 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0090
new file mode 100644
index 0000000..efa3d5e
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0090
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACCV5vqoE7PrDvvc76GQcZEA9/Udq0KkAlXs8UKBoyrouQAAAIg3mgznN5oM
+5wAAAAtzc2gtZWQyNTUxOQAAACCV5vqoE7PrDvvc76GQcZEA9/Udq0KkAlXs8UKBoyrouQ
+AAAEAkRynGUH9n5hcp/S1WALvuIEDtbkMi2A7yNWze0o4gWpXm+qgTs+sO+9zvoZBxkQD3
+9R2rQqQCVezxQoGjKui5AAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0090-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0090-cert.pub
new file mode 100644
index 0000000..26e61e0
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0090-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIOjIztpPiaKY0hztHWtWpX+4LEoyy8qYPPT277K3bykSAAAAIJXm+qgTs+sO+9zvoZBxkQD39R2rQqQCVezxQoGjKui5AAAAAAAAAFoAAAABAAAACnJldm9rZWQgOTAAAAAAAAAAAAAAAAD//////////wAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIBbReZevLKczhayKUADRdAvZ5DXVzAJpQkcB4MPdQu/OAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEBUaAWyv/jZrbrCO5zw2HuZcWYBig8R2jdvkKr5yzWMWEVRtn97gnAUsIGxkgUnUAs3B2En2FH2NaicC1F1n3sF ./tst-keys/revoked-0090.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0090.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0090.pub
new file mode 100644
index 0000000..e51b88c
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0090.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJXm+qgTs+sO+9zvoZBxkQD39R2rQqQCVezxQoGjKui5
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0500 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0500
new file mode 100644
index 0000000..900d444
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0500
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACC/PmkCo2vkm6sX1ketRQnLGwcNo2hfyh73KnT9hW6ekAAAAIhDam0PQ2pt
+DwAAAAtzc2gtZWQyNTUxOQAAACC/PmkCo2vkm6sX1ketRQnLGwcNo2hfyh73KnT9hW6ekA
+AAAED606GrYWlY7TOXcr8vAr3fjMtCtetdpwFHi2pzgf2Bbb8+aQKja+SbqxfWR61FCcsb
+Bw2jaF/KHvcqdP2Fbp6QAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0500-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0500-cert.pub
new file mode 100644
index 0000000..0709618
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0500-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIIEblAg4b1eJ5KnT7KvYoOfe24La+nAKKLIYdsR6CdreAAAAIL8+aQKja+SbqxfWR61FCcsbBw2jaF/KHvcqdP2Fbp6QAAAAAAAAAfQAAAABAAAAC3Jldm9rZWQgNTAwAAAAAAAAAAAAAAAA//////////8AAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACAW0XmXryynM4WsilAA0XQL2eQ11cwCaUJHAeDD3ULvzgAAAFMAAAALc3NoLWVkMjU1MTkAAABAc0WEuRfi9LG9uTfKY4Dh5MJCHUG7Dqp1J4S4Gs1iOzFX2YKgYXc0O+9j3jJ5/fB4z960Y1AxYR4TWEo1pNjzBQ== ./tst-keys/revoked-0500.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0500.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0500.pub
new file mode 100644
index 0000000..13d1aa4
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0500.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL8+aQKja+SbqxfWR61FCcsbBw2jaF/KHvcqdP2Fbp6Q
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0510 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0510
new file mode 100644
index 0000000..a58675e
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0510
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACADi+yFC2pU9iM6PU4EX9bdhzaWeVgmSR+aEQnfSytwTQAAAIgigF2AIoBd
+gAAAAAtzc2gtZWQyNTUxOQAAACADi+yFC2pU9iM6PU4EX9bdhzaWeVgmSR+aEQnfSytwTQ
+AAAEBWpyFpK0a+cdNPFMsvHTHtjBJpX4aMHxBAcEPN8hnpWAOL7IULalT2Izo9TgRf1t2H
+NpZ5WCZJH5oRCd9LK3BNAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0510-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0510-cert.pub
new file mode 100644
index 0000000..1431af3
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0510-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAII8u8ho0YtDyXWYKv4WeOXSaRUxU8sUV0dQujB2J9VLaAAAAIAOL7IULalT2Izo9TgRf1t2HNpZ5WCZJH5oRCd9LK3BNAAAAAAAAAf4AAAABAAAAC3Jldm9rZWQgNTEwAAAAAAAAAAAAAAAA//////////8AAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACAW0XmXryynM4WsilAA0XQL2eQ11cwCaUJHAeDD3ULvzgAAAFMAAAALc3NoLWVkMjU1MTkAAABA3aijJnt8mJ8vLtr7H2PBVJHtNJpL6MQZNXHC6svzygIqZwEq3tDHGR00TPHaCYAqDEXQZysONciOQtQHzKXuBw== ./tst-keys/revoked-0510.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0510.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0510.pub
new file mode 100644
index 0000000..33ad644
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0510.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAOL7IULalT2Izo9TgRf1t2HNpZ5WCZJH5oRCd9LK3BN
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0520 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0520
new file mode 100644
index 0000000..630316c
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0520
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACBlWknnYT5Jdh68dZzjik9hOdgA7AXm2sMLPOzTdfj+ngAAAIghEm1OIRJt
+TgAAAAtzc2gtZWQyNTUxOQAAACBlWknnYT5Jdh68dZzjik9hOdgA7AXm2sMLPOzTdfj+ng
+AAAEDfVYURudvfzK3ZFx6T2O1CWi0emOZ0MYPcDzUVlu1WmGVaSedhPkl2Hrx1nOOKT2E5
+2ADsBebawws87NN1+P6eAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0520-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0520-cert.pub
new file mode 100644
index 0000000..b290943
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0520-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAID/r9T2Sv0NGmlcHl6Fw8rVPIupmsqwq3WAG1NvW7WRcAAAAIGVaSedhPkl2Hrx1nOOKT2E52ADsBebawws87NN1+P6eAAAAAAAAAggAAAABAAAAC3Jldm9rZWQgNTIwAAAAAAAAAAAAAAAA//////////8AAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACAW0XmXryynM4WsilAA0XQL2eQ11cwCaUJHAeDD3ULvzgAAAFMAAAALc3NoLWVkMjU1MTkAAABAF8zkeAqwtlxF4iy4mDEHkzVaRqcS0sZ57gcZBWGn/peGFy3MpSxlFQM/IC2pNZ7GuCVSIPV6rRLJC65YMMOEDQ== ./tst-keys/revoked-0520.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0520.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0520.pub
new file mode 100644
index 0000000..fc13d37
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0520.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGVaSedhPkl2Hrx1nOOKT2E52ADsBebawws87NN1+P6e
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0550 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0550
new file mode 100644
index 0000000..5e671b4
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0550
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACAscmsY+6UVQ3of4MSXpvQS4aFEChpylx3w6wfxrQtSgQAAAIj/9GKZ//Ri
+mQAAAAtzc2gtZWQyNTUxOQAAACAscmsY+6UVQ3of4MSXpvQS4aFEChpylx3w6wfxrQtSgQ
+AAAEDKC3eEgvCMy86rktq7VU1YQjjKY1iDFPVxWgKKcGJKkyxyaxj7pRVDeh/gxJem9BLh
+oUQKGnKXHfDrB/GtC1KBAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0550-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0550-cert.pub
new file mode 100644
index 0000000..f529a91
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0550-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIF9q+Cg+9DSKt09eW1NXqVC4dZ3v80sZIYtc0/yqHRb+AAAAICxyaxj7pRVDeh/gxJem9BLhoUQKGnKXHfDrB/GtC1KBAAAAAAAAAiYAAAABAAAAC3Jldm9rZWQgNTUwAAAAAAAAAAAAAAAA//////////8AAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACAW0XmXryynM4WsilAA0XQL2eQ11cwCaUJHAeDD3ULvzgAAAFMAAAALc3NoLWVkMjU1MTkAAABAovTuFOXLNCc4hQcI2hatXe2hbBQYbcnUo2BNdJ9EvIOsH/T0DzzEfRQajMQ+QD6oujIx7fb1Z2sRVPOAb3AcBg== ./tst-keys/revoked-0550.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0550.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0550.pub
new file mode 100644
index 0000000..e09316a
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0550.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICxyaxj7pRVDeh/gxJem9BLhoUQKGnKXHfDrB/GtC1KB
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0799 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0799
new file mode 100644
index 0000000..8edd736
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0799
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACA/ehirY+MDfjsL60LID9+30FVF04QjC+/eqqhTS1QwgAAAAIjdntzR3Z7c
+0QAAAAtzc2gtZWQyNTUxOQAAACA/ehirY+MDfjsL60LID9+30FVF04QjC+/eqqhTS1QwgA
+AAAEDQEb+IFCIz+yvkhmrOQ85GafOm9ra0oNRontpox62UTj96GKtj4wN+OwvrQsgP37fQ
+VUXThCML796qqFNLVDCAAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0799-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0799-cert.pub
new file mode 100644
index 0000000..80312fb
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0799-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIC1z1LkrZhMz1mBWPU8sJIuH59v+ig4OK/B4/x8jLAtUAAAAID96GKtj4wN+OwvrQsgP37fQVUXThCML796qqFNLVDCAAAAAAAAAAx8AAAABAAAAC3Jldm9rZWQgNzk5AAAAAAAAAAAAAAAA//////////8AAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACAW0XmXryynM4WsilAA0XQL2eQ11cwCaUJHAeDD3ULvzgAAAFMAAAALc3NoLWVkMjU1MTkAAABASNkJSbdRDARfgbqPOnuES0o6m6VZ7RC2XLPm3uwTqCvMqtHbFvq9etMddSUIR4XXah6ef+O7CJDk/Yjpkn+2CA== ./tst-keys/revoked-0799.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0799.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0799.pub
new file mode 100644
index 0000000..1f0556c
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0799.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID96GKtj4wN+OwvrQsgP37fQVUXThCML796qqFNLVDCA
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0999 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0999
new file mode 100644
index 0000000..f05a1e4
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0999
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACBIdwRXE815oNRYuS71olA1jkbB81YVApvHpKxCRuvugAAAAIgzBpObMwaT
+mwAAAAtzc2gtZWQyNTUxOQAAACBIdwRXE815oNRYuS71olA1jkbB81YVApvHpKxCRuvugA
+AAAECxY5wx3XKIhMT+ajMZXPl51x8rkCPBq6gUgZV3Uqpu7Eh3BFcTzXmg1Fi5LvWiUDWO
+RsHzVhUCm8ekrEJG6+6AAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0999-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0999-cert.pub
new file mode 100644
index 0000000..4aedb77
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0999-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIGt3nV/XJmtz9sQGP2fiZiKOH7mkPhezN3S+8TnsVcQjAAAAIEh3BFcTzXmg1Fi5LvWiUDWORsHzVhUCm8ekrEJG6+6AAAAAAAAAA+cAAAABAAAAC3Jldm9rZWQgOTk5AAAAAAAAAAAAAAAA//////////8AAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACAW0XmXryynM4WsilAA0XQL2eQ11cwCaUJHAeDD3ULvzgAAAFMAAAALc3NoLWVkMjU1MTkAAABAvLVCRCs7CV0JSXYL8ge4iRxL4y48bYuvu3YimKZDg7NdCXqw/jkaCsxJykRzb/xVnQDoNVCQQuzydt/I13FdBA== ./tst-keys/revoked-0999.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0999.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0999.pub
new file mode 100644
index 0000000..c837fe0
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-0999.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEh3BFcTzXmg1Fi5LvWiUDWORsHzVhUCm8ekrEJG6+6A
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-ca b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-ca
new file mode 100644
index 0000000..47e01fb
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-ca
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACAW0XmXryynM4WsilAA0XQL2eQ11cwCaUJHAeDD3ULvzgAAAIgok4I2KJOC
+NgAAAAtzc2gtZWQyNTUxOQAAACAW0XmXryynM4WsilAA0XQL2eQ11cwCaUJHAeDD3ULvzg
+AAAEAEN+knz2qOyj+jbY+SJSHYQhlJoB1u9jLqoQoiAerI3hbReZevLKczhayKUADRdAvZ
+5DXVzAJpQkcB4MPdQu/OAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-ca.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-ca.pub
new file mode 100644
index 0000000..2b92f89
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-ca.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBbReZevLKczhayKUADRdAvZ5DXVzAJpQkcB4MPdQu/O
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-ca2 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-ca2
new file mode 100644
index 0000000..770ceee
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-ca2
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACDsEBCX5jBwggkpt4XZXct1fOhDBuvgLL0KMpGoHRtj9wAAAIjMEwOtzBMD
+rQAAAAtzc2gtZWQyNTUxOQAAACDsEBCX5jBwggkpt4XZXct1fOhDBuvgLL0KMpGoHRtj9w
+AAAEAurE2/d7VhoEJeNFdDnVS7lpBRoMe/zAjA8dJRP1Z/I+wQEJfmMHCCCSm3hdldy3V8
+6EMG6+AsvQoykagdG2P3AAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-ca2.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-ca2.pub
new file mode 100644
index 0000000..a177fd0
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-ca2.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOwQEJfmMHCCCSm3hdldy3V86EMG6+AsvQoykagdG2P3
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-hash b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-hash
new file mode 100644
index 0000000..c6f2361
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-hash
@@ -0,0 +1,11 @@
+hash: SHA256:RvNFBEc/N9jsm3toDkitgr/wnWu/6qWBHo4Xmh5ZUpM
+hash: SHA256:qu2IwCnItWWX+orXv0rjCeT4i++2O6ViTzLye6kyWzU
+hash: SHA256:qQTACAkAJxYk1zvSQ+Rx9wa2IuOFJKtaEy/XwxM89J0
+hash: SHA256:Fe4GdmipzulS9oMB/h3U69tSm5wil6bTUKSJCT+Jf3E
+hash: SHA256:esUK/whZ5oJeRFNeOrHK1bbx9dKC+nRITZ7up7HJaGA
+hash: SHA256:xkii+r6t9rEBFYkx1b3dGNXzEs69M5NUMfHP05ypSdI
+hash: SHA256:lZrSycKcBNvUafU9y4R0EEbDaQWqMFvIGM9M+VKt2zk
+hash: SHA256:/2bgZOiYEH2UVahUllNaQ5P0advEB7liCPkp+aNVKDk
+hash: SHA256:He3c0W5o/P1I0pK5/VusqD5V6duAMeZl6f+6Yy5P1z0
+hash: SHA256:5V5Xw2lgcAGR8dO9cbgRmCNlhcCsBBv/hmEstKsqKr4
+hash: SHA256:T7s26JPzzRP2WHOcw3OjLwWo8ZZTkfo2jBCrRfJ6BR4
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-keyid b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-keyid
new file mode 100644
index 0000000..592ddb4
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-keyid
@@ -0,0 +1,512 @@
+id: revoked 1
+id: revoked 2
+id: revoked 3
+id: revoked 4
+id: revoked 10
+id: revoked 15
+id: revoked 30
+id: revoked 50
+id: revoked 90
+id: revoked 300
+id: revoked 301
+id: revoked 302
+id: revoked 303
+id: revoked 304
+id: revoked 305
+id: revoked 306
+id: revoked 307
+id: revoked 308
+id: revoked 309
+id: revoked 310
+id: revoked 311
+id: revoked 312
+id: revoked 313
+id: revoked 314
+id: revoked 315
+id: revoked 316
+id: revoked 317
+id: revoked 318
+id: revoked 319
+id: revoked 320
+id: revoked 321
+id: revoked 322
+id: revoked 323
+id: revoked 324
+id: revoked 325
+id: revoked 326
+id: revoked 327
+id: revoked 328
+id: revoked 329
+id: revoked 330
+id: revoked 331
+id: revoked 332
+id: revoked 333
+id: revoked 334
+id: revoked 335
+id: revoked 336
+id: revoked 337
+id: revoked 338
+id: revoked 339
+id: revoked 340
+id: revoked 341
+id: revoked 342
+id: revoked 343
+id: revoked 344
+id: revoked 345
+id: revoked 346
+id: revoked 347
+id: revoked 348
+id: revoked 349
+id: revoked 350
+id: revoked 351
+id: revoked 352
+id: revoked 353
+id: revoked 354
+id: revoked 355
+id: revoked 356
+id: revoked 357
+id: revoked 358
+id: revoked 359
+id: revoked 360
+id: revoked 361
+id: revoked 362
+id: revoked 363
+id: revoked 364
+id: revoked 365
+id: revoked 366
+id: revoked 367
+id: revoked 368
+id: revoked 369
+id: revoked 370
+id: revoked 371
+id: revoked 372
+id: revoked 373
+id: revoked 374
+id: revoked 375
+id: revoked 376
+id: revoked 377
+id: revoked 378
+id: revoked 379
+id: revoked 380
+id: revoked 381
+id: revoked 382
+id: revoked 383
+id: revoked 384
+id: revoked 385
+id: revoked 386
+id: revoked 387
+id: revoked 388
+id: revoked 389
+id: revoked 390
+id: revoked 391
+id: revoked 392
+id: revoked 393
+id: revoked 394
+id: revoked 395
+id: revoked 396
+id: revoked 397
+id: revoked 398
+id: revoked 399
+id: revoked 400
+id: revoked 401
+id: revoked 402
+id: revoked 403
+id: revoked 404
+id: revoked 405
+id: revoked 406
+id: revoked 407
+id: revoked 408
+id: revoked 409
+id: revoked 410
+id: revoked 411
+id: revoked 412
+id: revoked 413
+id: revoked 414
+id: revoked 415
+id: revoked 416
+id: revoked 417
+id: revoked 418
+id: revoked 419
+id: revoked 420
+id: revoked 421
+id: revoked 422
+id: revoked 423
+id: revoked 424
+id: revoked 425
+id: revoked 426
+id: revoked 427
+id: revoked 428
+id: revoked 429
+id: revoked 430
+id: revoked 431
+id: revoked 432
+id: revoked 433
+id: revoked 434
+id: revoked 435
+id: revoked 436
+id: revoked 437
+id: revoked 438
+id: revoked 439
+id: revoked 440
+id: revoked 441
+id: revoked 442
+id: revoked 443
+id: revoked 444
+id: revoked 445
+id: revoked 446
+id: revoked 447
+id: revoked 448
+id: revoked 449
+id: revoked 450
+id: revoked 451
+id: revoked 452
+id: revoked 453
+id: revoked 454
+id: revoked 455
+id: revoked 456
+id: revoked 457
+id: revoked 458
+id: revoked 459
+id: revoked 460
+id: revoked 461
+id: revoked 462
+id: revoked 463
+id: revoked 464
+id: revoked 465
+id: revoked 466
+id: revoked 467
+id: revoked 468
+id: revoked 469
+id: revoked 470
+id: revoked 471
+id: revoked 472
+id: revoked 473
+id: revoked 474
+id: revoked 475
+id: revoked 476
+id: revoked 477
+id: revoked 478
+id: revoked 479
+id: revoked 480
+id: revoked 481
+id: revoked 482
+id: revoked 483
+id: revoked 484
+id: revoked 485
+id: revoked 486
+id: revoked 487
+id: revoked 488
+id: revoked 489
+id: revoked 490
+id: revoked 491
+id: revoked 492
+id: revoked 493
+id: revoked 494
+id: revoked 495
+id: revoked 496
+id: revoked 497
+id: revoked 498
+id: revoked 500
+id: revoked 501
+id: revoked 502
+id: revoked 503
+id: revoked 504
+id: revoked 505
+id: revoked 506
+id: revoked 507
+id: revoked 508
+id: revoked 509
+id: revoked 510
+id: revoked 511
+id: revoked 512
+id: revoked 513
+id: revoked 514
+id: revoked 515
+id: revoked 516
+id: revoked 517
+id: revoked 518
+id: revoked 519
+id: revoked 520
+id: revoked 521
+id: revoked 522
+id: revoked 523
+id: revoked 524
+id: revoked 525
+id: revoked 526
+id: revoked 527
+id: revoked 528
+id: revoked 529
+id: revoked 530
+id: revoked 531
+id: revoked 532
+id: revoked 533
+id: revoked 534
+id: revoked 535
+id: revoked 536
+id: revoked 537
+id: revoked 538
+id: revoked 539
+id: revoked 540
+id: revoked 541
+id: revoked 542
+id: revoked 543
+id: revoked 544
+id: revoked 545
+id: revoked 546
+id: revoked 547
+id: revoked 548
+id: revoked 549
+id: revoked 550
+id: revoked 551
+id: revoked 552
+id: revoked 553
+id: revoked 554
+id: revoked 555
+id: revoked 556
+id: revoked 557
+id: revoked 558
+id: revoked 559
+id: revoked 560
+id: revoked 561
+id: revoked 562
+id: revoked 563
+id: revoked 564
+id: revoked 565
+id: revoked 566
+id: revoked 567
+id: revoked 568
+id: revoked 569
+id: revoked 570
+id: revoked 571
+id: revoked 572
+id: revoked 573
+id: revoked 574
+id: revoked 575
+id: revoked 576
+id: revoked 577
+id: revoked 578
+id: revoked 579
+id: revoked 580
+id: revoked 581
+id: revoked 582
+id: revoked 583
+id: revoked 584
+id: revoked 585
+id: revoked 586
+id: revoked 587
+id: revoked 588
+id: revoked 589
+id: revoked 590
+id: revoked 591
+id: revoked 592
+id: revoked 593
+id: revoked 594
+id: revoked 595
+id: revoked 596
+id: revoked 597
+id: revoked 598
+id: revoked 599
+id: revoked 600
+id: revoked 601
+id: revoked 602
+id: revoked 603
+id: revoked 604
+id: revoked 605
+id: revoked 606
+id: revoked 607
+id: revoked 608
+id: revoked 609
+id: revoked 610
+id: revoked 611
+id: revoked 612
+id: revoked 613
+id: revoked 614
+id: revoked 615
+id: revoked 616
+id: revoked 617
+id: revoked 618
+id: revoked 619
+id: revoked 620
+id: revoked 621
+id: revoked 622
+id: revoked 623
+id: revoked 624
+id: revoked 625
+id: revoked 626
+id: revoked 627
+id: revoked 628
+id: revoked 629
+id: revoked 630
+id: revoked 631
+id: revoked 632
+id: revoked 633
+id: revoked 634
+id: revoked 635
+id: revoked 636
+id: revoked 637
+id: revoked 638
+id: revoked 639
+id: revoked 640
+id: revoked 641
+id: revoked 642
+id: revoked 643
+id: revoked 644
+id: revoked 645
+id: revoked 646
+id: revoked 647
+id: revoked 648
+id: revoked 649
+id: revoked 650
+id: revoked 651
+id: revoked 652
+id: revoked 653
+id: revoked 654
+id: revoked 655
+id: revoked 656
+id: revoked 657
+id: revoked 658
+id: revoked 659
+id: revoked 660
+id: revoked 661
+id: revoked 662
+id: revoked 663
+id: revoked 664
+id: revoked 665
+id: revoked 666
+id: revoked 667
+id: revoked 668
+id: revoked 669
+id: revoked 670
+id: revoked 671
+id: revoked 672
+id: revoked 673
+id: revoked 674
+id: revoked 675
+id: revoked 676
+id: revoked 677
+id: revoked 678
+id: revoked 679
+id: revoked 680
+id: revoked 681
+id: revoked 682
+id: revoked 683
+id: revoked 684
+id: revoked 685
+id: revoked 686
+id: revoked 687
+id: revoked 688
+id: revoked 689
+id: revoked 690
+id: revoked 691
+id: revoked 692
+id: revoked 693
+id: revoked 694
+id: revoked 695
+id: revoked 696
+id: revoked 697
+id: revoked 698
+id: revoked 699
+id: revoked 700
+id: revoked 701
+id: revoked 702
+id: revoked 703
+id: revoked 704
+id: revoked 705
+id: revoked 706
+id: revoked 707
+id: revoked 708
+id: revoked 709
+id: revoked 710
+id: revoked 711
+id: revoked 712
+id: revoked 713
+id: revoked 714
+id: revoked 715
+id: revoked 716
+id: revoked 717
+id: revoked 718
+id: revoked 719
+id: revoked 720
+id: revoked 721
+id: revoked 722
+id: revoked 723
+id: revoked 724
+id: revoked 725
+id: revoked 726
+id: revoked 727
+id: revoked 728
+id: revoked 729
+id: revoked 730
+id: revoked 731
+id: revoked 732
+id: revoked 733
+id: revoked 734
+id: revoked 735
+id: revoked 736
+id: revoked 737
+id: revoked 738
+id: revoked 739
+id: revoked 740
+id: revoked 741
+id: revoked 742
+id: revoked 743
+id: revoked 744
+id: revoked 745
+id: revoked 746
+id: revoked 747
+id: revoked 748
+id: revoked 749
+id: revoked 750
+id: revoked 751
+id: revoked 752
+id: revoked 753
+id: revoked 754
+id: revoked 755
+id: revoked 756
+id: revoked 757
+id: revoked 758
+id: revoked 759
+id: revoked 760
+id: revoked 761
+id: revoked 762
+id: revoked 763
+id: revoked 764
+id: revoked 765
+id: revoked 766
+id: revoked 767
+id: revoked 768
+id: revoked 769
+id: revoked 770
+id: revoked 771
+id: revoked 772
+id: revoked 773
+id: revoked 774
+id: revoked 775
+id: revoked 776
+id: revoked 777
+id: revoked 778
+id: revoked 779
+id: revoked 780
+id: revoked 781
+id: revoked 782
+id: revoked 783
+id: revoked 784
+id: revoked 785
+id: revoked 786
+id: revoked 787
+id: revoked 788
+id: revoked 789
+id: revoked 790
+id: revoked 791
+id: revoked 792
+id: revoked 793
+id: revoked 794
+id: revoked 795
+id: revoked 796
+id: revoked 797
+id: revoked 798
+id: revoked 799
+id: revoked 999
+id: revoked 1000
+id: revoked 1001
+id: revoked 1002
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-serials b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-serials
new file mode 100644
index 0000000..b20fec2
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-serials
@@ -0,0 +1,19 @@
+serial: 1-4
+serial: 10
+serial: 15
+serial: 30
+serial: 50
+serial: 90
+serial: 999
+# The following sum to 500-799
+serial: 500
+serial: 501
+serial: 502
+serial: 503-600
+serial: 700-797
+serial: 798
+serial: 799
+serial: 599-701
+# Some multiple consecutive serial number ranges
+serial: 10000-20000
+serial: 30000-40000
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-sha1 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-sha1
new file mode 100644
index 0000000..475e90c
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-sha1
@@ -0,0 +1,11 @@
+sha1: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC29QZ72HtyCdaNo6p2GH3fJpUynwkvs8Acwn66G7YTh
+sha1: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL0ZC4fqgbBgROeX1sOEPr4uMVNfPdJ62bVo/zvSMRQx
+sha1: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG5IH9T7FY47VJDUoyOlB/iqCN4pO8dgOrxclmKN5R5w
+sha1: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINjhG4+EnQy8YHLsfE8+IQwNWZVn1GBYX75pwxBCZGmy
+sha1: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJXm+qgTs+sO+9zvoZBxkQD39R2rQqQCVezxQoGjKui5
+sha1: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL8+aQKja+SbqxfWR61FCcsbBw2jaF/KHvcqdP2Fbp6Q
+sha1: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAOL7IULalT2Izo9TgRf1t2HNpZ5WCZJH5oRCd9LK3BN
+sha1: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGVaSedhPkl2Hrx1nOOKT2E52ADsBebawws87NN1+P6e
+sha1: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICxyaxj7pRVDeh/gxJem9BLhoUQKGnKXHfDrB/GtC1KB
+sha1: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID96GKtj4wN+OwvrQsgP37fQVUXThCML796qqFNLVDCA
+sha1: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEh3BFcTzXmg1Fi5LvWiUDWORsHzVhUCm8ekrEJG6+6A
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-sha256 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-sha256
new file mode 100644
index 0000000..13109e9
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/revoked-sha256
@@ -0,0 +1,11 @@
+sha256: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC29QZ72HtyCdaNo6p2GH3fJpUynwkvs8Acwn66G7YTh
+sha256: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL0ZC4fqgbBgROeX1sOEPr4uMVNfPdJ62bVo/zvSMRQx
+sha256: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG5IH9T7FY47VJDUoyOlB/iqCN4pO8dgOrxclmKN5R5w
+sha256: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINjhG4+EnQy8YHLsfE8+IQwNWZVn1GBYX75pwxBCZGmy
+sha256: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJXm+qgTs+sO+9zvoZBxkQD39R2rQqQCVezxQoGjKui5
+sha256: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL8+aQKja+SbqxfWR61FCcsbBw2jaF/KHvcqdP2Fbp6Q
+sha256: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAOL7IULalT2Izo9TgRf1t2HNpZ5WCZJH5oRCd9LK3BN
+sha256: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGVaSedhPkl2Hrx1nOOKT2E52ADsBebawws87NN1+P6e
+sha256: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICxyaxj7pRVDeh/gxJem9BLhoUQKGnKXHfDrB/GtC1KB
+sha256: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID96GKtj4wN+OwvrQsgP37fQVUXThCML796qqFNLVDCA
+sha256: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEh3BFcTzXmg1Fi5LvWiUDWORsHzVhUCm8ekrEJG6+6A
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0005 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0005
new file mode 100644
index 0000000..d82a0b5
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0005
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACDqONQediveIXoseoT+MWp9yEdMO7hP7F4fAno6gunyoAAAAIig1MZroNTG
+awAAAAtzc2gtZWQyNTUxOQAAACDqONQediveIXoseoT+MWp9yEdMO7hP7F4fAno6gunyoA
+AAAEBSEPLoX4NVkAchYZEGi7hjd5NoVBWuoxqluCGt/fWrYeo41B52K94heix6hP4xan3I
+R0w7uE/sXh8CejqC6fKgAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0005-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0005-cert.pub
new file mode 100644
index 0000000..59ea422
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0005-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIGnzDhP/hp83ipkW8T7f0CIXJuPK7ldbJFKDUrkvn6J1AAAAIOo41B52K94heix6hP4xan3IR0w7uE/sXh8CejqC6fKgAAAAAAAAAAUAAAABAAAACXJldm9rZWQgNQAAAAAAAAAAAAAAAP//////////AAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAADMAAAALc3NoLWVkMjU1MTkAAAAgFtF5l68spzOFrIpQANF0C9nkNdXMAmlCRwHgw91C784AAABTAAAAC3NzaC1lZDI1NTE5AAAAQO9W58IrK+I0o2us9Hs/QBkrEe1YIgl6PzCMsu/Zu/tdZxGDK5Pxoz7tKzXezS9LPGQfZ3fVdl58PZC1DtxQ5gU= ./tst-keys/unrevoked-0005.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0005.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0005.pub
new file mode 100644
index 0000000..081ac6c
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0005.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOo41B52K94heix6hP4xan3IR0w7uE/sXh8CejqC6fKg
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0009 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0009
new file mode 100644
index 0000000..9479498
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0009
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACDXQqTeALQCMo64B4EX5abjRvrjVu69Mnxgg2q0SB5/oQAAAIgIqeXLCKnl
+ywAAAAtzc2gtZWQyNTUxOQAAACDXQqTeALQCMo64B4EX5abjRvrjVu69Mnxgg2q0SB5/oQ
+AAAECubGChJGu90ZNiP/zF+tTtr0+l7y8BrTDMQ0m0+cU0qtdCpN4AtAIyjrgHgRflpuNG
++uNW7r0yfGCDarRIHn+hAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0009-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0009-cert.pub
new file mode 100644
index 0000000..9ee8890
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0009-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIERRY0M1bHm2Qjyo105OCHWp0UCRHLP0xkMuHnkMDP5eAAAAINdCpN4AtAIyjrgHgRflpuNG+uNW7r0yfGCDarRIHn+hAAAAAAAAAAkAAAABAAAACXJldm9rZWQgOQAAAAAAAAAAAAAAAP//////////AAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAADMAAAALc3NoLWVkMjU1MTkAAAAgFtF5l68spzOFrIpQANF0C9nkNdXMAmlCRwHgw91C784AAABTAAAAC3NzaC1lZDI1NTE5AAAAQFsA4xJHRCXSyq6GHkKdemfbg+jvUZxHlu/UBoZf4esEHAtx0mXiajbUwkWzkh1vCtxZNZhiLIhxqDcNMu+O+wo= ./tst-keys/unrevoked-0009.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0009.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0009.pub
new file mode 100644
index 0000000..74a797b
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0009.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINdCpN4AtAIyjrgHgRflpuNG+uNW7r0yfGCDarRIHn+h
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0014 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0014
new file mode 100644
index 0000000..6fa4fd9
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0014
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACDvTTMHyjozzZabuUzy61XOKBm4klUjUGSWYtX6T4XtEwAAAIhyFdxYchXc
+WAAAAAtzc2gtZWQyNTUxOQAAACDvTTMHyjozzZabuUzy61XOKBm4klUjUGSWYtX6T4XtEw
+AAAEBtC+f4bz1/qtq5K2Rf+0bPeY3P0OWdD3rvrlGPh8wN5u9NMwfKOjPNlpu5TPLrVc4o
+GbiSVSNQZJZi1fpPhe0TAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0014-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0014-cert.pub
new file mode 100644
index 0000000..bb954f9
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0014-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIPes2n/Xk4mm4OpuvHDqx9+76vm+SmFgc9d7ATGT1+C8AAAAIO9NMwfKOjPNlpu5TPLrVc4oGbiSVSNQZJZi1fpPhe0TAAAAAAAAAA4AAAABAAAACnJldm9rZWQgMTQAAAAAAAAAAAAAAAD//////////wAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIBbReZevLKczhayKUADRdAvZ5DXVzAJpQkcB4MPdQu/OAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEDGVORypw3DoMuWBu0V4cH/OgRBstD5cY37CfLrVZpmGv9jDRXVNQee7vYowk0r3XvQPoUecQBIMZGAQtEiw18E ./tst-keys/unrevoked-0014.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0014.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0014.pub
new file mode 100644
index 0000000..4a866e4
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0014.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO9NMwfKOjPNlpu5TPLrVc4oGbiSVSNQZJZi1fpPhe0T
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0016 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0016
new file mode 100644
index 0000000..62d5027
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0016
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACBWKMDlSwSGo4dcBAZmL+Xxk64Wp/ZfFSu2vkp82JXQCQAAAIjUcNt51HDb
+eQAAAAtzc2gtZWQyNTUxOQAAACBWKMDlSwSGo4dcBAZmL+Xxk64Wp/ZfFSu2vkp82JXQCQ
+AAAEC1V7PD5tJSOUZtpfqVfWyiSIMJkCDFZzTmFs7GBpJE71YowOVLBIajh1wEBmYv5fGT
+rhan9l8VK7a+SnzYldAJAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0016-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0016-cert.pub
new file mode 100644
index 0000000..367e4ab
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0016-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAICGqa0xwr0etbKquuBy5/hYQ/rbMrKfEE6XShgb4YWpUAAAAIFYowOVLBIajh1wEBmYv5fGTrhan9l8VK7a+SnzYldAJAAAAAAAAABAAAAABAAAACnJldm9rZWQgMTYAAAAAAAAAAAAAAAD//////////wAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIBbReZevLKczhayKUADRdAvZ5DXVzAJpQkcB4MPdQu/OAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEBKVetE3dsch2wjMIHGoiH8zp6gFMn1KgGKn01EPc1A08a/JKNvaSDYhlARLjiBzjIUGlykhHTTr4EcHTPWl58P ./tst-keys/unrevoked-0016.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0016.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0016.pub
new file mode 100644
index 0000000..47cac1e
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0016.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFYowOVLBIajh1wEBmYv5fGTrhan9l8VK7a+SnzYldAJ
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0029 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0029
new file mode 100644
index 0000000..589daa6
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0029
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACA3B1NQ9RFEkJUGcIUcCL22yMVEeob8/PUsk9lYH43vPwAAAIjxPrzV8T68
+1QAAAAtzc2gtZWQyNTUxOQAAACA3B1NQ9RFEkJUGcIUcCL22yMVEeob8/PUsk9lYH43vPw
+AAAED89ht9KdlYRfsKwh+pzh6BOvPf/U58QBkw1d3LfKnn+jcHU1D1EUSQlQZwhRwIvbbI
+xUR6hvz89SyT2Vgfje8/AAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0029-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0029-cert.pub
new file mode 100644
index 0000000..1bf3883
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0029-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIEVLRuchC4z7/EqITmyqCxOyhC7/enmFWsalP8FFFYiXAAAAIDcHU1D1EUSQlQZwhRwIvbbIxUR6hvz89SyT2Vgfje8/AAAAAAAAAB0AAAABAAAACnJldm9rZWQgMjkAAAAAAAAAAAAAAAD//////////wAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIBbReZevLKczhayKUADRdAvZ5DXVzAJpQkcB4MPdQu/OAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEChRFz/Zb6b3znoIWJjd8OTmCIEH7YE/fKWtyWHoGjz02G4VnCfwuHp23yD+k1XsoOGC7xcSnQeqZ19160HDNgC ./tst-keys/unrevoked-0029.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0029.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0029.pub
new file mode 100644
index 0000000..4072d92
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0029.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDcHU1D1EUSQlQZwhRwIvbbIxUR6hvz89SyT2Vgfje8/
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0049 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0049
new file mode 100644
index 0000000..b5788a0
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0049
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACD2mB5GBuavtb/bX7W54OmUCCJzUWBwG7cQ4q/jon1MBQAAAIjRkEU40ZBF
+OAAAAAtzc2gtZWQyNTUxOQAAACD2mB5GBuavtb/bX7W54OmUCCJzUWBwG7cQ4q/jon1MBQ
+AAAECuUtJb+T0um2mGvjD/ZZpbtjIhWc3jGVbzuDnEovOjnPaYHkYG5q+1v9tftbng6ZQI
+InNRYHAbtxDir+OifUwFAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0049-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0049-cert.pub
new file mode 100644
index 0000000..587cf62
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0049-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAILZPLEL5xQ8HDLa8pJhchJ3EEhZcjMqACCAEeL+U6c/QAAAAIPaYHkYG5q+1v9tftbng6ZQIInNRYHAbtxDir+OifUwFAAAAAAAAADEAAAABAAAACnJldm9rZWQgNDkAAAAAAAAAAAAAAAD//////////wAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIBbReZevLKczhayKUADRdAvZ5DXVzAJpQkcB4MPdQu/OAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEB2GglzoC1VgsYNAVd5BDsLbeR5M5hHcVVvNsGnK1QCXMj56cgfkbXLj6W6tjJEEFY4G+KPJh1F/SGJi02P5lkJ ./tst-keys/unrevoked-0049.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0049.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0049.pub
new file mode 100644
index 0000000..07d5369
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0049.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPaYHkYG5q+1v9tftbng6ZQIInNRYHAbtxDir+OifUwF
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0051 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0051
new file mode 100644
index 0000000..52d3283
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0051
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACD39ygfAlHPhZWU8inWu1hypIlQTChQxSKKB6iaV6Q0lQAAAIgMawsqDGsL
+KgAAAAtzc2gtZWQyNTUxOQAAACD39ygfAlHPhZWU8inWu1hypIlQTChQxSKKB6iaV6Q0lQ
+AAAEB4Ng9MekhsMKYDaBcOUWdxmi1rjgCsPOOfpABTxiCef/f3KB8CUc+FlZTyKda7WHKk
+iVBMKFDFIooHqJpXpDSVAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0051-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0051-cert.pub
new file mode 100644
index 0000000..5b4bd11
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0051-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIGTNYRrlJ1vExK7dume319Krn4YW6wyZc4PzZLjZoB8zAAAAIPf3KB8CUc+FlZTyKda7WHKkiVBMKFDFIooHqJpXpDSVAAAAAAAAADMAAAABAAAACnJldm9rZWQgNTEAAAAAAAAAAAAAAAD//////////wAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIBbReZevLKczhayKUADRdAvZ5DXVzAJpQkcB4MPdQu/OAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEAgUiwWKerMo8nuejTER/EmM6ZUpmXjgFwPCpb1LAxBJH71iOnyF9S0gp+CSmjqiTS2yuQajSMen64wOdJCX7wF ./tst-keys/unrevoked-0051.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0051.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0051.pub
new file mode 100644
index 0000000..88867e5
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0051.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPf3KB8CUc+FlZTyKda7WHKkiVBMKFDFIooHqJpXpDSV
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0499 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0499
new file mode 100644
index 0000000..8f59be9
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0499
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACCpwI1aCbAOVvA7NJhLtBNpR4tiGGtTQ019wjKL6zJ/uQAAAIhllrzrZZa8
+6wAAAAtzc2gtZWQyNTUxOQAAACCpwI1aCbAOVvA7NJhLtBNpR4tiGGtTQ019wjKL6zJ/uQ
+AAAECQ6o+3J9W3wXFWEcrPJl5qJZudUPmPdKF7SYxcMTrVP6nAjVoJsA5W8Ds0mEu0E2lH
+i2IYa1NDTX3CMovrMn+5AAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0499-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0499-cert.pub
new file mode 100644
index 0000000..a6e76f1
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0499-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIJvt1IxZsGIIS9DDCCKiD13Dbs5Af5ouews+YwZ9FoydAAAAIKnAjVoJsA5W8Ds0mEu0E2lHi2IYa1NDTX3CMovrMn+5AAAAAAAAAfMAAAABAAAAC3Jldm9rZWQgNDk5AAAAAAAAAAAAAAAA//////////8AAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACAW0XmXryynM4WsilAA0XQL2eQ11cwCaUJHAeDD3ULvzgAAAFMAAAALc3NoLWVkMjU1MTkAAABAMaA4UjND4LX9kdHjhgWJjGzzs/xUBwxQQcAmNgwmmQzmkwj8ctWBBA1+TkBMcZbSNUWBdclT4UcnDPEYqG1NBg== ./tst-keys/unrevoked-0499.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0499.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0499.pub
new file mode 100644
index 0000000..5a3acbb
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0499.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKnAjVoJsA5W8Ds0mEu0E2lHi2IYa1NDTX3CMovrMn+5
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0800 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0800
new file mode 100644
index 0000000..9684d72
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0800
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACAn5h8A2vYJ1+IWVtdLMulUQKCqlVLHpcHEFqYC5gtGlwAAAIh2lf7UdpX+
+1AAAAAtzc2gtZWQyNTUxOQAAACAn5h8A2vYJ1+IWVtdLMulUQKCqlVLHpcHEFqYC5gtGlw
+AAAEAEXGgMPKs3HwkQmNdVkbO3PcaBVCBEv1l8yy/ly30jPSfmHwDa9gnX4hZW10sy6VRA
+oKqVUselwcQWpgLmC0aXAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0800-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0800-cert.pub
new file mode 100644
index 0000000..ab47a2b
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0800-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIPAKFTJ25v9CsCppsQ/FwXAZgntAIdQHUXo0KQ3FrlTzAAAAICfmHwDa9gnX4hZW10sy6VRAoKqVUselwcQWpgLmC0aXAAAAAAAAAyAAAAABAAAAC3Jldm9rZWQgODAwAAAAAAAAAAAAAAAA//////////8AAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACAW0XmXryynM4WsilAA0XQL2eQ11cwCaUJHAeDD3ULvzgAAAFMAAAALc3NoLWVkMjU1MTkAAABA16aKfsgD0iZ+qc2b1AxBHZ/nyczN2Xjbhg4eJm/6cPSkBHs8uan5e8yPBIQJq2LztC3If6Z6PARoWUnIKb43CQ== ./tst-keys/unrevoked-0800.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0800.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0800.pub
new file mode 100644
index 0000000..3a41f29
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-0800.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICfmHwDa9gnX4hZW10sy6VRAoKqVUselwcQWpgLmC0aX
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1010 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1010
new file mode 100644
index 0000000..89df717
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1010
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACAg0jawQzRMO/ESfFm6yDc66J5kjasOqTb7rmQSU6Nk3QAAAIhczXMoXM1z
+KAAAAAtzc2gtZWQyNTUxOQAAACAg0jawQzRMO/ESfFm6yDc66J5kjasOqTb7rmQSU6Nk3Q
+AAAEAdeQiqpyZqBaffmgy+UrvFVpygD0n8isn3zjumVNtKxiDSNrBDNEw78RJ8WbrINzro
+nmSNqw6pNvuuZBJTo2TdAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1010-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1010-cert.pub
new file mode 100644
index 0000000..2d0fe53
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1010-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIITg9nSjjofIKXTKf2byvYL3Ce43PP9Dtrbj/+AlfgEtAAAAICDSNrBDNEw78RJ8WbrINzronmSNqw6pNvuuZBJTo2TdAAAAAAAAA/IAAAABAAAADHJldm9rZWQgMTAxMAAAAAAAAAAAAAAAAP//////////AAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAADMAAAALc3NoLWVkMjU1MTkAAAAgFtF5l68spzOFrIpQANF0C9nkNdXMAmlCRwHgw91C784AAABTAAAAC3NzaC1lZDI1NTE5AAAAQIndHhKILtU0+FkKKw1KmhaHQS3p1KiQdld/2P5jpcEgb292iY+ICU+aHXKvS8qGM2aMImv8835NEyWy/MB74QM= ./tst-keys/unrevoked-1010.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1010.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1010.pub
new file mode 100644
index 0000000..05c5eac
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1010.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICDSNrBDNEw78RJ8WbrINzronmSNqw6pNvuuZBJTo2Td
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1011 b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1011
new file mode 100644
index 0000000..38b8232
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1011
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACCd4IBQx9BhO9FzYMOKu3cKgBcwUwb7XzS3uI26RgmEYgAAAIjHvhtux74b
+bgAAAAtzc2gtZWQyNTUxOQAAACCd4IBQx9BhO9FzYMOKu3cKgBcwUwb7XzS3uI26RgmEYg
+AAAEBsteyDUYUNwgY3SMkMs0guy8MJfek2kuvH35zEpVf6Hp3ggFDH0GE70XNgw4q7dwqA
+FzBTBvtfNLe4jbpGCYRiAAAAAAECAwQF
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1011-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1011-cert.pub
new file mode 100644
index 0000000..4671638
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1011-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIMjD2+xjmUC1VviOH+peT9C81Y4xjyTue/F69nFKmQBMAAAAIJ3ggFDH0GE70XNgw4q7dwqAFzBTBvtfNLe4jbpGCYRiAAAAAAAAA/MAAAABAAAADHJldm9rZWQgMTAxMQAAAAAAAAAAAAAAAP//////////AAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAADMAAAALc3NoLWVkMjU1MTkAAAAgFtF5l68spzOFrIpQANF0C9nkNdXMAmlCRwHgw91C784AAABTAAAAC3NzaC1lZDI1NTE5AAAAQNENdVFCE02X6z+wFJtm2DQcgdc4oov9DyFKLPqLrogo+pVao5QwOkeJ2J/tmp40H2+uP/jrDlQuCvOcoQGHqwY= ./tst-keys/unrevoked-1011.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1011.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1011.pub
new file mode 100644
index 0000000..0809077
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/krl/unrevoked-1011.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ3ggFDH0GE70XNgw4q7dwqAFzBTBvtfNLe4jbpGCYRi
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/other_key b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/other_key
new file mode 100644
index 0000000..ee3f922
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/other_key
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACA3ivU7wf37jE1ITC5KQjVeVlyFTkgWJxub8t380ovjiwAAAJDdMhQO3TIU
+DgAAAAtzc2gtZWQyNTUxOQAAACA3ivU7wf37jE1ITC5KQjVeVlyFTkgWJxub8t380ovjiw
+AAAEA4NlTFs3h2zqt5pSZ5S3dJb42GE7EjG16coKj70eELNDeK9TvB/fuMTUhMLkpCNV5W
+XIVOSBYnG5vy3fzSi+OLAAAADVRIV09AU0VBR044MDA=
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/other_key-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/other_key-cert.pub
new file mode 100644
index 0000000..2be08be
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/other_key-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIGXo4+L/NyBl1VQDP39PxJP3LSzaqopqZGVP3cG0WoFAAAAAIDeK9TvB/fuMTUhMLkpCNV5WXIVOSBYnG5vy3fzSi+OLAAAAAAAAAAUAAAABAAAABnRlc3RlcgAAABYAAAASdGVzdGVyQGV4YW1wbGUuY29tAAAAAGbTroAAAAAAZyLIgAAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIMdEl+iOTEbf1RC3uicECtid+SaIMsAw7wrlWhOQTyBVAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEA/HwKB8J/kvkEsdxDou+UebnR9u30xPH6FEnbHLlfKbKMIXwLFIHnf9F6bTL36WhFDEDcSBGS19VBWBDRosM8L
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/other_key.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/other_key.pub
new file mode 100644
index 0000000..0255005
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/other_key.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDeK9TvB/fuMTUhMLkpCNV5WXIVOSBYnG5vy3fzSi+OL
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/repo.bundle b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/repo.bundle
new file mode 100644
index 0000000..c402f54
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/repo.bundle
Binary files differ
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/signing_key b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/signing_key
new file mode 100644
index 0000000..3dd37be
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/signing_key
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACBgEzmfD3DinWPe/H8yLLZ2dPhbnnyFiqe8EWcp0C3czgAAAJDhSMqA4UjK
+gAAAAAtzc2gtZWQyNTUxOQAAACBgEzmfD3DinWPe/H8yLLZ2dPhbnnyFiqe8EWcp0C3czg
+AAAEB1yC00NMYEAVzhDj9odGVL0EonaIkf5jdUZ/czJ0+SPWATOZ8PcOKdY978fzIstnZ0
++FuefIWKp7wRZynQLdzOAAAADVRIV09AU0VBR044MDA=
+-----END OPENSSH PRIVATE KEY-----
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/signing_key-cert.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/signing_key-cert.pub
new file mode 100644
index 0000000..de191d1
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/signing_key-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIFEmoWkYraMju0JI0b/0RQtR6RYo/OVp53EVf48L/Pu/AAAAIBmHlkHFlA7HkoTZcau80PH5zduQu41m8BqnH/1v2BwVAAAAAAAAAAEAAAABAAAACGFfa2V5X2lkAAAAFgAAABJ0ZXN0ZXJAZXhhbXBsZS5jb20AAAAAZtOugAAAAABm1QAAAAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAADMAAAALc3NoLWVkMjU1MTkAAAAg2ifM9NMuXwQf7H/H5LCMhMjVqugyyN+jmcMoJUL2YLAAAABTAAAAC3NzaC1lZDI1NTE5AAAAQG1kXUido46YOnmwvkJuIAKyp6Q9Gr+lbdOQvU0St/Hc9HTTIxgDGyLpv0alIJpHOuSYUUUxDufvGKtLJK1duwg= ./signing_key.pub
diff --git a/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/signing_key.pub b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/signing_key.pub
new file mode 100644
index 0000000..e1210e7
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst-rsrc/org/eclipse/jgit/internal/signing/ssh/signing_key.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO
diff --git a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/AbstractSshSignatureTest.java b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/AbstractSshSignatureTest.java
new file mode 100644
index 0000000..fdfffce
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/AbstractSshSignatureTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.signing.ssh;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.time.Instant;
+import java.time.ZoneOffset;
+
+import org.eclipse.jgit.api.CommitCommand;
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.rules.TemporaryFolder;
+
+/**
+ * Common setup for SSH signature tests.
+ */
+public abstract class AbstractSshSignatureTest extends RepositoryTestCase {
+
+ @Rule
+ public TemporaryFolder keys = new TemporaryFolder();
+
+ protected File certs;
+
+ protected Instant commitTime;
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ copyResource("allowed_signers", keys.getRoot());
+ copyResource("other_key", keys.getRoot());
+ copyResource("other_key.pub", keys.getRoot());
+ copyResource("other_key-cert.pub", keys.getRoot());
+ copyResource("signing_key", keys.getRoot());
+ copyResource("signing_key.pub", keys.getRoot());
+ certs = keys.newFolder("certs");
+ copyResource("certs/expired.cert", certs);
+ copyResource("certs/no_principals.cert", certs);
+ copyResource("certs/other.cert", certs);
+ copyResource("certs/other-ca.cert", certs);
+ copyResource("certs/tester.cert", certs);
+ copyResource("certs/two_principals.cert", certs);
+ Repository repo = db;
+ StoredConfig config = repo.getConfig();
+ config.setString("gpg", null, "format", "ssh");
+ config.setString("gpg", "ssh", "allowedSignersFile",
+ keys.getRoot().toPath().resolve("allowed_signers").toString()
+ .replace('\\', '/'));
+ config.save();
+ // Run all tests with commit times on 2024-10-02T12:00:00Z. The test
+ // certificates are valid from 2024-09-01 to 2024-10-31, except the
+ // "expired" certificate which is valid only on 2024-09-01.
+ commitTime = Instant.parse("2024-10-02T12:00:00.00Z");
+ }
+
+ private void copyResource(String name, File directory) throws IOException {
+ try (InputStream in = this.getClass().getResourceAsStream(name)) {
+ int i = name.lastIndexOf('/');
+ String fileName = i < 0 ? name : name.substring(i + 1);
+ Files.copy(in, directory.toPath().resolve(fileName));
+ }
+ }
+
+ protected RevCommit createSignedCommit(String certificate,
+ String signingKey) throws Exception {
+ Repository repo = db;
+ Path key = keys.getRoot().toPath().resolve(signingKey);
+ if (certificate != null) {
+ Files.copy(certs.toPath().resolve(certificate),
+ keys.getRoot().toPath().resolve(signingKey),
+ StandardCopyOption.REPLACE_EXISTING);
+ }
+ PersonIdent commitAuthor = new PersonIdent("tester",
+ "tester@example.com", commitTime, ZoneOffset.UTC);
+ try (Git git = Git.wrap(repo)) {
+ writeTrashFile("foo.txt", "foo");
+ git.add().addFilepattern("foo.txt").call();
+ CommitCommand commit = git.commit();
+ commit.setAuthor(commitAuthor);
+ commit.setCommitter(commitAuthor);
+ commit.setMessage("Message");
+ commit.setSign(Boolean.TRUE);
+ commit.setSigningKey(key.toAbsolutePath().toString());
+ return commit.call();
+ }
+ }
+
+ protected RevCommit checkSshSignature(RevCommit c) {
+ byte[] sig = c.getRawGpgSignature();
+ assertNotNull(sig);
+ String signature = new String(sig, StandardCharsets.US_ASCII);
+ assertTrue("Not an SSH signature:\n" + signature,
+ signature.startsWith(Constants.SSH_SIGNATURE_PREFIX));
+ return c;
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/AllowedSignersParseTest.java b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/AllowedSignersParseTest.java
new file mode 100644
index 0000000..84d8179
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/AllowedSignersParseTest.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.signing.ssh;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
+
+import java.io.StreamCorruptedException;
+import java.time.Instant;
+
+import org.eclipse.jgit.junit.MockSystemReader;
+import org.eclipse.jgit.util.SystemReader;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for the line parsing in {@link AllowedSigners}.
+ */
+public class AllowedSignersParseTest {
+
+ @Before
+ public void setup() {
+ // Uses GMT-03:30 as time zone.
+ SystemReader.setInstance(new MockSystemReader());
+ }
+
+ @After
+ public void tearDown() {
+ SystemReader.setInstance(null);
+ }
+
+ @Test
+ public void testValidDate() {
+ assertEquals(Instant.parse("2024-09-01T00:00:00.00Z"),
+ AllowedSigners.parseDate("20240901Z"));
+ assertEquals(Instant.parse("2024-09-01T01:02:00.00Z"),
+ AllowedSigners.parseDate("202409010102Z"));
+ assertEquals(Instant.parse("2024-09-01T01:02:03.00Z"),
+ AllowedSigners.parseDate("20240901010203Z"));
+ assertEquals(Instant.parse("2024-09-01T03:30:00.00Z"),
+ AllowedSigners.parseDate("20240901"));
+ assertEquals(Instant.parse("2024-09-01T04:32:00.00Z"),
+ AllowedSigners.parseDate("202409010102"));
+ assertEquals(Instant.parse("2024-09-01T04:32:03.00Z"),
+ AllowedSigners.parseDate("20240901010203"));
+ }
+
+ @Test
+ public void testInvalidDate() {
+ assertThrows(Exception.class, () -> AllowedSigners.parseDate("1234"));
+ assertThrows(Exception.class,
+ () -> AllowedSigners.parseDate("09/01/2024"));
+ assertThrows(Exception.class,
+ () -> AllowedSigners.parseDate("2024-09-01"));
+ }
+
+ private void checkValidKey(String expected, String input, int from)
+ throws StreamCorruptedException {
+ assertEquals(expected, AllowedSigners.parsePublicKey(input, from));
+ }
+ @Test
+ public void testValidPublicKey() throws StreamCorruptedException {
+ checkValidKey(
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO",
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO",
+ 0);
+ checkValidKey(
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO",
+ "xyzssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO",
+ 3);
+ checkValidKey(
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO",
+ "xyz ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO abc",
+ 3);
+ checkValidKey(
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO",
+ "xyz\tssh-ed25519 \tAAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO abc",
+ 3);
+ }
+
+ @Test
+ public void testInvalidPublicKey() {
+ assertThrows(Exception.class,
+ () -> AllowedSigners.parsePublicKey(null, 0));
+ assertThrows(Exception.class,
+ () -> AllowedSigners.parsePublicKey("", 0));
+ assertThrows(Exception.class,
+ () -> AllowedSigners.parsePublicKey("foo", 0));
+ assertThrows(Exception.class,
+ () -> AllowedSigners.parsePublicKey("ssh-ed25519 bar", -1));
+ assertThrows(Exception.class,
+ () -> AllowedSigners.parsePublicKey("ssh-ed25519 bar", 12));
+ assertThrows(Exception.class,
+ () -> AllowedSigners.parsePublicKey("ssh-ed25519 bar", 13));
+ assertThrows(Exception.class,
+ () -> AllowedSigners.parsePublicKey("ssh-ed25519 bar", 16));
+ }
+
+ @Test
+ public void testValidDequote() {
+ assertEquals(new AllowedSigners.Dequoted("a\\bc", 4),
+ AllowedSigners.dequote("a\\bc", 0));
+ assertEquals(new AllowedSigners.Dequoted("a\\bc\"", 5),
+ AllowedSigners.dequote("a\\bc\"", 0));
+ assertEquals(new AllowedSigners.Dequoted("a\\b\"c", 5),
+ AllowedSigners.dequote("a\\b\"c", 0));
+ assertEquals(new AllowedSigners.Dequoted("a\\b\"c", 8),
+ AllowedSigners.dequote("\"a\\b\\\"c\"", 0));
+ assertEquals(new AllowedSigners.Dequoted("a\\b\"c", 11),
+ AllowedSigners.dequote("xyz\"a\\b\\\"c\"", 3));
+ assertEquals(new AllowedSigners.Dequoted("abc", 6),
+ AllowedSigners.dequote(" abc def", 3));
+ }
+
+ @Test
+ public void testInvalidDequote() {
+ assertThrows(Exception.class, () -> AllowedSigners.dequote("\"abc", 0));
+ assertThrows(Exception.class,
+ () -> AllowedSigners.dequote("\"abc\\\"", 0));
+ }
+
+ @Test
+ public void testValidLine() throws Exception {
+ assertEquals(new AllowedSigners.AllowedEntry(
+ new String[] { "*@a.com" },
+ true, null, null, null,
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"),
+ AllowedSigners.parseLine(
+ "*@a.com cert-authority ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"));
+ assertEquals(new AllowedSigners.AllowedEntry(
+ new String[] { "*@a.com", "*@b.a.com" },
+ true, null, null, null,
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"),
+ AllowedSigners.parseLine(
+ "*@a.com,*@b.a.com cert-authority ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"));
+ assertEquals(new AllowedSigners.AllowedEntry(
+ new String[] { "foo@a.com" },
+ false, null, null, null,
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"),
+ AllowedSigners.parseLine(
+ "foo@a.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"));
+ assertEquals(new AllowedSigners.AllowedEntry(
+ new String[] { "foo@a.com" },
+ false, new String[] { "foo", "bar" }, null, null,
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"),
+ AllowedSigners.parseLine(
+ "foo@a.com namespaces=\"foo,bar\" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"));
+ assertEquals(new AllowedSigners.AllowedEntry(
+ new String[] { "foo@a.com" },
+ false, null, Instant.parse("2024-09-01T03:30:00.00Z"), null,
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"),
+ AllowedSigners.parseLine(
+ "foo@a.com valid-After=\"20240901\" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"));
+ assertEquals(new AllowedSigners.AllowedEntry(
+ new String[] { "*@a.com", "*@b.a.com" },
+ true, new String[] { "git" },
+ Instant.parse("2024-09-01T03:30:00.00Z"),
+ Instant.parse("2024-09-01T12:00:00.00Z"),
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"),
+ AllowedSigners.parseLine(
+ "*@a.com,*@b.a.com cert-authority namespaces=\"git\" valid-after=\"20240901\" valid-before=\"202409011200Z\" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"));
+ assertEquals(new AllowedSigners.AllowedEntry(
+ new String[] { "foo@a.com" },
+ false, new String[] { "git" },
+ Instant.parse("2024-09-01T03:30:00.00Z"),
+ Instant.parse("2024-09-01T12:00:00.00Z"),
+ "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBGxkz2AUld8eitmyIYlVV+Sot4jT3CigyBmvFRff0q4cSsKLx4x2TxGQeKKVueJEawtsUC2GNRV9FxXsTCUGcZU="),
+ AllowedSigners.parseLine(
+ "foo@a.com namespaces=\"git\" valid-after=\"20240901\" valid-before=\"202409011200Z\" ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBGxkz2AUld8eitmyIYlVV+Sot4jT3CigyBmvFRff0q4cSsKLx4x2TxGQeKKVueJEawtsUC2GNRV9FxXsTCUGcZU="));
+ }
+
+ @Test
+ public void testInvalidLine() {
+ assertThrows(Exception.class, () -> AllowedSigners.parseLine(
+ "cert-authority ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"));
+ assertThrows(Exception.class, () -> AllowedSigners.parseLine(
+ "namespaces=\"git\" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"));
+ assertThrows(Exception.class, () -> AllowedSigners.parseLine(
+ "valid-after=\"20240901\" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"));
+ assertThrows(Exception.class, () -> AllowedSigners.parseLine(
+ "valid-before=\"20240901\" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"));
+ assertThrows(Exception.class, () -> AllowedSigners.parseLine(
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"));
+ assertThrows(Exception.class, () -> AllowedSigners.parseLine(
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO foo@bar.com"));
+ assertThrows(Exception.class, () -> AllowedSigners.parseLine(
+ "AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"));
+ assertThrows(Exception.class, () -> AllowedSigners.parseLine(
+ "a@a.com namespaces=\"\" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"));
+ assertThrows(Exception.class, () -> AllowedSigners.parseLine(
+ "a@a.com namespaces=\",,,\" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"));
+ assertThrows(Exception.class, () -> AllowedSigners.parseLine(
+ "a@a.com,,b@a.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATOZ8PcOKdY978fzIstnZ0+FuefIWKp7wRZynQLdzO"));
+ }
+
+ @Test
+ public void testSkippedLine() throws Exception {
+ assertNull(AllowedSigners.parseLine(null));
+ assertNull(AllowedSigners.parseLine(""));
+ assertNull(AllowedSigners.parseLine("# Comment"));
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/OpenSshBinaryKrlLoadTest.java b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/OpenSshBinaryKrlLoadTest.java
new file mode 100644
index 0000000..9f9c3ca
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/OpenSshBinaryKrlLoadTest.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.signing.ssh;
+
+import static org.junit.Assert.assertNotNull;
+
+import java.io.BufferedInputStream;
+import java.io.InputStream;
+
+import org.junit.Test;
+
+/**
+ * Tests loading an {@link OpenSshBinaryKrl}.
+ */
+public class OpenSshBinaryKrlLoadTest {
+
+ @Test
+ public void testLoad() throws Exception {
+ try (InputStream in = new BufferedInputStream(
+ this.getClass().getResourceAsStream("krl/krl"))) {
+ OpenSshBinaryKrl krl = OpenSshBinaryKrl.load(in, false);
+ assertNotNull(krl);
+ }
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/OpenSshKrlTest.java b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/OpenSshKrlTest.java
new file mode 100644
index 0000000..2fd7756
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/OpenSshKrlTest.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.signing.ssh;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.PublicKey;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
+import org.apache.sshd.common.config.keys.PublicKeyEntryResolver;
+import org.eclipse.jgit.util.FileUtils;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ * Tests for {@link OpenSshKrl} using binary KRLs.
+ */
+@RunWith(Parameterized.class)
+public class OpenSshKrlTest {
+
+ // The test data was generated using the public domain OpenSSH test script
+ // with some minor modifications (une ed25519 always, generate a "includes
+ // everything KRl, name the unrekoked keys "unrevoked*", generate a plain
+ // text KRL, and don't run the ssh-keygen tests). The original script is
+ // available at
+ // https://github.com/openssh/openssh-portable/blob/67a115e/regress/krl.sh
+
+ private static final String[] KRLS = {
+ "krl-empty", "krl-keys", "krl-all",
+ "krl-sha1", "krl-sha256", "krl-hash",
+ "krl-serial", "krl-keyid", "krl-cert", "krl-ca",
+ "krl-serial-wild", "krl-keyid-wild", "krl-text" };
+
+ private static final int[] REVOKED = { 1, 4, 10, 50, 90, 500, 510, 520, 550,
+ 799, 999 };
+
+ private static final int[] UNREVOKED = { 5, 9, 14, 16, 29, 49, 51, 499, 800,
+ 1010, 1011 };
+
+ private static class TestData {
+
+ String key;
+
+ String krl;
+
+ Boolean expected;
+
+ TestData(String key, String krl, boolean expected) {
+ this.key = key;
+ this.krl = krl;
+ this.expected = Boolean.valueOf(expected);
+ }
+
+ @Override
+ public String toString() {
+ return key + '-' + krl;
+ }
+ }
+
+ @Parameters(name = "{0}")
+ public static List<TestData> initTestData() {
+ List<TestData> tests = new ArrayList<>();
+ for (int i = 0; i < REVOKED.length; i++) {
+ String key = String.format("revoked-%04d",
+ Integer.valueOf(REVOKED[i]));
+ for (String krl : KRLS) {
+ boolean expected = !krl.endsWith("-empty");
+ tests.add(new TestData(key + "-cert.pub", krl, expected));
+ expected = krl.endsWith("-keys") || krl.endsWith("-all")
+ || krl.endsWith("-hash") || krl.endsWith("-sha1")
+ || krl.endsWith("-sha256") || krl.endsWith("-text");
+ tests.add(new TestData(key + ".pub", krl, expected));
+ }
+ }
+ for (int i = 0; i < UNREVOKED.length; i++) {
+ String key = String.format("unrevoked-%04d",
+ Integer.valueOf(UNREVOKED[i]));
+ for (String krl : KRLS) {
+ boolean expected = false;
+ tests.add(new TestData(key + ".pub", krl, expected));
+ expected = krl.endsWith("-ca");
+ tests.add(new TestData(key + "-cert.pub", krl, expected));
+ }
+ }
+ return tests;
+ }
+
+ private static Path tmp;
+
+ @BeforeClass
+ public static void setUp() throws IOException {
+ tmp = Files.createTempDirectory("krls");
+ for (String krl : KRLS) {
+ copyResource("krl/" + krl, tmp);
+ }
+ }
+
+ private static void copyResource(String name, Path directory)
+ throws IOException {
+ try (InputStream in = OpenSshKrlTest.class
+ .getResourceAsStream(name)) {
+ int i = name.lastIndexOf('/');
+ String fileName = i < 0 ? name : name.substring(i + 1);
+ Files.copy(in, directory.resolve(fileName));
+ }
+ }
+
+ @AfterClass
+ public static void cleanUp() throws Exception {
+ FileUtils.delete(tmp.toFile(), FileUtils.RECURSIVE);
+ }
+
+ // Injected by JUnit
+ @Parameter
+ public TestData data;
+
+ @Test
+ public void testIsRevoked() throws Exception {
+ OpenSshKrl krl = new OpenSshKrl(tmp.resolve(data.krl));
+ try (InputStream in = this.getClass()
+ .getResourceAsStream("krl/" + data.key)) {
+ PublicKey key = AuthorizedKeyEntry.readAuthorizedKeys(in, true)
+ .get(0)
+ .resolvePublicKey(null, PublicKeyEntryResolver.FAILING);
+ assertEquals(data.expected, Boolean.valueOf(krl.isRevoked(key)));
+ }
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/SerialRangeSetTest.java b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/SerialRangeSetTest.java
new file mode 100644
index 0000000..e6709ad
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/SerialRangeSetTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.signing.ssh;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+/**
+ * Tests for the set of serial number ranges.
+ */
+public class SerialRangeSetTest {
+
+ private SerialRangeSet ranges = new SerialRangeSet();
+
+ @Test
+ public void testInsertSimple() {
+ ranges.add(1);
+ ranges.add(3);
+ ranges.add(5);
+ assertEquals(3, ranges.size());
+ assertFalse(ranges.contains(0));
+ assertTrue(ranges.contains(1));
+ assertFalse(ranges.contains(2));
+ assertTrue(ranges.contains(3));
+ assertFalse(ranges.contains(4));
+ assertTrue(ranges.contains(5));
+ assertFalse(ranges.contains(6));
+ }
+
+ @Test
+ public void testInsertSimpleRanges() {
+ ranges.add(1, 2);
+ ranges.add(4, 5);
+ ranges.add(7, 8);
+ assertEquals(3, ranges.size());
+ assertFalse(ranges.contains(0));
+ assertTrue(ranges.contains(1));
+ assertTrue(ranges.contains(2));
+ assertFalse(ranges.contains(3));
+ assertTrue(ranges.contains(4));
+ assertTrue(ranges.contains(5));
+ assertFalse(ranges.contains(6));
+ assertTrue(ranges.contains(7));
+ assertTrue(ranges.contains(8));
+ assertFalse(ranges.contains(9));
+ }
+
+ @Test
+ public void testInsertCoalesce() {
+ ranges.add(5);
+ ranges.add(1);
+ ranges.add(2);
+ ranges.add(4);
+ ranges.add(7);
+ ranges.add(3);
+ assertEquals(2, ranges.size());
+ assertFalse(ranges.contains(0));
+ assertTrue(ranges.contains(1));
+ assertTrue(ranges.contains(2));
+ assertTrue(ranges.contains(3));
+ assertTrue(ranges.contains(4));
+ assertTrue(ranges.contains(5));
+ assertFalse(ranges.contains(6));
+ assertTrue(ranges.contains(7));
+ assertFalse(ranges.contains(8));
+ }
+
+ @Test
+ public void testInsertOverlap() {
+ ranges.add(1, 3);
+ ranges.add(6);
+ ranges.add(2, 5);
+ assertEquals(1, ranges.size());
+ assertFalse(ranges.contains(0));
+ assertTrue(ranges.contains(1));
+ assertTrue(ranges.contains(2));
+ assertTrue(ranges.contains(3));
+ assertTrue(ranges.contains(4));
+ assertTrue(ranges.contains(5));
+ assertTrue(ranges.contains(6));
+ assertFalse(ranges.contains(7));
+ }
+
+ @Test
+ public void testInsertOverlapMultiple() {
+ ranges.add(1, 3);
+ ranges.add(5, 6);
+ ranges.add(8);
+ ranges.add(2, 5);
+ assertEquals(2, ranges.size());
+ assertFalse(ranges.contains(0));
+ assertTrue(ranges.contains(1));
+ assertTrue(ranges.contains(2));
+ assertTrue(ranges.contains(3));
+ assertTrue(ranges.contains(4));
+ assertTrue(ranges.contains(5));
+ assertTrue(ranges.contains(6));
+ assertFalse(ranges.contains(7));
+ assertTrue(ranges.contains(8));
+ assertFalse(ranges.contains(9));
+ }
+
+ @Test
+ public void testInsertOverlapTotal() {
+ ranges.add(1, 3);
+ ranges.add(2, 3);
+ assertEquals(1, ranges.size());
+ assertFalse(ranges.contains(0));
+ assertTrue(ranges.contains(1));
+ assertTrue(ranges.contains(2));
+ assertTrue(ranges.contains(3));
+ assertFalse(ranges.contains(4));
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/SshCertificateUtilsTest.java b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/SshCertificateUtilsTest.java
new file mode 100644
index 0000000..79ca21f
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/SshCertificateUtilsTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.signing.ssh;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.InputStream;
+import java.security.PublicKey;
+import java.time.Instant;
+import java.util.List;
+
+import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
+import org.apache.sshd.common.config.keys.OpenSshCertificate;
+import org.apache.sshd.common.config.keys.PublicKeyEntryResolver;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for {@link SshCertificateUtils}. They use a certificate valid from
+ * 2024-09-01 00:00:00 to 2024-09-02 00:00:00 UTC.
+ */
+public class SshCertificateUtilsTest {
+
+ private OpenSshCertificate certificate;
+
+ @Before
+ public void loadCertificate() throws Exception {
+ try (InputStream in = this.getClass().getResourceAsStream(
+ "certs/expired.cert")) {
+ List<AuthorizedKeyEntry> keys = AuthorizedKeyEntry
+ .readAuthorizedKeys(in, true);
+ if (keys.isEmpty()) {
+ certificate = null;
+ }
+ PublicKey key = keys.get(0).resolvePublicKey(null,
+ PublicKeyEntryResolver.FAILING);
+ assertTrue(
+ "Expected an OpenSshKeyCertificate but got a "
+ + key.getClass().getName(),
+ key instanceof OpenSshCertificate);
+ certificate = (OpenSshCertificate) key;
+ }
+ }
+
+ @Test
+ public void testValidUserCertificate() {
+ assertNull(SshCertificateUtils.verify(certificate, null));
+ Instant validTime = Instant.parse("2024-09-01T00:00:00.00Z");
+ assertNull(SshCertificateUtils.verify(certificate, validTime));
+ assertNull(SshCertificateUtils.checkExpiration(certificate, validTime));
+ }
+
+ @Test
+ public void testCheckTooEarly() {
+ Instant invalidTime = Instant.parse("2024-08-31T23:59:59.00Z");
+ assertNotNull(
+ SshCertificateUtils.checkExpiration(certificate, invalidTime));
+ assertNotNull(SshCertificateUtils.verify(certificate, invalidTime));
+ }
+
+ @Test
+ public void testCheckExpired() {
+ Instant invalidTime = Instant.parse("2024-09-02T00:00:01.00Z");
+ assertNotNull(
+ SshCertificateUtils.checkExpiration(certificate, invalidTime));
+ assertNotNull(SshCertificateUtils.verify(certificate, invalidTime));
+ }
+
+ @Test
+ public void testInvalidSignature() throws Exception {
+ // Modify the serialized certificate, then re-load it again. To check that
+ // serialization per se works fine, also check an unmodified version.
+ Buffer buffer = new ByteArrayBuffer();
+ buffer.putPublicKey(certificate);
+ int pos = buffer.rpos();
+ PublicKey unchanged = buffer.getPublicKey();
+ assertTrue(
+ "Expected an OpenSshCertificate but got a "
+ + unchanged.getClass().getName(),
+ unchanged instanceof OpenSshCertificate);
+ assertNull(SshCertificateUtils.verify((OpenSshCertificate) unchanged,
+ null));
+ buffer.rpos(pos);
+ // Change a byte. The test certificate has the key ID at offset 128.
+ // Changing a byte in the key ID should still result in a successful
+ // deserialization, but then fail the signature check.
+ buffer.array()[pos + 128]++;
+ PublicKey changed = buffer.getPublicKey();
+ assertTrue(
+ "Expected an OpenSshCertificate but got a "
+ + changed.getClass().getName(),
+ changed instanceof OpenSshCertificate);
+ assertNotNull(
+ SshCertificateUtils.verify((OpenSshCertificate) changed, null));
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/SshSignatureVerifierTest.java b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/SshSignatureVerifierTest.java
new file mode 100644
index 0000000..e5dfe49
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/SshSignatureVerifierTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.signing.ssh;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Map;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.VerificationResult;
+import org.eclipse.jgit.lib.SignatureVerifier;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.util.StringUtils;
+import org.junit.Test;
+
+/**
+ * Tests for the {@link SshSignatureVerifier}.
+ */
+public class SshSignatureVerifierTest extends AbstractSshSignatureTest {
+
+ @Test
+ public void testPlainSignature() throws Exception {
+ RevCommit c = checkSshSignature(
+ createSignedCommit(null, "signing_key.pub"));
+ try (Git git = new Git(db)) {
+ Map<String, VerificationResult> results = git.verifySignature()
+ .addName(c.getName()).call();
+ assertEquals(1, results.size());
+ VerificationResult verified = results.get(c.getName());
+ assertNotNull(verified);
+ assertNull(verified.getException());
+ SignatureVerifier.SignatureVerification v = verified
+ .getVerification();
+ assertTrue(v.verified());
+ assertFalse(v.expired());
+ assertTrue(StringUtils.isEmptyOrNull(v.message()));
+ assertEquals("tester@example.com", v.keyUser());
+ assertEquals("SHA256:GKW0xy+XKnJGs0CJqP6j5bd4FdiwWNaUbwvUbHvhQKo",
+ v.keyFingerprint());
+ assertEquals(SignatureVerifier.TrustLevel.FULL, v.trustLevel());
+ assertEquals(commitTime, v.creationDate().toInstant());
+ }
+ }
+
+ @Test
+ public void testCertificateSignature() throws Exception {
+ RevCommit c = checkSshSignature(
+ createSignedCommit("tester.cert", "signing_key-cert.pub"));
+ try (Git git = new Git(db)) {
+ Map<String, VerificationResult> results = git.verifySignature()
+ .addName(c.getName()).call();
+ assertEquals(1, results.size());
+ VerificationResult verified = results.get(c.getName());
+ assertNotNull(verified);
+ assertNull(verified.getException());
+ SignatureVerifier.SignatureVerification v = verified
+ .getVerification();
+ assertTrue(v.verified());
+ assertFalse(v.expired());
+ assertTrue(StringUtils.isEmptyOrNull(v.message()));
+ assertEquals("tester@example.com", v.keyUser());
+ assertEquals("SHA256:GKW0xy+XKnJGs0CJqP6j5bd4FdiwWNaUbwvUbHvhQKo",
+ v.keyFingerprint());
+ assertEquals(SignatureVerifier.TrustLevel.FULL, v.trustLevel());
+ assertEquals(commitTime, v.creationDate().toInstant());
+ }
+ }
+
+ @Test
+ public void testNoPrincipalsSignature() throws Exception {
+ RevCommit c = checkSshSignature(createSignedCommit("no_principals.cert",
+ "signing_key-cert.pub"));
+ try (Git git = new Git(db)) {
+ Map<String, VerificationResult> results = git.verifySignature()
+ .addName(c.getName()).call();
+ assertEquals(1, results.size());
+ VerificationResult verified = results.get(c.getName());
+ assertNotNull(verified);
+ assertNull(verified.getException());
+ SignatureVerifier.SignatureVerification v = verified
+ .getVerification();
+ assertFalse(v.verified());
+ assertFalse(v.expired());
+ assertNull(v.keyUser());
+ assertEquals("SHA256:GKW0xy+XKnJGs0CJqP6j5bd4FdiwWNaUbwvUbHvhQKo",
+ v.keyFingerprint());
+ assertEquals(SignatureVerifier.TrustLevel.NEVER, v.trustLevel());
+ assertTrue(v.message().contains("*@example.com"));
+ assertEquals(commitTime, v.creationDate().toInstant());
+ }
+ }
+
+ @Test
+ public void testOtherCertificateSignature() throws Exception {
+ RevCommit c = checkSshSignature(
+ createSignedCommit("other.cert", "signing_key-cert.pub"));
+ try (Git git = new Git(db)) {
+ Map<String, VerificationResult> results = git.verifySignature()
+ .addName(c.getName()).call();
+ assertEquals(1, results.size());
+ VerificationResult verified = results.get(c.getName());
+ assertNotNull(verified);
+ assertNull(verified.getException());
+ SignatureVerifier.SignatureVerification v = verified
+ .getVerification();
+ assertTrue(v.verified());
+ assertFalse(v.expired());
+ assertTrue(StringUtils.isEmptyOrNull(v.message()));
+ assertEquals("other@example.com", v.keyUser());
+ assertEquals("SHA256:GKW0xy+XKnJGs0CJqP6j5bd4FdiwWNaUbwvUbHvhQKo",
+ v.keyFingerprint());
+ assertEquals(SignatureVerifier.TrustLevel.FULL, v.trustLevel());
+ assertEquals(commitTime, v.creationDate().toInstant());
+ }
+ }
+
+ @Test
+ public void testTwoPrincipalsCertificateSignature() throws Exception {
+ RevCommit c = checkSshSignature(createSignedCommit(
+ "two_principals.cert", "signing_key-cert.pub"));
+ try (Git git = new Git(db)) {
+ Map<String, VerificationResult> results = git.verifySignature()
+ .addName(c.getName()).call();
+ assertEquals(1, results.size());
+ VerificationResult verified = results.get(c.getName());
+ assertNotNull(verified);
+ assertNull(verified.getException());
+ SignatureVerifier.SignatureVerification v = verified
+ .getVerification();
+ assertTrue(v.verified());
+ assertFalse(v.expired());
+ assertTrue(StringUtils.isEmptyOrNull(v.message()));
+ assertEquals("foo@example.com,tester@example.com", v.keyUser());
+ assertEquals("SHA256:GKW0xy+XKnJGs0CJqP6j5bd4FdiwWNaUbwvUbHvhQKo",
+ v.keyFingerprint());
+ assertEquals(SignatureVerifier.TrustLevel.FULL, v.trustLevel());
+ assertEquals(commitTime, v.creationDate().toInstant());
+ }
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/SshSignerTest.java b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/SshSignerTest.java
new file mode 100644
index 0000000..b3a4482
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/SshSignerTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.signing.ssh;
+
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+
+/**
+ * Tests for the {@link SshSigner}.
+ */
+public class SshSignerTest extends AbstractSshSignatureTest {
+
+ @Test
+ public void testPlainSignature() throws Exception {
+ checkSshSignature(createSignedCommit(null, "signing_key.pub"));
+ }
+
+ @Test
+ public void testExpiredSignature() throws Exception {
+ Throwable t = assertThrows(Throwable.class,
+ () -> createSignedCommit("expired.cert",
+ "signing_key-cert.pub"));
+ // The exception or one of its causes should mention "[Ee]xpired" and
+ // "[Cc]ertificate" in the message
+ while (t != null) {
+ String message = t.getMessage();
+ if (message.contains("xpired") && message.contains("ertificate")) {
+ return;
+ }
+ t = t.getCause();
+ }
+ fail("Expected exception message not found");
+ }
+
+ @Test
+ public void testCertificateSignature() throws Exception {
+ checkSshSignature(createSignedCommit("tester.cert", "signing_key.pub"));
+ }
+
+ @Test
+ public void testNoPrincipalsSignature() throws Exception {
+ // Certificate has no principals; should still work
+ checkSshSignature(
+ createSignedCommit("no_principals.cert", "signing_key.pub"));
+ }
+
+ @Test
+ public void testOtherSignature() throws Exception {
+ // Certificate has a principal different that tester@example.com; should
+ // still work
+ checkSshSignature(createSignedCommit("other.cert", "signing_key.pub"));
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/VerifyGitSignaturesTest.java b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/VerifyGitSignaturesTest.java
new file mode 100644
index 0000000..30ddee5
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/signing/ssh/VerifyGitSignaturesTest.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.signing.ssh;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.io.File;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.VerificationResult;
+import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.SignatureVerifier;
+import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.util.GitDateFormatter;
+import org.eclipse.jgit.util.SignatureUtils;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Verifies signatures made with C git and OpenSSH 9.0 to ensure we arrive at
+ * the same good/bad decisions, and that we can verify signatures not created by
+ * ourselves.
+ * <p>
+ * Clones a JGit repo from a git bundle file created with C git, then checks all
+ * the commits and their signatures. (All commits in that bundle have SSH
+ * signatures.)
+ * </p>
+ */
+public class VerifyGitSignaturesTest extends LocalDiskRepositoryTestCase {
+
+ private static final Logger LOG = LoggerFactory
+ .getLogger(VerifyGitSignaturesTest.class);
+
+ @Rule
+ public TemporaryFolder bundleDir = new TemporaryFolder();
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ try (InputStream in = this.getClass()
+ .getResourceAsStream("repo.bundle")) {
+ Files.copy(in, bundleDir.getRoot().toPath().resolve("repo.bundle"));
+ }
+ try (InputStream in = this.getClass()
+ .getResourceAsStream("allowed_signers")) {
+ Files.copy(in,
+ bundleDir.getRoot().toPath().resolve("allowed_signers"));
+ }
+ }
+
+ /**
+ * Tests signatures created by C git using OpenSSH 9.0.
+ */
+ @Test
+ public void testGitSignatures() throws Exception {
+ File gitDir = new File(getTemporaryDirectory(), "repo.git");
+ try (Git git = Git.cloneRepository().setBare(true)
+ .setGitDir(gitDir)
+ .setURI(new File(bundleDir.getRoot(), "repo.bundle").toURI()
+ .toString())
+ .setBranch("master")
+ .call()) {
+ StoredConfig config = git.getRepository().getConfig();
+ config.setString("gpg", "ssh", "allowedSignersFile",
+ bundleDir.getRoot().toPath().resolve("allowed_signers")
+ .toAbsolutePath().toString().replace('\\', '/'));
+ config.save();
+ List<String> commits = new ArrayList<>();
+ Map<String, PersonIdent> committers = new HashMap<>();
+ git.log().all().call().forEach(c -> {
+ commits.add(c.getName());
+ committers.put(c.getName(), c.getCommitterIdent());
+ });
+ Map<String, Boolean> expected = new HashMap<>();
+ // These two commits do have multiple principals. GIT just reports
+ // the first one; we report both.
+ expected.put("9f79a7b661a22ab1ddf8af880d23678ae7696b71",
+ Boolean.TRUE);
+ expected.put("435108d157440e77d61a914b6a5736bc831c874d",
+ Boolean.TRUE);
+ // This commit has a wrong commit message; the certificate used
+ // did _not_ have two principals, but only a single principal
+ // foo@example.org.
+ expected.put("779dac7de40ebc3886af87d5e6680a09f8b13a3e",
+ Boolean.TRUE);
+ // Signed with other_key-cert.pub: we still don't know the key,
+ // but we do know the certificate's CA key, and trust it, so it's
+ // accepted as a signature from the principal(s) listed in the
+ // certificate.
+ expected.put("951f06d5b5598b721b98d98b04e491f234c1926a",
+ Boolean.TRUE);
+ // Signature with other_key.pub not listed in allowed_signers
+ expected.put("984e629c6d543a7f77eb49a8c9316f2ae4416375",
+ Boolean.FALSE);
+ // Signed with other-ca.cert (CA key not in allowed_signers), but
+ // the certified key _is_ listed in allowed_signers.
+ expected.put("1d7ac6d91747a9c9a777df238fbdaeffa7731a6c",
+ Boolean.FALSE);
+ expected.put("a297bcfbf5c4a850f9770655fef7315328a4b3fb",
+ Boolean.TRUE);
+ expected.put("852729d54676cb83826ed821dc7734013e97950d",
+ Boolean.TRUE);
+ // Signature with a certificate without principals.
+ expected.put("e39a049f75fe127eb74b30aba4b64e171d4281dd",
+ Boolean.FALSE);
+ // Signature made with expired.cert (expired at the commit time).
+ // git/OpenSSH 9.0 allows to create such signatures, but reports
+ // them as FALSE. Our SshSigner doesn't allow creating such
+ // signatures.
+ expected.put("303ea5e61feacdad4cb012b4cb6b0cea3fbcef9f",
+ Boolean.FALSE);
+ expected.put("1ae4b120a869b72a7a2d4ad4d7a8c9d454384333",
+ Boolean.TRUE);
+ Map<String, VerificationResult> results = git.verifySignature()
+ .addNames(commits).call();
+ GitDateFormatter dateFormat = new GitDateFormatter(
+ GitDateFormatter.Format.ISO);
+ for (String oid : commits) {
+ VerificationResult v = results.get(oid);
+ assertNotNull(v);
+ assertNull(v.getException());
+ SignatureVerifier.SignatureVerification sv = v
+ .getVerification();
+ assertNotNull(sv);
+ LOG.info("Commit {}\n{}", oid, SignatureUtils.toString(sv,
+ committers.get(oid), dateFormat));
+ Boolean wanted = expected.get(oid);
+ assertNotNull(wanted);
+ assertEquals(wanted, Boolean.valueOf(sv.verified()));
+ }
+ }
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/transport/sshd/KnownHostEntryReaderTest.java b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/transport/sshd/KnownHostEntryReaderTest.java
new file mode 100644
index 0000000..d36c38f
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/transport/sshd/KnownHostEntryReaderTest.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.transport.sshd;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import org.apache.sshd.client.config.hosts.KnownHostEntry;
+import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
+import org.junit.Test;
+
+public class KnownHostEntryReaderTest {
+
+ @Test
+ public void testUnsupportedHostKeyLine() {
+ KnownHostEntry entry = KnownHostEntryReader.parseHostEntry(
+ "[localhost]:2222 ssh-unknown AAAAC3NzaC1lZDI1NTE5AAAAIPu6ntmyfSOkqLl3qPxD5XxwW7OONwwSG3KO+TGn+PFu");
+ AuthorizedKeyEntry keyEntry = entry.getKeyEntry();
+ assertNotNull(keyEntry);
+ assertEquals("ssh-unknown", keyEntry.getKeyType());
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyDatabaseTest.java b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyDatabaseTest.java
new file mode 100644
index 0000000..6b61821
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyDatabaseTest.java
@@ -0,0 +1,586 @@
+/*
+ * Copyright (C) 2025 Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.transport.sshd;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PublicKey;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.sshd.certificate.OpenSshCertificateBuilder;
+import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.PublicKeyEntry;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.errors.UnsupportedCredentialItem;
+import org.eclipse.jgit.transport.CredentialItem;
+import org.eclipse.jgit.transport.CredentialsProvider;
+import org.eclipse.jgit.transport.URIish;
+import org.eclipse.jgit.transport.sshd.ServerKeyDatabase;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+/**
+ * Tests for {@link OpenSshServerKeyDatabase}.
+ */
+public class OpenSshServerKeyDatabaseTest {
+
+ private static final InetSocketAddress LOCAL = new InetSocketAddress(
+ InetAddress.getLoopbackAddress(), SshConstants.DEFAULT_PORT);
+
+ private static final InetSocketAddress LOCAL_29418 = new InetSocketAddress(
+ InetAddress.getLoopbackAddress(), 29418);
+
+ private static PublicKey rsa1024;
+ private static PublicKey rsa2048;
+ private static PublicKey ec256;
+ private static PublicKey ec384;
+ private static PublicKey caKey;
+ private static PublicKey certificate;
+
+ @BeforeClass
+ public static void initKeys() throws Exception {
+ // Generate a few keys that we can use
+ KeyPairGenerator gen = SecurityUtils.getKeyPairGenerator(KeyUtils.RSA_ALGORITHM);
+ gen.initialize(1024);
+ rsa1024 = gen.generateKeyPair().getPublic();
+ gen.initialize(2048);
+ rsa2048 = gen.generateKeyPair().getPublic();
+ gen = SecurityUtils.getKeyPairGenerator(KeyUtils.EC_ALGORITHM);
+ ECCurves curve = ECCurves.fromCurveSize(256);
+ gen.initialize(curve.getParameters());
+ ec256 = gen.generateKeyPair().getPublic();
+ PublicKey certKey = gen.generateKeyPair().getPublic();
+ curve = ECCurves.fromCurveSize(384);
+ gen.initialize(curve.getParameters());
+ ec384 = gen.generateKeyPair().getPublic();
+ // Generate a certificate for some key
+ gen.initialize(curve.getParameters());
+ KeyPair ca = gen.generateKeyPair();
+ caKey = ca.getPublic();
+ certificate = OpenSshCertificateBuilder
+ .hostCertificate()
+ .serial(System.currentTimeMillis())
+ .publicKey(certKey)
+ .id("test-host-cert")
+ .validBefore(
+ System.currentTimeMillis() + TimeUnit.HOURS.toMillis(1))
+ .principals(List.of("localhost", "127.0.0.1"))
+ .sign(ca, "ecdsa-sha2-nistp384");
+ }
+
+ @Rule
+ public TemporaryFolder tmp = new TemporaryFolder();
+
+ private Path knownHosts;
+ private Path knownHosts2;
+ private ServerKeyDatabase database;
+
+ @Before
+ public void setupDatabase() throws Exception {
+ Path root = tmp.getRoot().toPath();
+ knownHosts = root.resolve("known_hosts");
+ knownHosts2 = root.resolve("known_hosts2");
+ database = new OpenSshServerKeyDatabase(false, List.of(knownHosts, knownHosts2));
+ }
+
+ @Test
+ public void testFindInSecondFile() throws Exception {
+ Files.write(knownHosts,
+ List.of(
+ "some.other.host " + PublicKeyEntry.toString(rsa1024),
+ "some.other.com " + PublicKeyEntry.toString(ec384)));
+ Files.write(knownHosts2,
+ List.of(
+ "localhost,127.0.0.1 " + PublicKeyEntry.toString(ec256),
+ "some.other.com " + PublicKeyEntry.toString(rsa2048)));
+ assertTrue(database.accept("localhost", LOCAL, ec256,
+ new KnownHostsConfig(), null));
+ assertFalse(database.accept("localhost", LOCAL, ec384,
+ new KnownHostsConfig(), null));
+ }
+
+ @Test
+ public void testNoFirstFile() throws Exception {
+ Files.write(knownHosts2,
+ List.of("localhost,127.0.0.1 " + PublicKeyEntry.toString(ec256),
+ "some.other.com " + PublicKeyEntry.toString(rsa2048)));
+ assertTrue(database.accept("localhost", LOCAL, ec256,
+ new KnownHostsConfig(), null));
+ }
+
+ @Test
+ public void testFind() throws Exception {
+ Files.write(knownHosts,
+ List.of("localhost,127.0.0.1 "
+ + PublicKeyEntry.toString(rsa1024),
+ "some.other.com " + PublicKeyEntry.toString(ec384)));
+ Files.write(knownHosts2,
+ List.of("localhost,127.0.0.1 " + PublicKeyEntry.toString(ec256),
+ "some.other.com " + PublicKeyEntry.toString(rsa2048)));
+ assertTrue(database.accept("localhost", LOCAL, ec256,
+ new KnownHostsConfig(), null));
+ assertTrue(database.accept("localhost:22", LOCAL, ec256,
+ new KnownHostsConfig(), null));
+ assertTrue(database.accept("127.0.0.1", LOCAL, rsa1024,
+ new KnownHostsConfig(), null));
+ assertTrue(database.accept("[127.0.0.1]:22", LOCAL, rsa1024,
+ new KnownHostsConfig(), null));
+ assertFalse(database.accept("localhost:29418", LOCAL_29418, ec256,
+ new KnownHostsConfig(), null));
+ assertFalse(database.accept("localhost", LOCAL, ec384,
+ new KnownHostsConfig(), null));
+ }
+
+ @Test
+ public void testFindCertificate() throws Exception {
+ Files.write(knownHosts,
+ List.of("localhost,127.0.0.1 "
+ + PublicKeyEntry.toString(rsa1024),
+ "some.other.com " + PublicKeyEntry.toString(ec384),
+ "@cert-authority localhost,127.0.0.1 "
+ + PublicKeyEntry.toString(caKey)));
+ assertTrue(database.accept("localhost", LOCAL, certificate,
+ new KnownHostsConfig(), null));
+ }
+
+ @Test
+ public void testCaKeyNotConsidered() throws Exception {
+ Files.write(knownHosts,
+ List.of("localhost,127.0.0.1 "
+ + PublicKeyEntry.toString(rsa1024),
+ "some.other.com " + PublicKeyEntry.toString(ec384),
+ "@cert-authority localhost,127.0.0.1 "
+ + PublicKeyEntry.toString(ec256)));
+ assertFalse(database.accept("localhost", LOCAL, ec256,
+ new KnownHostsConfig(), null));
+ }
+
+ @Test
+ public void testkeyPlainAndCa() throws Exception {
+ Files.write(knownHosts, List.of(
+ "localhost,127.0.0.1 " + PublicKeyEntry.toString(rsa1024),
+ "some.other.com " + PublicKeyEntry.toString(ec384),
+ "@cert-authority localhost,127.0.0.1 "
+ + PublicKeyEntry.toString(ec256),
+ "localhost,127.0.0.1 " + PublicKeyEntry.toString(ec256)));
+ // ec256 is a CA key, but also a valid direct host key for localhost
+ assertTrue(database.accept("localhost", LOCAL, ec256,
+ new KnownHostsConfig(), null));
+ }
+
+ @Test
+ public void testLookupCertificate() throws Exception {
+ List<PublicKey> keys = database.lookup("localhost", LOCAL,
+ new KnownHostsConfig());
+ // Certificates or CA keys are not reported via lookup.
+ assertTrue(keys.isEmpty());
+ }
+
+ @Test
+ public void testCertificateNotAdded() throws Exception {
+ List<String> initialKnownHosts = List.of(
+ "localhost,127.0.0.1 " + PublicKeyEntry.toString(rsa1024),
+ "some.other.com " + PublicKeyEntry.toString(ec384));
+ Files.write(knownHosts, initialKnownHosts);
+ assertFalse(database.accept("localhost", LOCAL, certificate,
+ new KnownHostsConfig(), null));
+ TestCredentialsProvider ui = new TestCredentialsProvider(true, true);
+ assertFalse(
+ database.accept("localhost", LOCAL, certificate,
+ new KnownHostsConfig(
+ KnownHostsConfig.StrictHostKeyChecking.ASK),
+ ui));
+ assertEquals(0, ui.invocations);
+ assertFile(knownHosts, initialKnownHosts);
+ }
+
+ @Test
+ public void testCertificateNotModified() throws Exception {
+ List<String> initialKnownHosts = List.of(
+ "@cert-authority localhost,127.0.0.1 "
+ + PublicKeyEntry.toString(ec384),
+ "some.other.com " + PublicKeyEntry.toString(ec256));
+ Files.write(knownHosts, initialKnownHosts);
+ assertFalse(database.accept("localhost", LOCAL, certificate,
+ new KnownHostsConfig(), null));
+ TestCredentialsProvider ui = new TestCredentialsProvider(true, true);
+ assertFalse(
+ database.accept("localhost", LOCAL, certificate,
+ new KnownHostsConfig(
+ KnownHostsConfig.StrictHostKeyChecking.ASK),
+ ui));
+ assertEquals(0, ui.invocations);
+ assertFile(knownHosts, initialKnownHosts);
+ }
+
+ @Test
+ public void testModifyFile() throws Exception {
+ List<String> initialKnownHosts = List.of(
+ "some.other.host " + PublicKeyEntry.toString(rsa1024),
+ "some.other.com " + PublicKeyEntry.toString(ec384));
+ Files.write(knownHosts, initialKnownHosts);
+ List<String> initialKnownHosts2 = List.of(
+ "localhost,127.0.0.1 " + PublicKeyEntry.toString(ec256),
+ "some.other.com " + PublicKeyEntry.toString(rsa2048));
+ Files.write(knownHosts2, initialKnownHosts2);
+ assertFalse(database.accept("localhost", LOCAL, ec384,
+ new KnownHostsConfig(), null));
+ assertFile(knownHosts, initialKnownHosts);
+ assertFile(knownHosts2, initialKnownHosts2);
+ assertFalse(database.accept("localhost", LOCAL, ec384,
+ new KnownHostsConfig(
+ ServerKeyDatabase.Configuration.StrictHostKeyChecking.ACCEPT_NEW),
+ null));
+ assertFile(knownHosts, initialKnownHosts);
+ assertFile(knownHosts2, initialKnownHosts2);
+ assertTrue(database.accept("localhost", LOCAL, ec384,
+ new KnownHostsConfig(
+ ServerKeyDatabase.Configuration.StrictHostKeyChecking.ACCEPT_ANY),
+ null));
+ assertFile(knownHosts, initialKnownHosts);
+ assertFile(knownHosts2, initialKnownHosts2);
+ assertFalse(database.accept("localhost", LOCAL, ec384,
+ new KnownHostsConfig(
+ ServerKeyDatabase.Configuration.StrictHostKeyChecking.ASK),
+ null));
+ assertFile(knownHosts, initialKnownHosts);
+ assertFile(knownHosts2, initialKnownHosts2);
+
+ TestCredentialsProvider ui = new TestCredentialsProvider(true, false);
+ assertTrue(database.accept("localhost", LOCAL, ec384,
+ new KnownHostsConfig(
+ ServerKeyDatabase.Configuration.StrictHostKeyChecking.ASK),
+ ui));
+ assertEquals(1, ui.invocations);
+ assertFile(knownHosts, initialKnownHosts);
+ assertFile(knownHosts2, initialKnownHosts2);
+
+ ui = new TestCredentialsProvider(true, true);
+ assertTrue(database.accept("localhost", LOCAL, ec384,
+ new KnownHostsConfig(
+ ServerKeyDatabase.Configuration.StrictHostKeyChecking.ASK),
+ ui));
+ assertEquals(1, ui.invocations);
+ assertFile(knownHosts, initialKnownHosts);
+ assertFile(knownHosts2, List.of(
+ "localhost,127.0.0.1 " + PublicKeyEntry.toString(ec384),
+ "some.other.com " + PublicKeyEntry.toString(rsa2048)));
+ assertTrue(database.accept("127.0.0.1", LOCAL, ec384,
+ new KnownHostsConfig(), null));
+ }
+
+ @Test
+ public void testModifyFirstFile() throws Exception {
+ List<String> initialKnownHosts = List.of(
+ "localhost,127.0.0.1 " + PublicKeyEntry.toString(rsa1024),
+ "some.other.com " + PublicKeyEntry.toString(ec384));
+ Files.write(knownHosts, initialKnownHosts);
+ List<String> initialKnownHosts2 = List.of(
+ "some.other.host " + PublicKeyEntry.toString(ec256),
+ "some.other.com " + PublicKeyEntry.toString(rsa2048));
+ Files.write(knownHosts2, initialKnownHosts2);
+ TestCredentialsProvider ui = new TestCredentialsProvider(true, true);
+ assertTrue(database.accept("localhost", LOCAL, ec384,
+ new KnownHostsConfig(
+ ServerKeyDatabase.Configuration.StrictHostKeyChecking.ASK),
+ ui));
+ assertEquals(1, ui.invocations);
+ assertFile(knownHosts,
+ List.of("localhost,127.0.0.1 " + PublicKeyEntry.toString(ec384),
+ "some.other.com " + PublicKeyEntry.toString(ec384)));
+ assertFile(knownHosts2, initialKnownHosts2);
+ assertTrue(database.accept("127.0.0.1:22", LOCAL, ec384,
+ new KnownHostsConfig(), null));
+ }
+
+ @Test
+ public void testModifyMatchingKeyType() throws Exception {
+ List<String> initialKnownHosts = List.of(
+ "localhost,127.0.0.1 " + PublicKeyEntry.toString(rsa1024),
+ "some.other.com " + PublicKeyEntry.toString(ec384));
+ Files.write(knownHosts, initialKnownHosts);
+ List<String> initialKnownHosts2 = List.of(
+ "localhost,127.0.0.1 " + PublicKeyEntry.toString(ec256),
+ "some.other.com " + PublicKeyEntry.toString(rsa2048));
+ Files.write(knownHosts2, initialKnownHosts2);
+ TestCredentialsProvider ui = new TestCredentialsProvider(true, true);
+ assertTrue(database.accept("localhost", LOCAL, rsa2048,
+ new KnownHostsConfig(
+ ServerKeyDatabase.Configuration.StrictHostKeyChecking.ASK),
+ ui));
+ assertEquals(1, ui.invocations);
+ assertFile(knownHosts,
+ List.of("localhost,127.0.0.1 "
+ + PublicKeyEntry.toString(rsa2048),
+ "some.other.com " + PublicKeyEntry.toString(ec384)));
+ assertFile(knownHosts2, initialKnownHosts2);
+ assertTrue(database.accept("127.0.0.1:22", LOCAL, rsa2048,
+ new KnownHostsConfig(), null));
+ }
+
+ @Test
+ public void testModifyMatchingKeyType2() throws Exception {
+ List<String> initialKnownHosts = List.of(
+ "localhost,127.0.0.1 " + PublicKeyEntry.toString(ec256),
+ "some.other.com " + PublicKeyEntry.toString(ec384));
+ Files.write(knownHosts, initialKnownHosts);
+ List<String> initialKnownHosts2 = List.of(
+ "localhost,127.0.0.1 " + PublicKeyEntry.toString(rsa1024),
+ "some.other.com " + PublicKeyEntry.toString(rsa2048));
+ Files.write(knownHosts2, initialKnownHosts2);
+ TestCredentialsProvider ui = new TestCredentialsProvider(true, true);
+ assertTrue(database.accept("localhost", LOCAL, ec384,
+ new KnownHostsConfig(
+ ServerKeyDatabase.Configuration.StrictHostKeyChecking.ASK),
+ ui));
+ assertEquals(1, ui.invocations);
+ assertFile(knownHosts,
+ List.of("localhost,127.0.0.1 " + PublicKeyEntry.toString(ec384),
+ "some.other.com " + PublicKeyEntry.toString(ec384)));
+ assertFile(knownHosts2, initialKnownHosts2);
+ assertTrue(database.accept("127.0.0.1:22", LOCAL, ec384,
+ new KnownHostsConfig(), null));
+ }
+
+ @Test
+ public void testModifySecondFile() throws Exception {
+ List<String> initialKnownHosts = List.of(
+ "localhost,127.0.0.1 " + PublicKeyEntry.toString(rsa1024),
+ "some.other.com " + PublicKeyEntry.toString(ec384));
+ Files.write(knownHosts, initialKnownHosts);
+ List<String> initialKnownHosts2 = List.of(
+ "localhost,127.0.0.1 " + PublicKeyEntry.toString(ec256),
+ "some.other.com " + PublicKeyEntry.toString(rsa2048));
+ Files.write(knownHosts2, initialKnownHosts2);
+ TestCredentialsProvider ui = new TestCredentialsProvider(true, true);
+ assertTrue(database.accept("localhost", LOCAL, ec384,
+ new KnownHostsConfig(
+ ServerKeyDatabase.Configuration.StrictHostKeyChecking.ASK),
+ ui));
+ assertEquals(1, ui.invocations);
+ assertFile(knownHosts, initialKnownHosts);
+ assertFile(knownHosts2,
+ List.of("localhost,127.0.0.1 " + PublicKeyEntry.toString(ec384),
+ "some.other.com " + PublicKeyEntry.toString(rsa2048)));
+ assertTrue(database.accept("127.0.0.1:22", LOCAL, ec384,
+ new KnownHostsConfig(), null));
+ }
+
+ @Test
+ public void testAddNewKey() throws Exception {
+ List<String> initialKnownHosts = List.of(
+ "some.other.host " + PublicKeyEntry.toString(rsa1024),
+ "some.other.com " + PublicKeyEntry.toString(ec256));
+ Files.write(knownHosts, initialKnownHosts);
+ List<String> initialKnownHosts2 = List
+ .of("some.other.com " + PublicKeyEntry.toString(rsa2048));
+ Files.write(knownHosts2, initialKnownHosts2);
+ TestCredentialsProvider ui = new TestCredentialsProvider(true, true);
+ assertTrue(database.accept("localhost", LOCAL, ec384,
+ new KnownHostsConfig(
+ ServerKeyDatabase.Configuration.StrictHostKeyChecking.ASK),
+ ui));
+ assertEquals(1, ui.invocations);
+ List<String> expected = new ArrayList<>(initialKnownHosts);
+ expected.add("localhost,127.0.0.1 " + PublicKeyEntry.toString(ec384));
+ assertFile(knownHosts, expected);
+ assertFile(knownHosts2, initialKnownHosts2);
+ assertTrue(database.accept("127.0.0.1:22", LOCAL, ec384,
+ new KnownHostsConfig(), null));
+ }
+
+ @Test
+ public void testCreateNewFile() throws Exception {
+ List<String> initialKnownHosts2 = List
+ .of("some.other.com " + PublicKeyEntry.toString(ec256));
+ Files.write(knownHosts2, initialKnownHosts2);
+ TestCredentialsProvider ui = new TestCredentialsProvider(true, true);
+ assertTrue(database.accept("localhost", LOCAL, ec384,
+ new KnownHostsConfig(
+ ServerKeyDatabase.Configuration.StrictHostKeyChecking.ASK),
+ ui));
+ assertEquals(1, ui.invocations);
+ assertFile(knownHosts, List
+ .of("localhost,127.0.0.1 " + PublicKeyEntry.toString(ec384)));
+ assertFile(knownHosts2, initialKnownHosts2);
+ assertTrue(database.accept("127.0.0.1:22", LOCAL, ec384,
+ new KnownHostsConfig(), null));
+ }
+
+ @Test
+ public void testAddNewKey2() throws Exception {
+ List<String> initialKnownHosts = List.of(
+ "some.other.host " + PublicKeyEntry.toString(rsa1024),
+ "some.other.com " + PublicKeyEntry.toString(ec256));
+ Files.write(knownHosts, initialKnownHosts);
+ List<String> initialKnownHosts2 = List
+ .of("some.other.com " + PublicKeyEntry.toString(rsa2048));
+ Files.write(knownHosts2, initialKnownHosts2);
+ TestCredentialsProvider ui = new TestCredentialsProvider(true, true);
+ assertTrue(database.accept("127.0.0.1:29418", LOCAL_29418, ec384,
+ new KnownHostsConfig(
+ ServerKeyDatabase.Configuration.StrictHostKeyChecking.ASK),
+ ui));
+ assertEquals(1, ui.invocations);
+ List<String> expected = new ArrayList<>(initialKnownHosts);
+ expected.add("[127.0.0.1]:29418,[localhost]:29418 "
+ + PublicKeyEntry.toString(ec384));
+ assertFile(knownHosts, expected);
+ assertFile(knownHosts2, initialKnownHosts2);
+ assertTrue(database.accept("localhost:29418", LOCAL_29418, ec384,
+ new KnownHostsConfig(), null));
+ }
+
+ @Test
+ public void testAddNewKey3() throws Exception {
+ List<String> initialKnownHosts = List.of(
+ "some.other.host " + PublicKeyEntry.toString(rsa1024),
+ "some.other.com " + PublicKeyEntry.toString(ec256));
+ Files.write(knownHosts, initialKnownHosts);
+ List<String> initialKnownHosts2 = List
+ .of("some.other.com " + PublicKeyEntry.toString(rsa2048));
+ Files.write(knownHosts2, initialKnownHosts2);
+ TestCredentialsProvider ui = new TestCredentialsProvider(true, true);
+ assertTrue(database.accept("localhost:29418", LOCAL_29418, ec384,
+ new KnownHostsConfig(
+ ServerKeyDatabase.Configuration.StrictHostKeyChecking.ASK),
+ ui));
+ assertEquals(1, ui.invocations);
+ List<String> expected = new ArrayList<>(initialKnownHosts);
+ expected.add("[localhost]:29418,[127.0.0.1]:29418 "
+ + PublicKeyEntry.toString(ec384));
+ assertFile(knownHosts, expected);
+ assertFile(knownHosts2, initialKnownHosts2);
+ assertTrue(database.accept("127.0.0.1:29418", LOCAL_29418, ec384,
+ new KnownHostsConfig(), null));
+ }
+
+ @Test
+ public void testUnknownKeyType() throws Exception {
+ List<String> initialKnownHosts = List.of(
+ "localhost,127.0.0.1 " + PublicKeyEntry.toString(ec384)
+ .replace("ecdsa", "foo"),
+ "some.other.com " + PublicKeyEntry.toString(ec384));
+ Files.write(knownHosts, initialKnownHosts);
+ TestCredentialsProvider ui = new TestCredentialsProvider(true, true);
+ assertTrue(database.accept("localhost", LOCAL, ec384,
+ new KnownHostsConfig(
+ ServerKeyDatabase.Configuration.StrictHostKeyChecking.ASK),
+ ui));
+ assertEquals(1, ui.invocations);
+ // The "modified key" dialog has two questions; whereas the "add new
+ // key" is just a simple question.
+ assertEquals(2, ui.questions);
+ List<String> expected = new ArrayList<>(initialKnownHosts);
+ expected.add("localhost,127.0.0.1 " + PublicKeyEntry.toString(ec384));
+ assertFile(knownHosts, expected);
+ assertTrue(database.accept("127.0.0.1:22", LOCAL, ec384,
+ new KnownHostsConfig(), null));
+ }
+
+ private void assertFile(Path path, List<String> lines) throws Exception {
+ assertEquals(lines, Files.readAllLines(path).stream()
+ .filter(s -> !s.isBlank()).toList());
+ }
+
+ private static class TestCredentialsProvider extends CredentialsProvider {
+
+ private final boolean[] values;
+
+ int invocations = 0;
+
+ int questions = 0;
+
+ TestCredentialsProvider(boolean accept, boolean store) {
+ values = new boolean[] { accept, store };
+ }
+
+ @Override
+ public boolean isInteractive() {
+ return true;
+ }
+
+ @Override
+ public boolean supports(CredentialItem... items) {
+ return true;
+ }
+
+ @Override
+ public boolean get(URIish uri, CredentialItem... items)
+ throws UnsupportedCredentialItem {
+ invocations++;
+ int i = 0;
+ for (CredentialItem item : items) {
+ if (item instanceof CredentialItem.YesNoType) {
+ ((CredentialItem.YesNoType) item)
+ .setValue(i < values.length && values[i++]);
+ questions++;
+ }
+ }
+ return true;
+ }
+ }
+
+ private static class KnownHostsConfig implements ServerKeyDatabase.Configuration {
+
+ @NonNull
+ private final StrictHostKeyChecking check;
+
+ KnownHostsConfig() {
+ this(StrictHostKeyChecking.REQUIRE_MATCH);
+ }
+
+ KnownHostsConfig(@NonNull StrictHostKeyChecking check) {
+ this.check = check;
+ }
+
+ @Override
+ public List<String> getUserKnownHostsFiles() {
+ return List.of();
+ }
+
+ @Override
+ public List<String> getGlobalKnownHostsFiles() {
+ return List.of();
+ }
+
+ @Override
+ public StrictHostKeyChecking getStrictHostKeyChecking() {
+ return check;
+ }
+
+ @Override
+ public boolean getHashKnownHosts() {
+ return false;
+ }
+
+ @Override
+ public String getUsername() {
+ return "user";
+ }
+
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshProtocol2Test.java b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshProtocol2Test.java
index eef0402..69bd5c5 100644
--- a/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshProtocol2Test.java
+++ b/org.eclipse.jgit.ssh.apache.test/tst/org/eclipse/jgit/transport/sshd/ApacheSshProtocol2Test.java
@@ -17,7 +17,6 @@
import org.eclipse.jgit.junit.ssh.SshBasicTestBase;
import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.transport.SshSessionFactory;
import org.eclipse.jgit.util.FS;
@@ -52,7 +51,8 @@ protected void installConfig(String... config) {
@Override
public void setUp() throws Exception {
super.setUp();
- StoredConfig config = ((Repository) db).getConfig();
+ @SuppressWarnings("restriction")
+ StoredConfig config = db.getConfig();
config.setInt("protocol", null, "version", 2);
config.save();
}
diff --git a/org.eclipse.jgit.ssh.apache/BUILD b/org.eclipse.jgit.ssh.apache/BUILD
index fd88a8a..83709c3 100644
--- a/org.eclipse.jgit.ssh.apache/BUILD
+++ b/org.eclipse.jgit.ssh.apache/BUILD
@@ -12,7 +12,6 @@
resource_strip_prefix = "org.eclipse.jgit.ssh.apache/resources",
resources = RESOURCES,
deps = [
- "//lib:eddsa",
"//lib:slf4j-api",
"//lib:sshd-osgi",
"//lib:sshd-sftp",
diff --git a/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF b/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF
index 02d9466..f38ea72 100644
--- a/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.apache/META-INF/MANIFEST.MF
@@ -6,9 +6,10 @@
Bundle-Vendor: %Bundle-Vendor
Bundle-Localization: OSGI-INF/l10n/plugin
Bundle-ActivationPolicy: lazy
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-RequiredExecutionEnvironment: JavaSE-17
-Export-Package: org.eclipse.jgit.internal.transport.sshd;version="7.0.0";x-internal:=true;
+Export-Package: org.eclipse.jgit.internal.signing.ssh;version="7.3.0";x-friends:="org.eclipse.jgit.ssh.apache.test",
+ org.eclipse.jgit.internal.transport.sshd;version="7.3.0";x-friends:="org.eclipse.jgit.ssh.apache.test";
uses:="org.apache.sshd.client,
org.apache.sshd.client.auth,
org.apache.sshd.client.auth.keyboard,
@@ -23,78 +24,79 @@
org.apache.sshd.common.signature,
org.apache.sshd.common.util.buffer,
org.eclipse.jgit.transport",
- org.eclipse.jgit.internal.transport.sshd.agent;version="7.0.0";x-internal:=true,
- org.eclipse.jgit.internal.transport.sshd.auth;version="7.0.0";x-internal:=true,
- org.eclipse.jgit.internal.transport.sshd.pkcs11;version="7.0.0";x-internal:=true,
- org.eclipse.jgit.internal.transport.sshd.proxy;version="7.0.0";x-friends:="org.eclipse.jgit.ssh.apache.test",
- org.eclipse.jgit.transport.sshd;version="7.0.0";
+ org.eclipse.jgit.internal.transport.sshd.agent;version="7.3.0";x-internal:=true,
+ org.eclipse.jgit.internal.transport.sshd.auth;version="7.3.0";x-internal:=true,
+ org.eclipse.jgit.internal.transport.sshd.pkcs11;version="7.3.0";x-internal:=true,
+ org.eclipse.jgit.internal.transport.sshd.proxy;version="7.3.0";x-friends:="org.eclipse.jgit.ssh.apache.test",
+ org.eclipse.jgit.signing.ssh;version="7.3.0";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.transport.sshd;version="7.3.0";
uses:="org.eclipse.jgit.transport,
org.apache.sshd.client.config.hosts,
org.apache.sshd.common.keyprovider,
org.eclipse.jgit.util,
org.apache.sshd.client.session,
org.apache.sshd.client.keyverifier",
- org.eclipse.jgit.transport.sshd.agent;version="7.0.0",
- sun.security.x509
-Import-Package: net.i2p.crypto.eddsa;version="[0.3.0,0.4.0)",
- org.apache.sshd.agent;version="[2.12.0,2.13.0)",
- org.apache.sshd.client;version="[2.12.0,2.13.0)",
- org.apache.sshd.client.auth;version="[2.12.0,2.13.0)",
- org.apache.sshd.client.auth.keyboard;version="[2.12.0,2.13.0)",
- org.apache.sshd.client.auth.password;version="[2.12.0,2.13.0)",
- org.apache.sshd.client.auth.pubkey;version="[2.12.0,2.13.0)",
- org.apache.sshd.client.channel;version="[2.12.0,2.13.0)",
- org.apache.sshd.client.config.hosts;version="[2.12.0,2.13.0)",
- org.apache.sshd.client.config.keys;version="[2.12.0,2.13.0)",
- org.apache.sshd.client.future;version="[2.12.0,2.13.0)",
- org.apache.sshd.client.keyverifier;version="[2.12.0,2.13.0)",
- org.apache.sshd.client.session;version="[2.12.0,2.13.0)",
- org.apache.sshd.client.session.forward;version="[2.12.0,2.13.0)",
- org.apache.sshd.common;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.auth;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.channel;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.cipher;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.compression;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.config.keys;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.config.keys.loader;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.config.keys.loader.openssh.kdf;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.config.keys.u2f;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.digest;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.forward;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.future;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.helpers;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.io;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.kex;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.kex.extension;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.kex.extension.parser;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.keyprovider;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.mac;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.random;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.session;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.session.helpers;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.signature;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.util;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.util.buffer;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.util.buffer.keys;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.util.closeable;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.util.io;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.util.io.der;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.util.io.functors;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.util.io.resource;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.util.logging;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.util.net;version="[2.12.0,2.13.0)",
- org.apache.sshd.common.util.security;version="[2.12.0,2.13.0)",
- org.apache.sshd.core;version="[2.12.0,2.13.0)",
- org.apache.sshd.server.auth;version="[2.12.0,2.13.0)",
- org.apache.sshd.sftp;version="[2.12.0,2.13.0)",
- org.apache.sshd.sftp.client;version="[2.12.0,2.13.0)",
- org.apache.sshd.sftp.common;version="[2.12.0,2.13.0)",
- org.eclipse.jgit.annotations;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.fnmatch;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.file;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.transport.ssh;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.nls;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)",
+ org.eclipse.jgit.transport.sshd.agent;version="7.3.0"
+Import-Package: org.apache.sshd.agent;version="[2.15.0,2.16.0)",
+ org.apache.sshd.client;version="[2.15.0,2.16.0)",
+ org.apache.sshd.client.auth;version="[2.15.0,2.16.0)",
+ org.apache.sshd.client.auth.keyboard;version="[2.15.0,2.16.0)",
+ org.apache.sshd.client.auth.password;version="[2.15.0,2.16.0)",
+ org.apache.sshd.client.auth.pubkey;version="[2.15.0,2.16.0)",
+ org.apache.sshd.client.channel;version="[2.15.0,2.16.0)",
+ org.apache.sshd.client.config.hosts;version="[2.15.0,2.16.0)",
+ org.apache.sshd.client.config.keys;version="[2.15.0,2.16.0)",
+ org.apache.sshd.client.future;version="[2.15.0,2.16.0)",
+ org.apache.sshd.client.keyverifier;version="[2.15.0,2.16.0)",
+ org.apache.sshd.client.session;version="[2.15.0,2.16.0)",
+ org.apache.sshd.client.session.forward;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.auth;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.channel;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.cipher;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.compression;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.config.keys;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.config.keys.loader;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.config.keys.loader.openssh.kdf;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.config.keys.u2f;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.digest;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.forward;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.future;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.helpers;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.io;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.kex;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.kex.extension;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.kex.extension.parser;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.keyprovider;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.mac;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.random;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.session;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.session.helpers;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.signature;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.util;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.util.buffer;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.util.buffer.keys;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.util.closeable;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.util.io;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.util.io.der;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.util.io.functors;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.util.io.resource;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.util.logging;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.util.net;version="[2.15.0,2.16.0)",
+ org.apache.sshd.common.util.security;version="[2.15.0,2.16.0)",
+ org.apache.sshd.core;version="[2.15.0,2.16.0)",
+ org.apache.sshd.server.auth;version="[2.15.0,2.16.0)",
+ org.apache.sshd.sftp;version="[2.15.0,2.16.0)",
+ org.apache.sshd.sftp.client;version="[2.15.0,2.16.0)",
+ org.apache.sshd.sftp.common;version="[2.15.0,2.16.0)",
+ org.eclipse.jgit.annotations;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.api.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.fnmatch;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.transport.ssh;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.nls;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.0)",
org.slf4j;version="[1.7.0,3.0.0)"
diff --git a/org.eclipse.jgit.ssh.apache/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.ssh.apache/META-INF/SOURCE-MANIFEST.MF
index bb0d79b..d6225e2 100644
--- a/org.eclipse.jgit.ssh.apache/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.apache/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@
Bundle-Name: org.eclipse.jgit.ssh.apache - Sources
Bundle-SymbolicName: org.eclipse.jgit.ssh.apache.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.0.0.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache;version="7.0.0.qualifier";roots="."
+Bundle-Version: 7.3.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.ssh.apache;version="7.3.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.ssh.apache/pom.xml b/org.eclipse.jgit.ssh.apache/pom.xml
index 7cc3a55..365034a 100644
--- a/org.eclipse.jgit.ssh.apache/pom.xml
+++ b/org.eclipse.jgit.ssh.apache/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.ssh.apache</artifactId>
@@ -30,7 +30,6 @@
<properties>
<translate-qualifier/>
<source-bundle-manifest>${project.build.directory}/META-INF/SOURCE-MANIFEST.MF</source-bundle-manifest>
- <eddsa-version>0.3.0</eddsa-version>
</properties>
<dependencies>
@@ -63,12 +62,6 @@
</dependency>
<dependency>
- <groupId>net.i2p.crypto</groupId>
- <artifactId>eddsa</artifactId>
- <version>${eddsa-version}</version>
- </dependency>
-
- <dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
diff --git a/org.eclipse.jgit.ssh.apache/resources/META-INF/services/org.eclipse.jgit.lib.SignatureVerifierFactory b/org.eclipse.jgit.ssh.apache/resources/META-INF/services/org.eclipse.jgit.lib.SignatureVerifierFactory
new file mode 100644
index 0000000..4a0f553
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/resources/META-INF/services/org.eclipse.jgit.lib.SignatureVerifierFactory
@@ -0,0 +1 @@
+org.eclipse.jgit.signing.ssh.SshSignatureVerifierFactory
\ No newline at end of file
diff --git a/org.eclipse.jgit.ssh.apache/resources/META-INF/services/org.eclipse.jgit.lib.SignerFactory b/org.eclipse.jgit.ssh.apache/resources/META-INF/services/org.eclipse.jgit.lib.SignerFactory
new file mode 100644
index 0000000..80f22c0
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/resources/META-INF/services/org.eclipse.jgit.lib.SignerFactory
@@ -0,0 +1 @@
+org.eclipse.jgit.signing.ssh.SshSignerFactory
\ No newline at end of file
diff --git a/org.eclipse.jgit.ssh.apache/resources/org/eclipse/jgit/internal/transport/sshd/SshdText.properties b/org.eclipse.jgit.ssh.apache/resources/org/eclipse/jgit/internal/transport/sshd/SshdText.properties
index 7da7181..773c4b9 100644
--- a/org.eclipse.jgit.ssh.apache/resources/org/eclipse/jgit/internal/transport/sshd/SshdText.properties
+++ b/org.eclipse.jgit.ssh.apache/resources/org/eclipse/jgit/internal/transport/sshd/SshdText.properties
@@ -61,6 +61,7 @@
The {0} key actually received has the fingerprints:\n\
{5}\n\
{6}
+knownHostsRevokedCertificateMsg=Host ''{0}'' sent a certificate with a CA key that is marked as revoked in the known hosts file {1}.
knownHostsRevokedKeyMsg=Host ''{0}'' sent a key that is marked as revoked in the known hosts file {1}.
knownHostsUnknownKeyMsg=The authenticity of host ''{0}'' cannot be established.
knownHostsUnknownKeyPrompt=Accept and store this key, and continue connecting?
@@ -125,4 +126,69 @@
sshCommandTimeout={0} timed out after {1} seconds while opening the channel
sshProcessStillRunning={0} is not yet completed, cannot get exit code
sshProxySessionCloseFailed=Error while closing proxy session {0}
+signAllowedSignersCertAuthorityError=Garbage after cert-authority
+signAllowedSignersEmptyIdentity=Identities contains an empty identity; check for spurious extra commas: {0}
+signAllowedSignersEmptyNamespaces=Empty namespaces= is not allowed; to allow a key for any namespace, omit the namespaces option
+signAllowedSignersFormatError=Cannot parse allowed signers file {0}, problem at line {1}: {2}
+signAllowedSignersInvalidDate=Cannot parse valid-before or valid-after date {0}
+signAllowedSignersLineFormat=Invalid line format
+signAllowedSignersMultiple={0} is allowed only once
+signAllowedSignersNoIdentities=Line has no identity patterns
+signAllowedSignersPublicKeyParsing=Cannot parse public key {0}
+signAllowedSignersUnterminatedQuote=Unterminated double quote
+signCertAlgorithmMismatch=Certificate of type {0} with CA key {1} uses an incompatible signature algorithm {2}
+signCertAlgorithmUnknown=Certificate with CA key {0} is signed with an unknown algorithm {1}
+signCertificateExpired=Expired certificate with CA key {0}
+signCertificateInvalid=Certificate signature does not match on certificate with CA key {0}
+signCertificateNotForName=Certificate with CA key {0} does not apply for name ''{1}''
+signCertificateRevoked=Certificate with CA key {0} was revoked
+signCertificateTooEarly=Certificate with CA key {0} was not valid yet
+signCertificateWithoutPrincipals=Certificate with CA key {0} has no principals; identities from gpg.ssh.allowedSignersFile: {1}
+signDefaultKeyEmpty=git.ssh.defaultKeyCommand {0} returned no key
+signDefaultKeyFailed=git.ssh.defaultKeyCommand {0} failed with exit code {1}\n{2}
+signDefaultKeyInterrupted=git.ssh.defaultKeyCommand {0} was interrupted
+signGarbageAtEnd=SSH signature has extra bytes at the end
+signInvalidAlgorithm=SSH signature has invalid signature algorithm {0}
+signInvalidKeyDSA=SSH signatures with DSA keys or certificates are not supported; use a different signing key.
+signInvalidMagic=SSH signature does not start with "SSHSIG"
+signInvalidNamespace=Namespace of SSH signature should be ''git'' but is ''{0}''
+signInvalidSignature=SSH signature is invalid: {0}
+signInvalidVersion=Cannot verify signature with version {0}
+signKeyExpired=Expired key used for SSH signature
+signKeyRevoked=Key used for the SSH signature was revoked
+signKeyTooEarly=Key used for the SSH signature was not valid yet
+signKrlBlobLeftover=gpg.ssh.revocationFile has invalid blob section {0} with {1} leftover bytes
+signKrlBlobLengthInvalid=gpg.ssh.revocationFile has invalid blob length {1} in section {0}
+signKrlBlobLengthInvalidExpected=gpg.ssh.revocationFile has invalid blob length {1} (expected {2}) in section {0}
+signKrlCaKeyLengthInvalid=gpg.ssh.revocationFile has invalid CA key length {0} in certificates section
+signKrlCertificateLeftover=gpg.ssh.revocationFile has invalid certificates section with {0} leftover bytes
+signKrlCertificateSubsectionLeftover=gpg.ssh.revocationFile has invalid certificates subsection with {0} leftover bytes
+signKrlCertificateSubsectionLength=gpg.ssh.revocationFile has invalid certificates subsection length {0}
+signKrlEmptyRange=gpg.ssh.revocationFile has an empty range of certificate serial numbers
+signKrlInvalidBitSetLength=gpg.ssh.revocationFile has invalid certificate serial number bit set length {0}
+signKrlInvalidKeyIdLength=gpg.ssh.revocationFile has invalid certificate key ID length {0}
+signKrlInvalidMagic=gpg.ssh.revocationFile is not a binary OpenSSH key revocation list
+signKrlInvalidReservedLength=gpg.ssh.revocationFile has an invalid reserved string length {0}
+signKrlInvalidVersion=gpg.ssh.revocationFile: cannot read KRLs with FORMAT_VERSION {0}
+signKrlNoCertificateSubsection=gpg.ssh.revocationFile has certificate section without subsections
+signKrlSerialZero=gpg.ssh.revocationFile: certificate serial number zero cannot be revoked
+signKrlShortRange=gpg.ssh.revocationFile: short certificate serial number range, need at least 8 more bytes, got only {0}
+signKrlUnknownSection=gpg.ssh.revocationFile has an unknown section type {0}
+signKrlUnknownSubsection=gpg.ssh.revocationFile has an unknown certificates subsection type {0}
+signLogFailure=SSH signature verification failed
+signMismatchedSignatureAlgorithm=SSH signature made with an ''{0}'' key has incompatible signature algorithm ''{1}''
+signNoAgent=No connector for ssh-agent found; maybe include org.eclipse.jgit.ssh.apache.agent in the application.
+signNoPrincipalMatched=No principal matched in gpg.ssh.allowedSignersFile
+signNoPublicKey=No public key found with signing key {0}
+signNoSigningKey=Git config user.signingKey or gpg.ssh.defaultKeyCommand must be set for SSH signing.
+signNotUserCertificate=Certificate with CA key {0} used for the SSH signature is not a user certificate.
+signPublicKeyError=Cannot read public key {0}
+signSeeLog=SSH signature verification failed; see the log for details
+signSignatureError=Could not create the signature
+signStderr=Cannot read stderr
+signTooManyPrivateKeys=Private key file {0} must contain exactly one private key
+signTooManyPublicKeys=Public key file {0} must contain exactly one public key
+signUnknownHashAlgorithm=SSH Signature has an unknown hash algorithm {0}
+signUnknownSignatureAlgorithm=SSH Signature has an unknown signature algorithm {0}
+signWrongNamespace=Key may not be used in namespace "{0}".
unknownProxyProtocol=Ignoring unknown proxy protocol {0}
\ No newline at end of file
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/AllowedSigners.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/AllowedSigners.java
new file mode 100644
index 0000000..80b171f
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/AllowedSigners.java
@@ -0,0 +1,530 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.signing.ssh;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StreamCorruptedException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.security.PublicKey;
+import java.text.MessageFormat;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
+import java.time.temporal.ChronoField;
+import java.time.temporal.TemporalAccessor;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.OpenSshCertificate;
+import org.apache.sshd.common.config.keys.PublicKeyEntry;
+import org.apache.sshd.common.util.io.ModifiableFileWatcher;
+import org.eclipse.jgit.internal.transport.ssh.OpenSshConfigFile;
+import org.eclipse.jgit.internal.transport.sshd.SshdText;
+import org.eclipse.jgit.signing.ssh.VerificationException;
+import org.eclipse.jgit.util.StringUtils;
+import org.eclipse.jgit.util.SystemReader;
+
+/**
+ * Encapsulates the allowed signers handling.
+ */
+final class AllowedSigners extends ModifiableFileWatcher {
+
+ private static final String CERT_AUTHORITY = "cert-authority"; //$NON-NLS-1$
+
+ private static final String NAMESPACES = "namespaces="; //$NON-NLS-1$
+
+ private static final String VALID_AFTER = "valid-after="; //$NON-NLS-1$
+
+ private static final String VALID_BEFORE = "valid-before="; //$NON-NLS-1$
+
+ private static final DateTimeFormatter SSH_DATE_FORMAT = new DateTimeFormatterBuilder()
+ .appendValue(ChronoField.YEAR, 4)
+ .appendValue(ChronoField.MONTH_OF_YEAR, 2)
+ .appendValue(ChronoField.DAY_OF_MONTH, 2)
+ .optionalStart()
+ .appendValue(ChronoField.HOUR_OF_DAY, 2)
+ .appendValue(ChronoField.MINUTE_OF_HOUR, 2)
+ .optionalStart()
+ .appendValue(ChronoField.SECOND_OF_MINUTE, 2)
+ .toFormatter(Locale.ROOT);
+
+ private static final Predicate<AllowedEntry> CERTIFICATES = AllowedEntry::isCA;
+
+ private static final Predicate<AllowedEntry> PLAIN_KEYS = Predicate
+ .not(CERTIFICATES);
+
+ @SuppressWarnings("ArrayRecordComponent")
+ static record AllowedEntry(String[] identities, boolean isCA,
+ String[] namespaces, Instant validAfter, Instant validBefore,
+ String key) {
+ // Empty
+
+ @Override
+ public final boolean equals(Object any) {
+ if (this == any) {
+ return true;
+ }
+ if (any == null || !(any instanceof AllowedEntry)) {
+ return false;
+ }
+ AllowedEntry other = (AllowedEntry) any;
+ return isCA == other.isCA
+ && Arrays.equals(identities, other.identities)
+ && Arrays.equals(namespaces, other.namespaces)
+ && Objects.equals(validAfter, other.validAfter)
+ && Objects.equals(validBefore, other.validBefore)
+ && Objects.equals(key, other.key);
+ }
+
+ @Override
+ public final int hashCode() {
+ int hash = Boolean.hashCode(isCA);
+ hash = hash * 31 + Arrays.hashCode(identities);
+ hash = hash * 31 + Arrays.hashCode(namespaces);
+ return hash * 31 + Objects.hash(validAfter, validBefore, key);
+ }
+ }
+
+ private static record State(Map<String, List<AllowedEntry>> entries) {
+ // Empty
+ }
+
+ private State state;
+
+ public AllowedSigners(Path path) {
+ super(path);
+ state = new State(new HashMap<>());
+ }
+
+ public String isAllowed(PublicKey key, String namespace, String name,
+ Instant time) throws IOException, VerificationException {
+ State currentState = refresh();
+ PublicKey keyToCheck = key;
+ if (key instanceof OpenSshCertificate certificate) {
+ AllowedEntry entry = find(currentState, certificate.getCaPubKey(),
+ namespace, name, time, CERTIFICATES);
+ if (entry != null) {
+ Collection<String> principals = certificate.getPrincipals();
+ if (principals.isEmpty()) {
+ // According to the OpenSSH documentation, a certificate
+ // without principals is valid for anyone.
+ //
+ // See https://man.openbsd.org/ssh-keygen.1#CERTIFICATES .
+ //
+ // However, the same documentation also says that a name
+ // must match both the entry's patterns and be listed in the
+ // certificate's principals.
+ //
+ // See https://man.openbsd.org/ssh-keygen.1#ALLOWED_SIGNERS
+ //
+ // git/OpenSSH considers signatures made by such
+ // certificates untrustworthy.
+ String identities;
+ if (!StringUtils.isEmptyOrNull(name)) {
+ // The name must have matched entry.identities.
+ identities = name;
+ } else {
+ identities = Arrays.stream(entry.identities())
+ .collect(Collectors.joining(",")); //$NON-NLS-1$
+ }
+ throw new VerificationException(false, MessageFormat.format(
+ SshdText.get().signCertificateWithoutPrincipals,
+ KeyUtils.getFingerPrint(certificate.getCaPubKey()),
+ identities));
+ }
+ if (!StringUtils.isEmptyOrNull(name)) {
+ if (!principals.contains(name)) {
+ throw new VerificationException(false,
+ MessageFormat.format(SshdText
+ .get().signCertificateNotForName,
+ KeyUtils.getFingerPrint(
+ certificate.getCaPubKey()),
+ name));
+ }
+ return name;
+ }
+ // Filter the principals listed in the certificate by
+ // the patterns defined in the file.
+ Set<String> filtered = new LinkedHashSet<>();
+ List<String> patterns = Arrays.asList(entry.identities());
+ for (String principal : principals) {
+ if (OpenSshConfigFile.patternMatch(patterns, principal)) {
+ filtered.add(principal);
+ }
+ }
+ return filtered.stream().collect(Collectors.joining(",")); //$NON-NLS-1$
+ }
+ // Certificate not found. git/OpenSSH considers this untrustworthy,
+ // even if the certified key itself might be listed.
+ return null;
+ // Alternative: go check for the certified key itself:
+ // keyToCheck = certificate.getCertPubKey();
+ }
+ AllowedEntry entry = find(currentState, keyToCheck, namespace, name,
+ time, PLAIN_KEYS);
+ if (entry != null) {
+ if (!StringUtils.isEmptyOrNull(name)) {
+ // The name must have matched entry.identities.
+ return name;
+ }
+ // No name given, but we consider the key valid: report the
+ // identities.
+ return Arrays.stream(entry.identities())
+ .collect(Collectors.joining(",")); //$NON-NLS-1$
+ }
+ return null;
+ }
+
+ private AllowedEntry find(State current, PublicKey key,
+ String namespace, String name, Instant time,
+ Predicate<AllowedEntry> filter)
+ throws VerificationException {
+ String k = PublicKeyEntry.toString(key);
+ VerificationException v = null;
+ List<AllowedEntry> candidates = current.entries().get(k);
+ if (candidates == null) {
+ return null;
+ }
+ for (AllowedEntry entry : candidates) {
+ if (!filter.test(entry)) {
+ continue;
+ }
+ if (name != null && !OpenSshConfigFile
+ .patternMatch(Arrays.asList(entry.identities()), name)) {
+ continue;
+ }
+ if (entry.namespaces() != null) {
+ if (!OpenSshConfigFile.patternMatch(
+ Arrays.asList(entry.namespaces()),
+ namespace)) {
+ if (v == null) {
+ v = new VerificationException(false,
+ MessageFormat.format(
+ SshdText.get().signWrongNamespace,
+ KeyUtils.getFingerPrint(key),
+ namespace));
+ }
+ continue;
+ }
+ }
+ if (time != null) {
+ if (entry.validAfter() != null
+ && time.isBefore(entry.validAfter())) {
+ if (v == null) {
+ v = new VerificationException(true,
+ MessageFormat.format(
+ SshdText.get().signKeyTooEarly,
+ KeyUtils.getFingerPrint(key)));
+ }
+ continue;
+ } else if (entry.validBefore() != null
+ && time.isAfter(entry.validBefore())) {
+ if (v == null) {
+ v = new VerificationException(true,
+ MessageFormat.format(
+ SshdText.get().signKeyTooEarly,
+ KeyUtils.getFingerPrint(key)));
+ }
+ continue;
+ }
+ }
+ return entry;
+ }
+ if (v != null) {
+ throw v;
+ }
+ return null;
+ }
+
+ private synchronized State refresh() throws IOException {
+ if (checkReloadRequired()) {
+ updateReloadAttributes();
+ try {
+ state = reload(getPath());
+ } catch (NoSuchFileException e) {
+ // File disappeared
+ resetReloadAttributes();
+ state = new State(new HashMap<>());
+ }
+ }
+ return state;
+ }
+
+ private static State reload(Path path) throws IOException {
+ Map<String, List<AllowedEntry>> entries = new HashMap<>();
+ try (BufferedReader r = Files.newBufferedReader(path,
+ StandardCharsets.UTF_8)) {
+ String line;
+ for (int lineNumber = 1;; lineNumber++) {
+ line = r.readLine();
+ if (line == null) {
+ break;
+ }
+ line = line.strip();
+ try {
+ AllowedEntry entry = parseLine(line);
+ if (entry != null) {
+ entries.computeIfAbsent(entry.key(),
+ k -> new ArrayList<>()).add(entry);
+ }
+ } catch (IOException | RuntimeException e) {
+ throw new IOException(MessageFormat.format(
+ SshdText.get().signAllowedSignersFormatError, path,
+ Integer.toString(lineNumber), line), e);
+ }
+ }
+ }
+ return new State(entries);
+ }
+
+ private static boolean matches(String src, String other, int offset) {
+ return src.regionMatches(true, offset, other, 0, other.length());
+ }
+
+ // Things below have package visibility for testing.
+
+ static AllowedEntry parseLine(String line)
+ throws IOException {
+ if (StringUtils.isEmptyOrNull(line) || line.charAt(0) == '#') {
+ return null;
+ }
+ int length = line.length();
+ if ((matches(line, CERT_AUTHORITY, 0)
+ && CERT_AUTHORITY.length() < length
+ && Character.isWhitespace(line.charAt(CERT_AUTHORITY.length())))
+ || matches(line, NAMESPACES, 0)
+ || matches(line, VALID_AFTER, 0)
+ || matches(line, VALID_BEFORE, 0)) {
+ throw new StreamCorruptedException(
+ SshdText.get().signAllowedSignersNoIdentities);
+ }
+ int i = 0;
+ while (i < length && !Character.isWhitespace(line.charAt(i))) {
+ i++;
+ }
+ if (i >= length) {
+ throw new StreamCorruptedException(SshdText.get().signAllowedSignersLineFormat);
+ }
+ String[] identities = line.substring(0, i).split(","); //$NON-NLS-1$
+ if (Arrays.stream(identities).anyMatch(String::isEmpty)) {
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signAllowedSignersEmptyIdentity,
+ line.substring(0, i)));
+ }
+ // Parse the options
+ i++;
+ boolean isCA = false;
+ List<String> namespaces = null;
+ Instant validAfter = null;
+ Instant validBefore = null;
+ while (i < length) {
+ // Skip whitespace
+ if (Character.isSpaceChar(line.charAt(i))) {
+ i++;
+ continue;
+ }
+ if (matches(line, CERT_AUTHORITY, i)) {
+ i += CERT_AUTHORITY.length();
+ isCA = true;
+ if (!Character.isWhitespace(line.charAt(i))) {
+ throw new StreamCorruptedException(SshdText.get().signAllowedSignersCertAuthorityError);
+ }
+ i++;
+ } else if (matches(line, NAMESPACES, i)) {
+ if (namespaces != null) {
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signAllowedSignersMultiple,
+ NAMESPACES));
+ }
+ i += NAMESPACES.length();
+ Dequoted parsed = dequote(line, i);
+ i = parsed.after();
+ String ns = parsed.value();
+ String[] items = ns.split(","); //$NON-NLS-1$
+ namespaces = new ArrayList<>(items.length);
+ for (int j = 0; j < items.length; j++) {
+ String n = items[j].strip();
+ if (!n.isEmpty()) {
+ namespaces.add(n);
+ }
+ }
+ if (namespaces.isEmpty()) {
+ throw new StreamCorruptedException(
+ SshdText.get().signAllowedSignersEmptyNamespaces);
+ }
+ } else if (matches(line, VALID_AFTER, i)) {
+ if (validAfter != null) {
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signAllowedSignersMultiple,
+ VALID_AFTER));
+ }
+ i += VALID_AFTER.length();
+ Dequoted parsed = dequote(line, i);
+ i = parsed.after();
+ validAfter = parseDate(parsed.value());
+ } else if (matches(line, VALID_BEFORE, i)) {
+ if (validBefore != null) {
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signAllowedSignersMultiple,
+ VALID_BEFORE));
+ }
+ i += VALID_BEFORE.length();
+ Dequoted parsed = dequote(line, i);
+ i = parsed.after();
+ validBefore = parseDate(parsed.value());
+ } else {
+ break;
+ }
+ }
+ // Now we should be at the key
+ String key = parsePublicKey(line, i);
+ return new AllowedEntry(identities, isCA,
+ namespaces == null ? null : namespaces.toArray(new String[0]),
+ validAfter, validBefore, key);
+ }
+
+ static String parsePublicKey(String s, int from)
+ throws StreamCorruptedException {
+ int i = from;
+ int length = s.length();
+ while (i < length && Character.isWhitespace(s.charAt(i))) {
+ i++;
+ }
+ if (i >= length) {
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signAllowedSignersPublicKeyParsing,
+ s.substring(from)));
+ }
+ int start = i;
+ while (i < length && !Character.isWhitespace(s.charAt(i))) {
+ i++;
+ }
+ if (i >= length) {
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signAllowedSignersPublicKeyParsing,
+ s.substring(start)));
+ }
+ int endOfKeyType = i;
+ i = endOfKeyType + 1;
+ while (i < length && Character.isWhitespace(s.charAt(i))) {
+ i++;
+ }
+ int startOfKey = i;
+ while (i < length && !Character.isWhitespace(s.charAt(i))) {
+ i++;
+ }
+ if (i == startOfKey) {
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signAllowedSignersPublicKeyParsing,
+ s.substring(start)));
+ }
+ String keyType = s.substring(start, endOfKeyType);
+ String key = s.substring(startOfKey, i);
+ if (!key.startsWith("AAAA")) { //$NON-NLS-1$
+ // base64 encoded SSH keys always start with four 'A's.
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signAllowedSignersPublicKeyParsing,
+ s.substring(start)));
+ }
+ return keyType + ' ' + s.substring(startOfKey, i);
+ }
+
+ static Instant parseDate(String input) {
+ // Allowed formats are YYYYMMDD[Z] or YYYYMMDDHHMM[SS][Z]. If 'Z', it's
+ // UTC, otherwise local time.
+ String timeSpec = input;
+ int length = input.length();
+ if (length < 8) {
+ throw new IllegalArgumentException(MessageFormat.format(
+ SshdText.get().signAllowedSignersInvalidDate, input));
+ }
+ boolean isUTC = false;
+ if (timeSpec.charAt(length - 1) == 'Z') {
+ isUTC = true;
+ timeSpec = timeSpec.substring(0, length - 1);
+ }
+ LocalDateTime time;
+ TemporalAccessor temporalAccessor = SSH_DATE_FORMAT.parseBest(timeSpec,
+ LocalDateTime::from, LocalDate::from);
+ if (temporalAccessor instanceof LocalDateTime) {
+ time = (LocalDateTime) temporalAccessor;
+ } else {
+ time = ((LocalDate) temporalAccessor).atStartOfDay();
+ }
+ if (isUTC) {
+ return time.atOffset(ZoneOffset.UTC).toInstant();
+ }
+ ZoneId tz = SystemReader.getInstance().getTimeZoneId();
+ return time.atZone(tz).toInstant();
+ }
+
+ // OpenSSH uses the backslash *only* to quote the double-quote.
+ static Dequoted dequote(String line, int from) {
+ int length = line.length();
+ int i = from;
+ if (line.charAt(i) == '"') {
+ boolean quoted = false;
+ i++;
+ StringBuilder b = new StringBuilder();
+ while (i < length) {
+ char ch = line.charAt(i);
+ if (ch == '"') {
+ if (quoted) {
+ b.append(ch);
+ quoted = false;
+ } else {
+ break;
+ }
+ } else if (ch == '\\') {
+ quoted = true;
+ } else {
+ if (quoted) {
+ b.append('\\');
+ }
+ b.append(ch);
+ quoted = false;
+ }
+ i++;
+ }
+ if (i >= length) {
+ throw new IllegalArgumentException(
+ SshdText.get().signAllowedSignersUnterminatedQuote);
+ }
+ return new Dequoted(b.toString(), i + 1);
+ }
+ while (i < length && !Character.isWhitespace(line.charAt(i))) {
+ i++;
+ }
+ return new Dequoted(line.substring(from, i), i);
+ }
+
+ static record Dequoted(String value, int after) {
+ // Empty
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/OpenSshBinaryKrl.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/OpenSshBinaryKrl.java
new file mode 100644
index 0000000..6b19eb32
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/OpenSshBinaryKrl.java
@@ -0,0 +1,491 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.signing.ssh;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StreamCorruptedException;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.sshd.common.config.keys.OpenSshCertificate;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.internal.transport.sshd.SshdText;
+import org.eclipse.jgit.util.IO;
+import org.eclipse.jgit.util.StringUtils;
+
+/**
+ * An implementation of OpenSSH binary format key revocation lists (KRLs).
+ *
+ * @see <a href=
+ * "https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.krl">PROTOCOL.krl</a>
+ */
+class OpenSshBinaryKrl {
+
+ /**
+ * The "magic" bytes at the start of an OpenSSH binary KRL.
+ */
+ static final byte[] MAGIC = { 'S', 'S', 'H', 'K', 'R', 'L', '\n', 0 };
+
+ private static final int FORMAT_VERSION = 1;
+
+ private static final int SECTION_CERTIFICATES = 1;
+
+ private static final int SECTION_KEY = 2;
+
+ private static final int SECTION_SHA1 = 3;
+
+ private static final int SECTION_SIGNATURE = 4; // Skipped
+
+ private static final int SECTION_SHA256 = 5;
+
+ private static final int SECTION_EXTENSION = 255; // Skipped
+
+ // Certificates
+
+ private static final int CERT_SERIAL_LIST = 0x20;
+
+ private static final int CERT_SERIAL_RANGES = 0x21;
+
+ private static final int CERT_SERIAL_BITS = 0x22;
+
+ private static final int CERT_KEY_IDS = 0x23;
+
+ private static final int CERT_EXTENSIONS = 0x39; // Skipped
+
+ private final Map<Blob, CertificateRevocation> certificates = new HashMap<>();
+
+ private static class CertificateRevocation {
+
+ final SerialRangeSet ranges = new SerialRangeSet();
+
+ final Set<String> keyIds = new HashSet<>();
+ }
+
+ // Plain keys
+
+ /**
+ * A byte array that can be used as a key in a {@link Map} or {@link Set}.
+ * {@link #equals(Object)} and {@link #hashCode()} are based on the content.
+ *
+ * @param blob
+ * the array to wrap
+ */
+ @SuppressWarnings("ArrayRecordComponent")
+ private static record Blob(byte[] blob) {
+
+ @Override
+ public final boolean equals(Object any) {
+ if (this == any) {
+ return true;
+ }
+ if (any == null || !(any instanceof Blob)) {
+ return false;
+ }
+ Blob other = (Blob) any;
+ return Arrays.equals(blob, other.blob);
+ }
+
+ @Override
+ public final int hashCode() {
+ return Arrays.hashCode(blob);
+ }
+ }
+
+ private final Set<Blob> blobs = new HashSet<>();
+
+ private final Set<Blob> sha1 = new HashSet<>();
+
+ private final Set<Blob> sha256 = new HashSet<>();
+
+ private OpenSshBinaryKrl() {
+ // No public instantiation, use load(InputStream, boolean) instead.
+ }
+
+ /**
+ * Tells whether the given key has been revoked.
+ *
+ * @param key
+ * {@link PublicKey} to check
+ * @return {@code true} if the key was revoked, {@code false} otherwise
+ */
+ boolean isRevoked(PublicKey key) {
+ if (key instanceof OpenSshCertificate certificate) {
+ if (certificates.isEmpty()) {
+ return false;
+ }
+ // These apply to all certificates
+ if (isRevoked(certificate, certificates.get(null))) {
+ return true;
+ }
+ if (isRevoked(certificate,
+ certificates.get(blob(certificate.getCaPubKey())))) {
+ return true;
+ }
+ // Keys themselves are checked in OpenSshKrl.
+ return false;
+ }
+ if (!blobs.isEmpty() && blobs.contains(blob(key))) {
+ return true;
+ }
+ if (!sha256.isEmpty() && sha256.contains(hash("SHA256", key))) { //$NON-NLS-1$
+ return true;
+ }
+ if (!sha1.isEmpty() && sha1.contains(hash("SHA1", key))) { //$NON-NLS-1$
+ return true;
+ }
+ return false;
+ }
+
+ private boolean isRevoked(OpenSshCertificate certificate,
+ CertificateRevocation revocations) {
+ if (revocations == null) {
+ return false;
+ }
+ String id = certificate.getId();
+ if (!StringUtils.isEmptyOrNull(id) && revocations.keyIds.contains(id)) {
+ return true;
+ }
+ long serial = certificate.getSerial();
+ if (serial != 0 && revocations.ranges.contains(serial)) {
+ return true;
+ }
+ return false;
+ }
+
+ private Blob blob(PublicKey key) {
+ ByteArrayBuffer buf = new ByteArrayBuffer();
+ buf.putRawPublicKey(key);
+ return new Blob(buf.getCompactData());
+ }
+
+ private Blob hash(String algorithm, PublicKey key) {
+ ByteArrayBuffer buf = new ByteArrayBuffer();
+ buf.putRawPublicKey(key);
+ try {
+ return new Blob(MessageDigest.getInstance(algorithm)
+ .digest(buf.getCompactData()));
+ } catch (NoSuchAlgorithmException e) {
+ throw new JGitInternalException(e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Loads a binary KRL from the given stream.
+ *
+ * @param in
+ * {@link InputStream} to read from
+ * @param magicSkipped
+ * whether the {@link #MAGIC} bytes at the beginning have already
+ * been skipped
+ * @return a new {@link OpenSshBinaryKrl}.
+ * @throws IOException
+ * if the stream cannot be read as an OpenSSH binary KRL
+ */
+ @NonNull
+ static OpenSshBinaryKrl load(InputStream in, boolean magicSkipped)
+ throws IOException {
+ if (!magicSkipped) {
+ byte[] magic = new byte[MAGIC.length];
+ IO.readFully(in, magic);
+ if (!Arrays.equals(magic, MAGIC)) {
+ throw new StreamCorruptedException(
+ SshdText.get().signKrlInvalidMagic);
+ }
+ }
+ skipHeader(in);
+ return load(in);
+ }
+
+ private static long getUInt(InputStream in) throws IOException {
+ byte[] buf = new byte[Integer.BYTES];
+ IO.readFully(in, buf);
+ return BufferUtils.getUInt(buf);
+ }
+
+ private static long getLong(InputStream in) throws IOException {
+ byte[] buf = new byte[Long.BYTES];
+ IO.readFully(in, buf);
+ return BufferUtils.getLong(buf, 0, Long.BYTES);
+ }
+
+ private static void skipHeader(InputStream in) throws IOException {
+ long version = getUInt(in);
+ if (version != FORMAT_VERSION) {
+ throw new StreamCorruptedException(
+ MessageFormat.format(SshdText.get().signKrlInvalidVersion,
+ Long.valueOf(version)));
+ }
+ // krl_version, generated_date, flags (none defined in version 1)
+ in.skip(24);
+ in.skip(getUInt(in)); // reserved
+ in.skip(getUInt(in)); // comment
+ }
+
+ private static OpenSshBinaryKrl load(InputStream in) throws IOException {
+ OpenSshBinaryKrl krl = new OpenSshBinaryKrl();
+ for (;;) {
+ int sectionType = in.read();
+ if (sectionType < 0) {
+ break; // EOF
+ }
+ switch (sectionType) {
+ case SECTION_CERTIFICATES:
+ readCertificates(krl.certificates, in, getUInt(in));
+ break;
+ case SECTION_KEY:
+ readBlobs("explicit_keys", krl.blobs, in, getUInt(in), 0); //$NON-NLS-1$
+ break;
+ case SECTION_SHA1:
+ readBlobs("fingerprint_sha1", krl.sha1, in, getUInt(in), 20); //$NON-NLS-1$
+ break;
+ case SECTION_SIGNATURE:
+ // Unsupported as of OpenSSH 9.4. It even refuses to load such
+ // KRLs. Just skip it.
+ in.skip(getUInt(in));
+ break;
+ case SECTION_SHA256:
+ readBlobs("fingerprint_sha256", krl.sha256, in, getUInt(in), //$NON-NLS-1$
+ 32);
+ break;
+ case SECTION_EXTENSION:
+ // No extensions are defined for version 1 KRLs.
+ in.skip(getUInt(in));
+ break;
+ default:
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signKrlUnknownSection,
+ Integer.valueOf(sectionType)));
+ }
+ }
+ return krl;
+ }
+
+ private static void readBlobs(String sectionName, Set<Blob> blobs,
+ InputStream in, long sectionLength, long expectedBlobLength)
+ throws IOException {
+ while (sectionLength >= Integer.BYTES) {
+ // Read blobs.
+ long blobLength = getUInt(in);
+ sectionLength -= Integer.BYTES;
+ if (blobLength > sectionLength) {
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signKrlBlobLengthInvalid, sectionName,
+ Long.valueOf(blobLength)));
+ }
+ if (expectedBlobLength != 0 && blobLength != expectedBlobLength) {
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signKrlBlobLengthInvalidExpected,
+ sectionName, Long.valueOf(blobLength),
+ Long.valueOf(expectedBlobLength)));
+ }
+ byte[] blob = new byte[(int) blobLength];
+ IO.readFully(in, blob);
+ sectionLength -= blobLength;
+ blobs.add(new Blob(blob));
+ }
+ if (sectionLength != 0) {
+ throw new StreamCorruptedException(
+ MessageFormat.format(SshdText.get().signKrlBlobLeftover,
+ sectionName, Long.valueOf(sectionLength)));
+ }
+ }
+
+ private static void readCertificates(Map<Blob, CertificateRevocation> certs,
+ InputStream in, long sectionLength) throws IOException {
+ long keyLength = getUInt(in);
+ sectionLength -= Integer.BYTES;
+ if (keyLength > sectionLength) {
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signKrlCaKeyLengthInvalid,
+ Long.valueOf(keyLength)));
+ }
+ Blob key = null;
+ if (keyLength > 0) {
+ byte[] blob = new byte[(int) keyLength];
+ IO.readFully(in, blob);
+ key = new Blob(blob);
+ sectionLength -= keyLength;
+ }
+ CertificateRevocation rev = certs.computeIfAbsent(key,
+ k -> new CertificateRevocation());
+ long reservedLength = getUInt(in);
+ sectionLength -= Integer.BYTES;
+ if (reservedLength > sectionLength) {
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signKrlCaKeyLengthInvalid,
+ Long.valueOf(reservedLength)));
+ }
+ in.skip(reservedLength);
+ sectionLength -= reservedLength;
+ if (sectionLength == 0) {
+ throw new StreamCorruptedException(
+ SshdText.get().signKrlNoCertificateSubsection);
+ }
+ while (sectionLength > 0) {
+ int subSection = in.read();
+ if (subSection < 0) {
+ throw new EOFException();
+ }
+ sectionLength--;
+ if (sectionLength < Integer.BYTES) {
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signKrlCertificateLeftover,
+ Long.valueOf(sectionLength)));
+ }
+ long subLength = getUInt(in);
+ sectionLength -= Integer.BYTES;
+ if (subLength > sectionLength) {
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signKrlCertificateSubsectionLength,
+ Long.valueOf(subLength)));
+ }
+ if (subLength > 0) {
+ switch (subSection) {
+ case CERT_SERIAL_LIST:
+ readSerials(rev.ranges, in, subLength, false);
+ break;
+ case CERT_SERIAL_RANGES:
+ readSerials(rev.ranges, in, subLength, true);
+ break;
+ case CERT_SERIAL_BITS:
+ readSerialBitSet(rev.ranges, in, subLength);
+ break;
+ case CERT_KEY_IDS:
+ readIds(rev.keyIds, in, subLength);
+ break;
+ case CERT_EXTENSIONS:
+ in.skip(subLength);
+ break;
+ default:
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signKrlUnknownSubsection,
+ Long.valueOf(subSection)));
+ }
+ }
+ sectionLength -= subLength;
+ }
+ }
+
+ private static void readSerials(SerialRangeSet set, InputStream in,
+ long length, boolean ranges) throws IOException {
+ while (length >= Long.BYTES) {
+ long a = getLong(in);
+ length -= Long.BYTES;
+ if (a == 0) {
+ throw new StreamCorruptedException(
+ SshdText.get().signKrlSerialZero);
+ }
+ if (!ranges) {
+ set.add(a);
+ continue;
+ }
+ if (length < Long.BYTES) {
+ throw new StreamCorruptedException(
+ MessageFormat.format(SshdText.get().signKrlShortRange,
+ Long.valueOf(length)));
+ }
+ long b = getLong(in);
+ length -= Long.BYTES;
+ if (Long.compareUnsigned(a, b) > 0) {
+ throw new StreamCorruptedException(
+ SshdText.get().signKrlEmptyRange);
+ }
+ set.add(a, b);
+ }
+ if (length != 0) {
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signKrlCertificateSubsectionLeftover,
+ Long.valueOf(length)));
+ }
+ }
+
+ private static void readSerialBitSet(SerialRangeSet set, InputStream in,
+ long subLength) throws IOException {
+ while (subLength > 0) {
+ if (subLength < Long.BYTES) {
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signKrlCertificateSubsectionLeftover,
+ Long.valueOf(subLength)));
+ }
+ long base = getLong(in);
+ subLength -= Long.BYTES;
+ if (subLength < Integer.BYTES) {
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signKrlCertificateSubsectionLeftover,
+ Long.valueOf(subLength)));
+ }
+ long setLength = getUInt(in);
+ subLength -= Integer.BYTES;
+ if (setLength == 0 || setLength > subLength) {
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signKrlInvalidBitSetLength,
+ Long.valueOf(setLength)));
+ }
+ // Now process the bits. Note that the mpint is stored MSB first.
+ //
+ // We set individual serial numbers (one for each set bit) and let
+ // the SerialRangeSet take care of coalescing for successive runs
+ // of set bits.
+ int n = (int) setLength;
+ for (int i = n - 1; i >= 0; i--) {
+ int b = in.read();
+ if (b < 0) {
+ throw new EOFException();
+ } else if (b == 0) {
+ // Stored as an mpint: may have leading zero bytes (actually
+ // at most one; if the high bit of the first byte is set).
+ continue;
+ }
+ for (int bit = 0,
+ mask = 1; bit < Byte.SIZE; bit++, mask <<= 1) {
+ if ((b & mask) != 0) {
+ set.add(base + (i * Byte.SIZE) + bit);
+ }
+ }
+ }
+ subLength -= setLength;
+ }
+ }
+
+ private static void readIds(Set<String> ids, InputStream in, long subLength)
+ throws IOException {
+ while (subLength >= Integer.BYTES) {
+ long length = getUInt(in);
+ subLength -= Integer.BYTES;
+ if (length > subLength) {
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signKrlInvalidKeyIdLength,
+ Long.valueOf(length)));
+ }
+ byte[] bytes = new byte[(int) length];
+ IO.readFully(in, bytes);
+ ids.add(new String(bytes, StandardCharsets.UTF_8));
+ subLength -= length;
+ }
+ if (subLength != 0) {
+ throw new StreamCorruptedException(MessageFormat.format(
+ SshdText.get().signKrlCertificateSubsectionLeftover,
+ Long.valueOf(subLength)));
+ }
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/OpenSshKrl.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/OpenSshKrl.java
new file mode 100644
index 0000000..7993def
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/OpenSshKrl.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.signing.ssh;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.security.PublicKey;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.sshd.common.config.keys.OpenSshCertificate;
+import org.apache.sshd.common.config.keys.PublicKeyEntry;
+import org.apache.sshd.common.util.io.ModifiableFileWatcher;
+import org.eclipse.jgit.util.IO;
+
+/**
+ * An implementation of an OpenSSH key revocation list (KRL), either a binary
+ * KRL or a simple list of public keys.
+ */
+class OpenSshKrl extends ModifiableFileWatcher {
+
+ private static record State(Set<String> keys, OpenSshBinaryKrl krl) {
+ // Empty
+ }
+
+ private State state;
+
+ public OpenSshKrl(Path path) {
+ super(path);
+ state = new State(Set.of(), null);
+ }
+
+ public boolean isRevoked(PublicKey key) throws IOException {
+ State current = refresh();
+ return isRevoked(current, key);
+ }
+
+ private boolean isRevoked(State current, PublicKey key) {
+ if (key instanceof OpenSshCertificate cert) {
+ OpenSshBinaryKrl krl = current.krl();
+ if (krl != null && krl.isRevoked(cert)) {
+ return true;
+ }
+ if (isRevoked(current, cert.getCaPubKey())
+ || isRevoked(current, cert.getCertPubKey())) {
+ return true;
+ }
+ return false;
+ }
+ OpenSshBinaryKrl krl = current.krl();
+ if (krl != null) {
+ return krl.isRevoked(key);
+ }
+ return current.keys().contains(PublicKeyEntry.toString(key));
+ }
+
+ private synchronized State refresh() throws IOException {
+ if (checkReloadRequired()) {
+ updateReloadAttributes();
+ try {
+ state = reload(getPath());
+ } catch (NoSuchFileException e) {
+ // File disappeared
+ resetReloadAttributes();
+ state = new State(Set.of(), null);
+ }
+ }
+ return state;
+ }
+
+ private static State reload(Path path) throws IOException {
+ try (BufferedInputStream in = new BufferedInputStream(
+ Files.newInputStream(path))) {
+ byte[] magic = new byte[OpenSshBinaryKrl.MAGIC.length];
+ in.mark(magic.length);
+ IO.readFully(in, magic);
+ if (Arrays.equals(magic, OpenSshBinaryKrl.MAGIC)) {
+ return new State(null, OpenSshBinaryKrl.load(in, true));
+ }
+ // Otherwise try reading it textually
+ in.reset();
+ return loadTextKrl(in);
+ }
+ }
+
+ private static State loadTextKrl(InputStream in) throws IOException {
+ Set<String> keys = new HashSet<>();
+ try (BufferedReader r = new BufferedReader(
+ new InputStreamReader(in, StandardCharsets.UTF_8))) {
+ String line;
+ for (;;) {
+ line = r.readLine();
+ if (line == null) {
+ break;
+ }
+ line = line.strip();
+ if (line.isEmpty() || line.charAt(0) == '#') {
+ continue;
+ }
+ keys.add(AllowedSigners.parsePublicKey(line, 0));
+ }
+ }
+ return new State(keys, null);
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/OpenSshSigningKeyDatabase.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/OpenSshSigningKeyDatabase.java
new file mode 100644
index 0000000..aa26886
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/OpenSshSigningKeyDatabase.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.signing.ssh;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.security.PublicKey;
+import java.time.Instant;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.lib.GpgConfig;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.signing.ssh.CachingSigningKeyDatabase;
+import org.eclipse.jgit.signing.ssh.VerificationException;
+import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.StringUtils;
+
+/**
+ * A {@link CachingSigningKeyDatabase} using the OpenSSH allowed signers file
+ * and the OpenSSH key revocation list.
+ */
+public class OpenSshSigningKeyDatabase implements CachingSigningKeyDatabase {
+
+ // Keep caches of allowed signers and KRLs. Cache by canonical path.
+
+ private static final int DEFAULT_CACHE_SIZE = 5;
+
+ private AtomicInteger cacheSize = new AtomicInteger(DEFAULT_CACHE_SIZE);
+
+ private class LRU<K, V> extends LinkedHashMap<K, V> {
+
+ private static final long serialVersionUID = 1L;
+
+ LRU() {
+ super(DEFAULT_CACHE_SIZE, 0.75f, true);
+ }
+
+ @Override
+ protected boolean removeEldestEntry(java.util.Map.Entry<K, V> eldest) {
+ return size() > cacheSize.get();
+ }
+ }
+
+ private final HashMap<Path, AllowedSigners> allowedSigners = new LRU<>();
+
+ private final HashMap<Path, OpenSshKrl> revocations = new LRU<>();
+
+ @Override
+ public boolean isRevoked(Repository repository, GpgConfig config,
+ PublicKey key) throws IOException {
+ String fileName = config.getSshRevocationFile();
+ if (StringUtils.isEmptyOrNull(fileName)) {
+ return false;
+ }
+ File file = getFile(repository, fileName);
+ OpenSshKrl revocationList;
+ synchronized (revocations) {
+ revocationList = revocations.computeIfAbsent(file.toPath(),
+ OpenSshKrl::new);
+ }
+ return revocationList.isRevoked(key);
+ }
+
+ @Override
+ public String isAllowed(Repository repository, GpgConfig config,
+ PublicKey key, String namespace, PersonIdent ident)
+ throws IOException, VerificationException {
+ String fileName = config.getSshAllowedSignersFile();
+ if (StringUtils.isEmptyOrNull(fileName)) {
+ // No file configured. Git would error out.
+ return null;
+ }
+ File file = getFile(repository, fileName);
+ AllowedSigners allowed;
+ synchronized (allowedSigners) {
+ allowed = allowedSigners.computeIfAbsent(file.toPath(),
+ AllowedSigners::new);
+ }
+ Instant gitTime = null;
+ if (ident != null) {
+ gitTime = ident.getWhenAsInstant();
+ }
+ return allowed.isAllowed(key, namespace, null, gitTime);
+ }
+
+ private File getFile(@NonNull Repository repository, String fileName)
+ throws IOException {
+ File file;
+ if (fileName.startsWith("~/") //$NON-NLS-1$
+ || fileName.startsWith('~' + File.separator)) {
+ file = FS.DETECTED.resolve(FS.DETECTED.userHome(),
+ fileName.substring(2));
+ } else {
+ file = new File(fileName);
+ if (!file.isAbsolute()) {
+ file = new File(repository.getWorkTree(), fileName);
+ }
+ }
+ return file.getCanonicalFile();
+ }
+
+ @Override
+ public int getCacheSize() {
+ return cacheSize.get();
+ }
+
+ @Override
+ public void setCacheSize(int size) {
+ if (size > 0) {
+ cacheSize.set(size);
+ pruneCache(size);
+ }
+ }
+
+ private void pruneCache(int size) {
+ prune(allowedSigners, size);
+ prune(revocations, size);
+ }
+
+ private void prune(HashMap<?, ?> map, int size) {
+ synchronized (map) {
+ if (map.size() <= size) {
+ return;
+ }
+ Iterator<?> iter = map.entrySet().iterator();
+ int i = 0;
+ while (iter.hasNext() && i < size) {
+ iter.next();
+ i++;
+ }
+ while (iter.hasNext()) {
+ iter.next();
+ iter.remove();
+ }
+ }
+ }
+
+ @Override
+ public void clearCache() {
+ synchronized (allowedSigners) {
+ allowedSigners.clear();
+ }
+ synchronized (revocations) {
+ revocations.clear();
+ }
+ }
+
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SerialRangeSet.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SerialRangeSet.java
new file mode 100644
index 0000000..f4eb884
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SerialRangeSet.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.signing.ssh;
+
+import java.util.TreeMap;
+
+import org.eclipse.jgit.internal.transport.sshd.SshdText;
+
+/**
+ * Encapsulates the storage for revoked certificate serial numbers.
+ */
+class SerialRangeSet {
+
+ /**
+ * A range of certificate serial numbers [from..to], i.e., with both range
+ * limits included.
+ */
+ private interface SerialRange {
+
+ long from();
+
+ long to();
+ }
+
+ private static record Singleton(long from) implements SerialRange {
+
+ @Override
+ public long to() {
+ return from;
+ }
+ }
+
+ private static record Range(long from, long to) implements SerialRange {
+
+ public Range(long from, long to) {
+ if (Long.compareUnsigned(from, to) > 0) {
+ throw new IllegalArgumentException(
+ SshdText.get().signKrlEmptyRange);
+ }
+ this.from = from;
+ this.to = to;
+ }
+ }
+
+ // We use the same data structure as OpenSSH; basically a TreeSet of mutable
+ // SerialRanges. To get "mutability", the set is implemented as a TreeMap
+ // with the same elements as keys and values.
+ //
+ // get(x) will return null if none of the serial numbers in the range x is
+ // in the set, and some range (partially) overlapping with x otherwise.
+ //
+ // containsKey(x) will return true if there is any (partially) overlapping
+ // range in the TreeMap.
+ private final TreeMap<SerialRange, SerialRange> ranges = new TreeMap<>(
+ SerialRangeSet::compare);
+
+ private static int compare(SerialRange a, SerialRange b) {
+ // Return == if they overlap
+ if (Long.compareUnsigned(a.to(), b.from()) >= 0
+ && Long.compareUnsigned(a.from(), b.to()) <= 0) {
+ return 0;
+ }
+ return Long.compareUnsigned(a.from(), b.from());
+ }
+
+ void add(long serial) {
+ add(ranges, new Singleton(serial));
+ }
+
+ void add(long from, long to) {
+ add(ranges, new Range(from, to));
+ }
+
+ boolean contains(long serial) {
+ return ranges.containsKey(new Singleton(serial));
+ }
+
+ int size() {
+ return ranges.size();
+ }
+
+ boolean isEmpty() {
+ return ranges.isEmpty();
+ }
+
+ private static void add(TreeMap<SerialRange, SerialRange> ranges,
+ SerialRange newRange) {
+ for (;;) {
+ SerialRange existing = ranges.get(newRange);
+ if (existing == null) {
+ break;
+ }
+ if (Long.compareUnsigned(existing.from(), newRange.from()) <= 0
+ && Long.compareUnsigned(existing.to(),
+ newRange.to()) >= 0) {
+ // newRange completely contained in existing
+ return;
+ }
+ ranges.remove(existing);
+ long newFrom = newRange.from();
+ if (Long.compareUnsigned(existing.from(), newFrom) < 0) {
+ newFrom = existing.from();
+ }
+ long newTo = newRange.to();
+ if (Long.compareUnsigned(existing.to(), newTo) > 0) {
+ newTo = existing.to();
+ }
+ newRange = new Range(newFrom, newTo);
+ }
+ // No overlapping range exists: check for coalescing with the
+ // previous/next range
+ SerialRange prev = ranges.floorKey(newRange);
+ if (prev != null && newRange.from() - prev.to() == 1) {
+ ranges.remove(prev);
+ newRange = new Range(prev.from(), newRange.to());
+ }
+ SerialRange next = ranges.ceilingKey(newRange);
+ if (next != null && next.from() - newRange.to() == 1) {
+ ranges.remove(next);
+ newRange = new Range(newRange.from(), next.to());
+ }
+ ranges.put(newRange, newRange);
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SigningDatabase.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SigningDatabase.java
new file mode 100644
index 0000000..e2e1a36
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SigningDatabase.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.signing.ssh;
+
+import org.eclipse.jgit.signing.ssh.CachingSigningKeyDatabase;
+import org.eclipse.jgit.signing.ssh.SigningKeyDatabase;
+
+/**
+ * A global {@link SigningKeyDatabase} instance.
+ */
+public final class SigningDatabase {
+
+ private static SigningKeyDatabase INSTANCE = new OpenSshSigningKeyDatabase();
+
+ private SigningDatabase() {
+ // No instantiation
+ }
+
+ /**
+ * Obtains the current instance.
+ *
+ * @return the global {@link SigningKeyDatabase}
+ */
+ public static synchronized SigningKeyDatabase getInstance() {
+ return INSTANCE;
+ }
+
+ /**
+ * Sets the global {@link SigningKeyDatabase}.
+ *
+ * @param database
+ * to set; if {@code null} a default database using the OpenSSH
+ * allowed signers file and the OpenSSH revocation list mechanism
+ * is used.
+ * @return the previously set {@link SigningKeyDatabase}
+ */
+ public static synchronized SigningKeyDatabase setInstance(
+ SigningKeyDatabase database) {
+ SigningKeyDatabase previous = INSTANCE;
+ if (database != INSTANCE) {
+ if (INSTANCE instanceof CachingSigningKeyDatabase caching) {
+ caching.clearCache();
+ }
+ if (database == null) {
+ INSTANCE = new OpenSshSigningKeyDatabase();
+ } else {
+ INSTANCE = database;
+ }
+ }
+ return previous;
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SshCertificateUtils.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SshCertificateUtils.java
new file mode 100644
index 0000000..040c6d4
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SshCertificateUtils.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.signing.ssh;
+
+import java.security.PublicKey;
+import java.text.MessageFormat;
+import java.time.Instant;
+
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.OpenSshCertificate;
+import org.apache.sshd.common.signature.BuiltinSignatures;
+import org.apache.sshd.common.signature.Signature;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.internal.transport.sshd.SshdText;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Utility methods for working with OpenSSH certificates.
+ */
+final class SshCertificateUtils {
+
+ private static final Logger LOG = LoggerFactory
+ .getLogger(SshCertificateUtils.class);
+
+ /**
+ * Verifies a certificate: checks that it is a user certificate and has a
+ * valid signature, and if a time is given, that the certificate is valid at
+ * that time.
+ *
+ * @param certificate
+ * {@link OpenSshCertificate} to verify
+ * @param signatureTime
+ * {@link Instant} to check whether the certificate is valid at
+ * that time; maybe {@code null}, in which case the valid-time
+ * check is skipped.
+ * @return {@code null} if the certificate is valid; otherwise a descriptive
+ * message
+ */
+ static String verify(OpenSshCertificate certificate,
+ Instant signatureTime) {
+ if (!OpenSshCertificate.Type.USER.equals(certificate.getType())) {
+ return MessageFormat.format(SshdText.get().signNotUserCertificate,
+ KeyUtils.getFingerPrint(certificate.getCaPubKey()));
+ }
+ String message = verifySignature(certificate);
+ if (message == null && signatureTime != null) {
+ message = checkExpiration(certificate, signatureTime);
+ }
+ return message;
+ }
+
+ /**
+ * Verifies the signature on a certificate.
+ *
+ * @param certificate
+ * {@link OpenSshCertificate} to verify
+ * @return {@code null} if the signature is valid; otherwise a descriptive
+ * message
+ */
+ static String verifySignature(OpenSshCertificate certificate) {
+ // Verify the signature on the certificate.
+ //
+ // Note that OpenSSH certificates do not support chaining.
+ //
+ // ssh-keygen refuses to create a certificate for a certificate, so the
+ // certified key cannot be another OpenSshCertificate. Additionally,
+ // when creating a certificate ssh-keygen loads the CA private key to
+ // make the signature and reconstructs the public key that it stores in
+ // the certificate from that, so the CA public key also cannot be an
+ // OpenSshCertificate.
+ PublicKey caKey = certificate.getCaPubKey();
+ PublicKey certifiedKey = certificate.getCertPubKey();
+ if (caKey == null
+ || caKey instanceof OpenSshCertificate
+ || certifiedKey == null
+ || certifiedKey instanceof OpenSshCertificate) {
+ return SshdText.get().signCertificateInvalid;
+ }
+ // Verify that key type and algorithm match
+ String keyType = KeyUtils.getKeyType(caKey);
+ String certAlgorithm = certificate.getSignatureAlgorithm();
+ if (!KeyUtils.getCanonicalKeyType(keyType)
+ .equals(KeyUtils.getCanonicalKeyType(certAlgorithm))) {
+ return MessageFormat.format(
+ SshdText.get().signCertAlgorithmMismatch, keyType,
+ KeyUtils.getFingerPrint(certificate.getCaPubKey()),
+ certAlgorithm);
+ }
+ BuiltinSignatures factory = BuiltinSignatures
+ .fromFactoryName(certAlgorithm);
+ if (factory == null || !factory.isSupported()) {
+ return MessageFormat.format(SshdText.get().signCertAlgorithmUnknown,
+ KeyUtils.getFingerPrint(certificate.getCaPubKey()),
+ certAlgorithm);
+ }
+ Signature signer = factory.create();
+ try {
+ signer.initVerifier(null, caKey);
+ signer.update(null, getBlob(certificate));
+ if (signer.verify(null, certificate.getRawSignature())) {
+ return null;
+ }
+ } catch (Exception e) {
+ LOG.warn("{}", SshdText.get().signLogFailure, e); //$NON-NLS-1$
+ return SshdText.get().signSeeLog;
+ }
+ return MessageFormat.format(SshdText.get().signCertificateInvalid,
+ KeyUtils.getFingerPrint(certificate.getCaPubKey()));
+ }
+
+ private static byte[] getBlob(OpenSshCertificate certificate) {
+ // Theoretically, this should be just certificate.getMessage(). But
+ // Apache MINA sshd has a bug and may return additional bytes if the
+ // certificate is not the first thing in the buffer it was read from.
+ // As a work-around, re-create the signed blob from scratch.
+ //
+ // This may be replaced by return certificate.getMessage() once the
+ // upstream bug is fixed.
+ //
+ // See https://github.com/apache/mina-sshd/issues/618
+ Buffer tmp = new ByteArrayBuffer();
+ tmp.putString(certificate.getKeyType());
+ tmp.putBytes(certificate.getNonce());
+ tmp.putRawPublicKeyBytes(certificate.getCertPubKey());
+ tmp.putLong(certificate.getSerial());
+ tmp.putInt(certificate.getType().getCode());
+ tmp.putString(certificate.getId());
+ Buffer list = new ByteArrayBuffer();
+ list.putStringList(certificate.getPrincipals(), false);
+ tmp.putBytes(list.getCompactData());
+ tmp.putLong(certificate.getValidAfter());
+ tmp.putLong(certificate.getValidBefore());
+ tmp.putCertificateOptions(certificate.getCriticalOptions());
+ tmp.putCertificateOptions(certificate.getExtensions());
+ tmp.putString(certificate.getReserved());
+ Buffer inner = new ByteArrayBuffer();
+ inner.putRawPublicKey(certificate.getCaPubKey());
+ tmp.putBytes(inner.getCompactData());
+ return tmp.getCompactData();
+ }
+
+ /**
+ * Checks whether a certificate is valid at a given time.
+ *
+ * @param certificate
+ * {@link OpenSshCertificate} to check
+ * @param signatureTime
+ * {@link Instant} to check
+ * @return {@code null} if the certificate is valid at the given instant;
+ * otherwise a descriptive message
+ */
+ static String checkExpiration(OpenSshCertificate certificate,
+ @NonNull Instant signatureTime) {
+ long instant = signatureTime.getEpochSecond();
+ if (Long.compareUnsigned(instant, certificate.getValidAfter()) < 0) {
+ return MessageFormat.format(SshdText.get().signCertificateTooEarly,
+ KeyUtils.getFingerPrint(certificate.getCaPubKey()));
+ } else if (Long.compareUnsigned(instant,
+ certificate.getValidBefore()) > 0) {
+ return MessageFormat.format(SshdText.get().signCertificateExpired,
+ KeyUtils.getFingerPrint(certificate.getCaPubKey()));
+ }
+ return null;
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SshSignatureConstants.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SshSignatureConstants.java
new file mode 100644
index 0000000..bc72196
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SshSignatureConstants.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.signing.ssh;
+
+import java.nio.charset.StandardCharsets;
+
+import org.eclipse.jgit.lib.Constants;
+
+/**
+ * Defines common constants for SSH signatures.
+ */
+final class SshSignatureConstants {
+
+ private static final String SIGNATURE_END = "-----END SSH SIGNATURE-----"; //$NON-NLS-1$
+
+ static final byte[] MAGIC = { 'S', 'S', 'H', 'S', 'I', 'G' };
+
+ static final int VERSION = 1;
+
+ static final String NAMESPACE = "git"; //$NON-NLS-1$
+
+ static final byte[] ARMOR_HEAD = Constants.SSH_SIGNATURE_PREFIX
+ .getBytes(StandardCharsets.US_ASCII);
+
+ static final byte[] ARMOR_END = SIGNATURE_END
+ .getBytes(StandardCharsets.US_ASCII);
+
+ private SshSignatureConstants() {
+ // No instantiation
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SshSignatureVerifier.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SshSignatureVerifier.java
new file mode 100644
index 0000000..76be340
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SshSignatureVerifier.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.signing.ssh;
+
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.text.MessageFormat;
+import java.time.Instant;
+import java.util.Date;
+import java.util.Locale;
+
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.OpenSshCertificate;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.signature.BuiltinSignatures;
+import org.apache.sshd.common.signature.Signature;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+import org.eclipse.jgit.internal.transport.sshd.SshdText;
+import org.eclipse.jgit.lib.GpgConfig;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.SignatureVerifier;
+import org.eclipse.jgit.signing.ssh.CachingSigningKeyDatabase;
+import org.eclipse.jgit.signing.ssh.SigningKeyDatabase;
+import org.eclipse.jgit.signing.ssh.VerificationException;
+import org.eclipse.jgit.util.Base64;
+import org.eclipse.jgit.util.RawParseUtils;
+import org.eclipse.jgit.util.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A {@link SignatureVerifier} for SSH signatures.
+ */
+public class SshSignatureVerifier implements SignatureVerifier {
+
+ private static final Logger LOG = LoggerFactory
+ .getLogger(SshSignatureVerifier.class);
+
+ private static final byte[] OBJECT = { 'o', 'b', 'j', 'e', 'c', 't', ' ' };
+
+ private static final byte[] TREE = { 't', 'r', 'e', 'e', ' ' };
+
+ private static final byte[] TYPE = { 't', 'y', 'p', 'e', ' ' };
+
+ @Override
+ public String getName() {
+ return "ssh"; //$NON-NLS-1$
+ }
+
+ @Override
+ public SignatureVerification verify(Repository repository, GpgConfig config,
+ byte[] data, byte[] signatureData) throws IOException {
+ // This is a bit stupid. SSH signatures do not store a signer, nor a
+ // time the signature was created. So we must use the committer's or
+ // tagger's PersonIdent, but here we have neither. But... if we see
+ // that the data is a commit or tag, then we can parse the PersonIdent
+ // from the data.
+ //
+ // Note: we cannot assume that absent a principal recorded in the
+ // allowedSignersFile or on a certificate that the key used to sign the
+ // commit belonged to the committer.
+ PersonIdent gitIdentity = getGitIdentity(data);
+ Date signatureDate = null;
+ Instant signatureInstant = null;
+ if (gitIdentity != null) {
+ signatureDate = gitIdentity.getWhen();
+ signatureInstant = gitIdentity.getWhenAsInstant();
+ }
+
+ TrustLevel trust = TrustLevel.NEVER;
+ byte[] decodedSignature;
+ try {
+ decodedSignature = dearmor(signatureData);
+ } catch (IllegalArgumentException e) {
+ return new SignatureVerification(getName(), signatureDate, null,
+ null, null, false, false, trust,
+ MessageFormat.format(SshdText.get().signInvalidSignature,
+ e.getLocalizedMessage()));
+ }
+ int start = RawParseUtils.match(decodedSignature, 0,
+ SshSignatureConstants.MAGIC);
+ if (start < 0) {
+ return new SignatureVerification(getName(), signatureDate, null,
+ null, null, false, false, trust,
+ SshdText.get().signInvalidMagic);
+ }
+ ByteArrayBuffer signature = new ByteArrayBuffer(decodedSignature, start,
+ decodedSignature.length - start);
+
+ long version = signature.getUInt();
+ if (version != SshSignatureConstants.VERSION) {
+ return new SignatureVerification(getName(), signatureDate, null,
+ null, null, false, false, trust,
+ MessageFormat.format(SshdText.get().signInvalidVersion,
+ Long.toString(version)));
+ }
+
+ PublicKey key = signature.getPublicKey();
+ String fingerprint;
+ if (key instanceof OpenSshCertificate cert) {
+ fingerprint = KeyUtils.getFingerPrint(cert.getCertPubKey());
+ String message = SshCertificateUtils.verify(cert, signatureInstant);
+ if (message != null) {
+ return new SignatureVerification(getName(), signatureDate, null,
+ fingerprint, null, false, false, trust, message);
+ }
+ } else {
+ fingerprint = KeyUtils.getFingerPrint(key);
+ }
+
+ String namespace = signature.getString();
+ if (!SshSignatureConstants.NAMESPACE.equals(namespace)) {
+ return new SignatureVerification(getName(), signatureDate, null,
+ fingerprint, null, false, false, trust,
+ MessageFormat.format(SshdText.get().signInvalidNamespace,
+ namespace));
+ }
+
+ signature.getString(); // Skip the reserved field
+ String hashAlgorithm = signature.getString();
+ byte[] hash;
+ try {
+ hash = MessageDigest
+ .getInstance(hashAlgorithm.toUpperCase(Locale.ROOT))
+ .digest(data);
+ } catch (NoSuchAlgorithmException e) {
+ return new SignatureVerification(getName(), signatureDate, null,
+ fingerprint, null, false, false, trust,
+ MessageFormat.format(
+ SshdText.get().signUnknownHashAlgorithm,
+ hashAlgorithm));
+ }
+ ByteArrayBuffer rawSignature = new ByteArrayBuffer(
+ signature.getBytes());
+ if (signature.available() > 0) {
+ return new SignatureVerification(getName(), signatureDate, null,
+ fingerprint, null, false, false, trust,
+ SshdText.get().signGarbageAtEnd);
+ }
+
+ String signatureAlgorithm = rawSignature.getString();
+ switch (signatureAlgorithm) {
+ case KeyPairProvider.SSH_DSS:
+ case KeyPairProvider.SSH_DSS_CERT:
+ case KeyPairProvider.SSH_RSA:
+ case KeyPairProvider.SSH_RSA_CERT:
+ return new SignatureVerification(getName(), signatureDate, null,
+ fingerprint, null, false, false, trust,
+ MessageFormat.format(SshdText.get().signInvalidAlgorithm,
+ signatureAlgorithm));
+ }
+
+ String keyType = KeyUtils
+ .getSignatureAlgorithm(KeyUtils.getKeyType(key), key);
+ if (!KeyUtils.getCanonicalKeyType(keyType)
+ .equals(KeyUtils.getCanonicalKeyType(signatureAlgorithm))) {
+ return new SignatureVerification(getName(), signatureDate, null,
+ fingerprint, null, false, false, trust,
+ MessageFormat.format(
+ SshdText.get().signMismatchedSignatureAlgorithm,
+ keyType, signatureAlgorithm));
+ }
+
+ BuiltinSignatures factory = BuiltinSignatures
+ .fromFactoryName(signatureAlgorithm);
+ if (factory == null || !factory.isSupported()) {
+ return new SignatureVerification(getName(), signatureDate, null,
+ fingerprint, null, false, false, trust,
+ MessageFormat.format(
+ SshdText.get().signUnknownSignatureAlgorithm,
+ signatureAlgorithm));
+ }
+
+ boolean valid;
+ String message = null;
+ try {
+ Signature verifier = factory.create();
+ verifier.initVerifier(null,
+ key instanceof OpenSshCertificate cert
+ ? cert.getCertPubKey()
+ : key);
+ // Feed it the data
+ Buffer toSign = new ByteArrayBuffer();
+ toSign.putRawBytes(SshSignatureConstants.MAGIC);
+ toSign.putString(SshSignatureConstants.NAMESPACE);
+ toSign.putUInt(0); // reserved: zero-length string
+ toSign.putString(hashAlgorithm);
+ toSign.putBytes(hash);
+ verifier.update(null, toSign.getCompactData());
+ valid = verifier.verify(null, rawSignature.getBytes());
+ } catch (Exception e) {
+ LOG.warn("{}", SshdText.get().signLogFailure, e); //$NON-NLS-1$
+ valid = false;
+ message = SshdText.get().signSeeLog;
+ }
+ boolean expired = false;
+ String principal = null;
+ if (valid) {
+ if (rawSignature.available() > 0) {
+ valid = false;
+ message = SshdText.get().signGarbageAtEnd;
+ } else {
+ SigningKeyDatabase database = SigningKeyDatabase.getInstance();
+ if (database.isRevoked(repository, config, key)) {
+ valid = false;
+ if (key instanceof OpenSshCertificate certificate) {
+ message = MessageFormat.format(
+ SshdText.get().signCertificateRevoked,
+ KeyUtils.getFingerPrint(
+ certificate.getCaPubKey()));
+ } else {
+ message = SshdText.get().signKeyRevoked;
+ }
+ } else {
+ // This may turn a positive verification into a failed one.
+ try {
+ principal = database.isAllowed(repository, config, key,
+ SshSignatureConstants.NAMESPACE, gitIdentity);
+ if (!StringUtils.isEmptyOrNull(principal)) {
+ trust = TrustLevel.FULL;
+ } else {
+ valid = false;
+ message = SshdText.get().signNoPrincipalMatched;
+ trust = TrustLevel.UNKNOWN;
+ }
+ } catch (VerificationException e) {
+ valid = false;
+ message = e.getMessage();
+ expired = e.isExpired();
+ } catch (IOException e) {
+ LOG.warn("{}", SshdText.get().signLogFailure, e); //$NON-NLS-1$
+ valid = false;
+ message = SshdText.get().signSeeLog;
+ }
+ }
+ }
+ }
+ return new SignatureVerification(getName(), signatureDate, null,
+ fingerprint, principal, valid, expired, trust, message);
+ }
+
+ private static PersonIdent getGitIdentity(byte[] rawObject) {
+ // Data from a commit will start with "tree ID\n".
+ int i = RawParseUtils.match(rawObject, 0, TREE);
+ if (i > 0) {
+ i = RawParseUtils.committer(rawObject, 0);
+ if (i < 0) {
+ return null;
+ }
+ return RawParseUtils.parsePersonIdent(rawObject, i);
+ }
+ // Data from a tag will start with "object ID\ntype ".
+ i = RawParseUtils.match(rawObject, 0, OBJECT);
+ if (i > 0) {
+ i = RawParseUtils.nextLF(rawObject, i);
+ i = RawParseUtils.match(rawObject, i, TYPE);
+ if (i > 0) {
+ i = RawParseUtils.tagger(rawObject, 0);
+ if (i < 0) {
+ return null;
+ }
+ return RawParseUtils.parsePersonIdent(rawObject, i);
+ }
+ }
+ return null;
+ }
+
+ private static byte[] dearmor(byte[] data) {
+ int start = RawParseUtils.match(data, 0,
+ SshSignatureConstants.ARMOR_HEAD);
+ if (start > 0) {
+ if (data[start] == '\r') {
+ start++;
+ }
+ if (data[start] == '\n') {
+ start++;
+ }
+ }
+ int end = data.length;
+ if (end > start + 1 && data[end - 1] == '\n') {
+ end--;
+ if (end > start + 1 && data[end - 1] == '\r') {
+ end--;
+ }
+ }
+ end = end - SshSignatureConstants.ARMOR_END.length;
+ if (end >= 0 && end >= start
+ && RawParseUtils.match(data, end,
+ SshSignatureConstants.ARMOR_END) >= 0) {
+ // end is fine: on the first the character of the end marker
+ } else {
+ // No end marker.
+ end = data.length;
+ }
+ if (start < 0) {
+ start = 0;
+ }
+ return Base64.decode(data, start, end - start);
+ }
+
+ @Override
+ public void clear() {
+ SigningKeyDatabase database = SigningKeyDatabase.getInstance();
+ if (database instanceof CachingSigningKeyDatabase caching) {
+ caching.clearCache();
+ }
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SshSigner.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SshSigner.java
new file mode 100644
index 0000000..8cfe5f4
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/signing/ssh/SshSigner.java
@@ -0,0 +1,485 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.signing.ssh;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.StreamCorruptedException;
+import java.io.StringReader;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.text.MessageFormat;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.sshd.client.auth.pubkey.PublicKeyIdentity;
+import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.OpenSshCertificate;
+import org.apache.sshd.common.config.keys.PublicKeyEntryResolver;
+import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.session.SessionContext;
+import org.apache.sshd.common.signature.BuiltinSignatures;
+import org.apache.sshd.common.signature.Signature;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.api.errors.CanceledException;
+import org.eclipse.jgit.api.errors.UnsupportedSigningFormatException;
+import org.eclipse.jgit.internal.transport.sshd.AuthenticationCanceledException;
+import org.eclipse.jgit.internal.transport.sshd.PasswordProviderWrapper;
+import org.eclipse.jgit.internal.transport.sshd.SshdText;
+import org.eclipse.jgit.internal.transport.sshd.agent.SshAgentClient;
+import org.eclipse.jgit.lib.GpgConfig;
+import org.eclipse.jgit.lib.GpgSignature;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.Signer;
+import org.eclipse.jgit.transport.CredentialsProvider;
+import org.eclipse.jgit.transport.sshd.KeyPasswordProviderFactory;
+import org.eclipse.jgit.transport.sshd.agent.Connector;
+import org.eclipse.jgit.transport.sshd.agent.ConnectorFactory;
+import org.eclipse.jgit.util.Base64;
+import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.FS.ExecutionResult;
+import org.eclipse.jgit.util.StringUtils;
+import org.eclipse.jgit.util.SystemReader;
+import org.eclipse.jgit.util.TemporaryBuffer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A {@link Signer} to create SSH signatures.
+ *
+ * @see <a href=
+ * "https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.sshsig">PROTOCOL.sshsig</a>
+ */
+public class SshSigner implements Signer {
+
+ private static final Logger LOG = LoggerFactory.getLogger(SshSigner.class);
+
+ private static final String GIT_KEY_PREFIX = "key::"; //$NON-NLS-1$
+
+ // Base64 encoded lines should not be longer than 75 characters, plus the
+ // newline.
+ private static final int LINE_LENGTH = 75;
+
+ @Override
+ public GpgSignature sign(Repository repository, GpgConfig config,
+ byte[] data, PersonIdent committer, String signingKey,
+ CredentialsProvider credentialsProvider) throws CanceledException,
+ IOException, UnsupportedSigningFormatException {
+ byte[] hash;
+ try {
+ hash = MessageDigest.getInstance("SHA512").digest(data); //$NON-NLS-1$
+ } catch (NoSuchAlgorithmException e) {
+ throw new UnsupportedSigningFormatException(
+ MessageFormat.format(
+ SshdText.get().signUnknownHashAlgorithm, "SHA512"), //$NON-NLS-1$
+ e);
+ }
+ Buffer toSign = new ByteArrayBuffer();
+ toSign.putRawBytes(SshSignatureConstants.MAGIC);
+ toSign.putString(SshSignatureConstants.NAMESPACE);
+ toSign.putUInt(0); // reserved: zero-length string
+ toSign.putString("sha512"); //$NON-NLS-1$
+ toSign.putBytes(hash);
+ String key = signingKey;
+ if (StringUtils.isEmptyOrNull(key)) {
+ key = config.getSigningKey();
+ }
+ if (StringUtils.isEmptyOrNull(key)) {
+ key = defaultKeyCommand(repository, config);
+ // According to documentation, this is supposed to return a
+ // valid SSH public key prefixed with "key::". We don't enforce
+ // this: there might be older command implementations (like just
+ // calling "ssh-add -L") that return keys without prefix.
+ }
+ PublicKeyIdentity identity;
+ try {
+ identity = getIdentity(key, committer, credentialsProvider);
+ } catch (GeneralSecurityException e) {
+ throw new UnsupportedSigningFormatException(MessageFormat
+ .format(SshdText.get().signPublicKeyError, key), e);
+ }
+ String algorithm = KeyUtils
+ .getKeyType(identity.getKeyIdentity().getPublic());
+ switch (algorithm) {
+ case KeyPairProvider.SSH_DSS:
+ case KeyPairProvider.SSH_DSS_CERT:
+ throw new UnsupportedSigningFormatException(
+ SshdText.get().signInvalidKeyDSA);
+ case KeyPairProvider.SSH_RSA:
+ algorithm = KeyUtils.RSA_SHA512_KEY_TYPE_ALIAS;
+ break;
+ case KeyPairProvider.SSH_RSA_CERT:
+ algorithm = KeyUtils.RSA_SHA512_CERT_TYPE_ALIAS;
+ break;
+ default:
+ break;
+ }
+
+ Map.Entry<String, byte[]> rawSignature;
+ try {
+ rawSignature = identity.sign(null, algorithm,
+ toSign.getCompactData());
+ } catch (Exception e) {
+ throw new UnsupportedSigningFormatException(
+ SshdText.get().signSignatureError, e);
+ }
+ algorithm = rawSignature.getKey();
+ Buffer signature = new ByteArrayBuffer();
+ signature.putRawBytes(SshSignatureConstants.MAGIC);
+ signature.putUInt(SshSignatureConstants.VERSION);
+ signature.putPublicKey(identity.getKeyIdentity().getPublic());
+ signature.putString(SshSignatureConstants.NAMESPACE);
+ signature.putUInt(0); // reserved: zero-length string
+ signature.putString("sha512"); //$NON-NLS-1$
+ Buffer sig = new ByteArrayBuffer();
+ sig.putString(KeyUtils.getSignatureAlgorithm(algorithm,
+ identity.getKeyIdentity().getPublic()));
+ sig.putBytes(rawSignature.getValue());
+ signature.putBytes(sig.getCompactData());
+ return armor(signature.getCompactData());
+ }
+
+ private static String defaultKeyCommand(@NonNull Repository repository,
+ @NonNull GpgConfig config) throws IOException {
+ String command = config.getSshDefaultKeyCommand();
+ if (StringUtils.isEmptyOrNull(command)) {
+ return null;
+ }
+ FS fileSystem = repository.getFS();
+ if (fileSystem == null) {
+ fileSystem = FS.DETECTED;
+ }
+ ProcessBuilder builder = fileSystem.runInShell(command,
+ new String[] {});
+ ExecutionResult result = null;
+ try {
+ result = fileSystem.execute(builder, null);
+ int exitCode = result.getRc();
+ if (exitCode == 0) {
+ // The command is supposed to return a public key in its first
+ // line on stdout.
+ try (BufferedReader r = new BufferedReader(
+ new InputStreamReader(
+ result.getStdout().openInputStream(),
+ SystemReader.getInstance()
+ .getDefaultCharset()))) {
+ String line = r.readLine();
+ if (line != null) {
+ line = line.strip();
+ }
+ if (StringUtils.isEmptyOrNull(line)) {
+ throw new IOException(MessageFormat.format(
+ SshdText.get().signDefaultKeyEmpty, command));
+ }
+ return line;
+ }
+ }
+ TemporaryBuffer stderr = result.getStderr();
+ throw new IOException(MessageFormat.format(
+ SshdText.get().signDefaultKeyFailed, command,
+ Integer.toString(exitCode), toString(stderr)));
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new IOException(
+ MessageFormat.format(
+ SshdText.get().signDefaultKeyInterrupted, command),
+ e);
+ } finally {
+ if (result != null) {
+ if (result.getStderr() != null) {
+ result.getStderr().destroy();
+ }
+ if (result.getStdout() != null) {
+ result.getStdout().destroy();
+ }
+ }
+ }
+ }
+
+ private static String toString(TemporaryBuffer b) {
+ if (b != null) {
+ try {
+ return new String(b.toByteArray(4000),
+ SystemReader.getInstance().getDefaultCharset());
+ } catch (IOException e) {
+ LOG.warn("{}", SshdText.get().signStderr, e); //$NON-NLS-1$
+ }
+ }
+ return ""; //$NON-NLS-1$
+ }
+
+ private static PublicKeyIdentity getIdentity(String signingKey,
+ PersonIdent committer, CredentialsProvider credentials)
+ throws CanceledException, GeneralSecurityException, IOException {
+ if (StringUtils.isEmptyOrNull(signingKey)) {
+ throw new IllegalArgumentException(SshdText.get().signNoSigningKey);
+ }
+ PublicKey publicKey = null;
+ PrivateKey privateKey = null;
+ File keyFile = null;
+ if (signingKey.startsWith(GIT_KEY_PREFIX)) {
+ try (StringReader r = new StringReader(
+ signingKey.substring(GIT_KEY_PREFIX.length()))) {
+ publicKey = fromEntry(
+ AuthorizedKeyEntry.readAuthorizedKeys(r, true));
+ }
+ } else if (signingKey.startsWith("~/") //$NON-NLS-1$
+ || signingKey.startsWith('~' + File.separator)) {
+ keyFile = new File(FS.DETECTED.userHome(), signingKey.substring(2));
+ } else {
+ try (StringReader r = new StringReader(signingKey)) {
+ publicKey = fromEntry(
+ AuthorizedKeyEntry.readAuthorizedKeys(r, true));
+ } catch (IOException e) {
+ // Ignore and try to read as a file
+ keyFile = new File(signingKey);
+ }
+ }
+ if (keyFile != null && keyFile.isFile()) {
+ try {
+ publicKey = fromEntry(AuthorizedKeyEntry
+ .readAuthorizedKeys(keyFile.toPath()));
+ if (publicKey == null) {
+ throw new IOException(MessageFormat.format(
+ SshdText.get().signTooManyPublicKeys, keyFile));
+ }
+ // Try to find the private key so we don't go looking for
+ // the agent (or PKCS#11) in vain.
+ keyFile = getPrivateKeyFile(keyFile.getParentFile(),
+ keyFile.getName());
+ if (keyFile != null) {
+ try {
+ KeyPair pair = loadPrivateKey(keyFile.toPath(),
+ credentials);
+ if (pair != null) {
+ PublicKey pk = pair.getPublic();
+ if (pk == null) {
+ privateKey = pair.getPrivate();
+ } else {
+ PublicKey original = publicKey;
+ if (publicKey instanceof OpenSshCertificate cert) {
+ original = cert.getCertPubKey();
+ }
+ if (KeyUtils.compareKeys(original, pk)) {
+ privateKey = pair.getPrivate();
+ }
+ }
+ }
+ } catch (IOException e) {
+ // Apparently it wasn't a private key file. Ignore.
+ }
+ }
+ } catch (StreamCorruptedException e) {
+ // File is readable, but apparently not a public key. Try to
+ // load it as a private key.
+ KeyPair pair = loadPrivateKey(keyFile.toPath(), credentials);
+ if (pair != null) {
+ publicKey = pair.getPublic();
+ privateKey = pair.getPrivate();
+ }
+ }
+ }
+ if (publicKey == null) {
+ throw new IOException(MessageFormat
+ .format(SshdText.get().signNoPublicKey, signingKey));
+ }
+ if (publicKey instanceof OpenSshCertificate cert) {
+ String message = SshCertificateUtils.verify(cert,
+ committer.getWhenAsInstant());
+ if (message != null) {
+ throw new IOException(message);
+ }
+ }
+ if (privateKey == null) {
+ // Could be in the agent, or a PKCS#11 key. The normal procedure
+ // with PKCS#11 keys is to put them in the agent and let the agent
+ // deal with it.
+ //
+ // This may or may not work well. For instance, the agent might ask
+ // for a passphrase for PKCS#11 keys... also, the OpenSSH ssh-agent
+ // had a bug with signing using PKCS#11 certificates in the agent;
+ // see https://bugzilla.mindrot.org/show_bug.cgi?id=3613 . If there
+ // are troubles, we might do the PKCS#11 dance ourselves, but we'd
+ // need additional configuration for the PKCS#11 library. (Plus
+ // some refactoring in the Pkcs11Provider.)
+ return new AgentIdentity(publicKey);
+
+ }
+ return new KeyPairIdentity(new KeyPair(publicKey, privateKey));
+ }
+
+ private static File getPrivateKeyFile(File directory,
+ String publicKeyName) {
+ if (publicKeyName.endsWith(".pub")) { //$NON-NLS-1$
+ String privateKeyName = publicKeyName.substring(0,
+ publicKeyName.length() - 4);
+ if (!privateKeyName.isEmpty()) {
+ File keyFile = new File(directory, privateKeyName);
+ if (keyFile.isFile()) {
+ return keyFile;
+ }
+ if (privateKeyName.endsWith("-cert")) { //$NON-NLS-1$
+ privateKeyName = privateKeyName.substring(0,
+ privateKeyName.length() - 5);
+ if (!privateKeyName.isEmpty()) {
+ keyFile = new File(directory, privateKeyName);
+ if (keyFile.isFile()) {
+ return keyFile;
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ private static KeyPair loadPrivateKey(Path path,
+ CredentialsProvider credentials)
+ throws CanceledException, GeneralSecurityException, IOException {
+ if (!Files.isRegularFile(path)) {
+ return null;
+ }
+ KeyPairResourceParser parser = SecurityUtils.getKeyPairResourceParser();
+ if (parser != null) {
+ PasswordProviderWrapper provider = null;
+ if (credentials != null) {
+ provider = new PasswordProviderWrapper(
+ () -> KeyPasswordProviderFactory.getInstance()
+ .apply(credentials));
+ }
+ try {
+ Collection<KeyPair> keyPairs = parser.loadKeyPairs(null, path,
+ provider);
+ if (keyPairs.size() != 1) {
+ throw new GeneralSecurityException(MessageFormat.format(
+ SshdText.get().signTooManyPrivateKeys, path));
+ }
+ return keyPairs.iterator().next();
+ } catch (AuthenticationCanceledException e) {
+ throw new CanceledException(e.getMessage());
+ }
+ }
+ return null;
+ }
+
+ private static GpgSignature armor(byte[] data) throws IOException {
+ try (ByteArrayOutputStream b = new ByteArrayOutputStream()) {
+ b.write(SshSignatureConstants.ARMOR_HEAD);
+ b.write('\n');
+ String encoded = Base64.encodeBytes(data);
+ int length = encoded.length();
+ int column = 0;
+ for (int i = 0; i < length; i++) {
+ b.write(encoded.charAt(i));
+ column++;
+ if (column == LINE_LENGTH) {
+ b.write('\n');
+ column = 0;
+ }
+ }
+ if (column > 0) {
+ b.write('\n');
+ }
+ b.write(SshSignatureConstants.ARMOR_END);
+ b.write('\n');
+ return new GpgSignature(b.toByteArray());
+ }
+ }
+
+ private static PublicKey fromEntry(List<AuthorizedKeyEntry> entries)
+ throws GeneralSecurityException, IOException {
+ if (entries == null || entries.size() != 1) {
+ return null;
+ }
+ return entries.get(0).resolvePublicKey(null,
+ PublicKeyEntryResolver.FAILING);
+ }
+
+ @Override
+ public boolean canLocateSigningKey(Repository repository, GpgConfig config,
+ PersonIdent committer, String signingKey,
+ CredentialsProvider credentialsProvider) throws CanceledException {
+ String key = signingKey;
+ if (key == null) {
+ key = config.getSigningKey();
+ }
+ return !(StringUtils.isEmptyOrNull(key)
+ && StringUtils.isEmptyOrNull(config.getSshDefaultKeyCommand()));
+ }
+
+ private static class KeyPairIdentity implements PublicKeyIdentity {
+
+ private final @NonNull KeyPair pair;
+
+ KeyPairIdentity(@NonNull KeyPair pair) {
+ this.pair = pair;
+ }
+
+ @Override
+ public KeyPair getKeyIdentity() {
+ return pair;
+ }
+
+ @Override
+ public Entry<String, byte[]> sign(SessionContext session, String algo,
+ byte[] data) throws Exception {
+ BuiltinSignatures factory = BuiltinSignatures.fromFactoryName(algo);
+ if (factory == null || !factory.isSupported()) {
+ throw new GeneralSecurityException(MessageFormat.format(
+ SshdText.get().signUnknownSignatureAlgorithm, algo));
+ }
+ Signature signer = factory.create();
+ signer.initSigner(null, pair.getPrivate());
+ signer.update(null, data);
+ return new SimpleImmutableEntry<>(factory.getName(),
+ signer.sign(null));
+ }
+ }
+
+ private static class AgentIdentity extends KeyPairIdentity {
+
+ AgentIdentity(PublicKey publicKey) {
+ super(new KeyPair(publicKey, null));
+ }
+
+ @Override
+ public Entry<String, byte[]> sign(SessionContext session, String algo,
+ byte[] data) throws Exception {
+ ConnectorFactory factory = ConnectorFactory.getDefault();
+ Connector connector = factory == null ? null
+ : factory.create("", null); //$NON-NLS-1$
+ if (connector == null) {
+ throw new IOException(SshdText.get().signNoAgent);
+ }
+ try (SshAgentClient agent = new SshAgentClient(connector)) {
+ return agent.sign(null, getKeyIdentity().getPublic(), algo,
+ data);
+ }
+ }
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java
index b0b1028..6aace47 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/JGitPublicKeyAuthentication.java
@@ -17,6 +17,7 @@
import java.io.File;
import java.io.IOException;
+import java.io.StreamCorruptedException;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
@@ -355,20 +356,20 @@ private PublicKey readPublicKey(String keyFile, boolean isDerived) {
// only warn about non-existing files in case the key file is
// not derived
if (!isDerived) {
- log.warn("{}", //$NON-NLS-1$
+ log.warn(LOG_FORMAT,
format(SshdText.get().cannotReadPublicKey, keyFile));
}
- } catch (InvalidPathException | IOException e) {
- log.warn("{}", //$NON-NLS-1$
- format(SshdText.get().cannotReadPublicKey, keyFile), e);
- } catch (GeneralSecurityException e) {
+ } catch (GeneralSecurityException | StreamCorruptedException e) {
// ignore in case this is not a derived key path, as in most
// cases this specifies a private key
if (isDerived) {
- log.warn("{}", //$NON-NLS-1$
+ log.warn(LOG_FORMAT,
format(SshdText.get().cannotReadPublicKey, keyFile),
e);
}
+ } catch (InvalidPathException | IOException e) {
+ log.warn(LOG_FORMAT,
+ format(SshdText.get().cannotReadPublicKey, keyFile), e);
}
return null;
}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/KnownHostEntryReader.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/KnownHostEntryReader.java
index 96829b7..6b2345d 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/KnownHostEntryReader.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/KnownHostEntryReader.java
@@ -29,6 +29,7 @@
import org.apache.sshd.client.config.hosts.KnownHostEntry;
import org.apache.sshd.client.config.hosts.KnownHostHashValue;
import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
+import org.apache.sshd.common.config.keys.PublicKeyEntry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -97,7 +98,7 @@ private static String clean(String line) {
return i < 0 ? line.trim() : line.substring(0, i).trim();
}
- private static KnownHostEntry parseHostEntry(String line) {
+ static KnownHostEntry parseHostEntry(String line) {
KnownHostEntry entry = new KnownHostEntry();
entry.setConfigLine(line);
String tmp = line;
@@ -135,8 +136,8 @@ private static KnownHostEntry parseHostEntry(String line) {
entry.setPatterns(patterns);
}
tmp = tmp.substring(i + 1).trim();
- AuthorizedKeyEntry key = AuthorizedKeyEntry
- .parseAuthorizedKeyEntry(tmp);
+ AuthorizedKeyEntry key = PublicKeyEntry
+ .parsePublicKeyEntry(new AuthorizedKeyEntry(), tmp);
if (key == null) {
return null;
}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyDatabase.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyDatabase.java
index 2b4f7e5..acb77c5 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyDatabase.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/OpenSshServerKeyDatabase.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018, 2021 Thomas Wolf <thomas.wolf@paranor.ch> and others
+ * Copyright (C) 2018, 2025 Thomas Wolf <twolf@apache.org> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -31,9 +31,11 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
+import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
@@ -45,10 +47,13 @@
import org.apache.sshd.client.keyverifier.KnownHostsServerKeyVerifier.HostEntryPair;
import org.apache.sshd.client.session.ClientSession;
import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.SshConstants;
import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.OpenSshCertificate;
import org.apache.sshd.common.config.keys.PublicKeyEntry;
import org.apache.sshd.common.config.keys.PublicKeyEntryResolver;
+import org.apache.sshd.common.config.keys.UnsupportedSshPublicKey;
import org.apache.sshd.common.digest.BuiltinDigests;
import org.apache.sshd.common.mac.Mac;
import org.apache.sshd.common.util.io.ModifiableFileWatcher;
@@ -126,6 +131,9 @@ public class OpenSshServerKeyDatabase
/** Can be used to mark revoked known host lines. */
private static final String MARKER_REVOKED = "revoked"; //$NON-NLS-1$
+ /** Marks CA keys used for SSH certificates. */
+ private static final String MARKER_CA = "cert-authority"; //$NON-NLS-1$
+
private final boolean askAboutNewFile;
private final Map<Path, HostKeyFile> knownHostsFiles = new ConcurrentHashMap<>();
@@ -178,7 +186,10 @@ public List<PublicKey> lookup(@NonNull String connectAddress,
for (HostKeyFile file : filesToUse) {
for (HostEntryPair current : file.get()) {
KnownHostEntry entry = current.getHostEntry();
- if (!isRevoked(entry)) {
+ if (current.getServerKey() instanceof UnsupportedSshPublicKey) {
+ continue;
+ }
+ if (!isRevoked(entry) && !isCertificateAuthority(entry)) {
for (SshdSocketAddress host : candidates) {
if (entry.isHostMatch(host.getHostName(),
host.getPort())) {
@@ -204,6 +215,7 @@ public boolean accept(@NonNull String connectAddress,
Collection<SshdSocketAddress> candidates = getCandidates(connectAddress,
remoteAddress);
for (HostKeyFile file : filesToUse) {
+ HostEntryPair lastModified = modified[0];
try {
if (find(candidates, serverKey, file.get(), modified)) {
return true;
@@ -212,24 +224,35 @@ public boolean accept(@NonNull String connectAddress,
ask.revokedKey(remoteAddress, serverKey, file.getPath());
return false;
}
- if (path == null && modified[0] != null) {
+ if (modified[0] != lastModified) {
// Remember the file in which we might need to update the
// entry
path = file.getPath();
}
}
+ if (serverKey instanceof OpenSshCertificate) {
+ return false;
+ }
if (modified[0] != null) {
- // We found an entry, but with a different key
+ // We found an entry, but with a different key.
AskUser.ModifiedKeyHandling toDo = ask.acceptModifiedServerKey(
remoteAddress, modified[0].getServerKey(),
serverKey, path);
if (toDo == AskUser.ModifiedKeyHandling.ALLOW_AND_STORE) {
- try {
- updateModifiedServerKey(serverKey, modified[0], path);
- knownHostsFiles.get(path).resetReloadAttributes();
- } catch (IOException e) {
- LOG.warn(format(SshdText.get().knownHostsCouldNotUpdate,
- path));
+ if (modified[0]
+ .getServerKey() instanceof UnsupportedSshPublicKey) {
+ // Never update a line containing an unknown key type,
+ // always add.
+ addKeyToFile(filesToUse.get(0), candidates, serverKey, ask,
+ config);
+ } else {
+ try {
+ updateModifiedServerKey(serverKey, modified[0], path);
+ knownHostsFiles.get(path).resetReloadAttributes();
+ } catch (IOException e) {
+ LOG.warn(format(SshdText.get().knownHostsCouldNotUpdate,
+ path));
+ }
}
}
if (toDo == AskUser.ModifiedKeyHandling.DENY) {
@@ -242,19 +265,8 @@ public boolean accept(@NonNull String connectAddress,
return true;
} else if (ask.acceptUnknownKey(remoteAddress, serverKey)) {
if (!filesToUse.isEmpty()) {
- HostKeyFile toUpdate = filesToUse.get(0);
- path = toUpdate.getPath();
- try {
- if (Files.exists(path) || !askAboutNewFile
- || ask.createNewFile(path)) {
- updateKnownHostsFile(candidates, serverKey, path,
- config);
- toUpdate.resetReloadAttributes();
- }
- } catch (Exception e) {
- LOG.warn(format(SshdText.get().knownHostsCouldNotUpdate,
- path), e);
- }
+ addKeyToFile(filesToUse.get(0), candidates, serverKey, ask,
+ config);
}
return true;
}
@@ -265,39 +277,90 @@ private static class RevokedKeyException extends Exception {
private static final long serialVersionUID = 1L;
}
- private boolean isRevoked(KnownHostEntry entry) {
+ private static boolean isRevoked(KnownHostEntry entry) {
return MARKER_REVOKED.equals(entry.getMarker());
}
+ private static boolean isCertificateAuthority(KnownHostEntry entry) {
+ return MARKER_CA.equals(entry.getMarker());
+ }
+
private boolean find(Collection<SshdSocketAddress> candidates,
PublicKey serverKey, List<HostEntryPair> entries,
HostEntryPair[] modified) throws RevokedKeyException {
+ PublicKey keyToCheck = serverKey;
+ boolean isCert = false;
+ String keyType = KeyUtils.getKeyType(keyToCheck);
+ String modifiedKeyType = null;
+ if (modified[0] != null) {
+ modifiedKeyType = modified[0].getHostEntry().getKeyEntry()
+ .getKeyType();
+ }
+ if (serverKey instanceof OpenSshCertificate) {
+ keyToCheck = ((OpenSshCertificate) serverKey).getCaPubKey();
+ isCert = true;
+ }
for (HostEntryPair current : entries) {
KnownHostEntry entry = current.getHostEntry();
- for (SshdSocketAddress host : candidates) {
- if (entry.isHostMatch(host.getHostName(), host.getPort())) {
- boolean revoked = isRevoked(entry);
- if (KeyUtils.compareKeys(serverKey,
- current.getServerKey())) {
- // Exact match
- if (revoked) {
- throw new RevokedKeyException();
- }
+ if (candidates.stream().anyMatch(host -> entry
+ .isHostMatch(host.getHostName(), host.getPort()))) {
+ boolean revoked = isRevoked(entry);
+ boolean haveCert = isCertificateAuthority(entry);
+ if (KeyUtils.compareKeys(keyToCheck, current.getServerKey())) {
+ // Exact match
+ if (revoked) {
+ throw new RevokedKeyException();
+ }
+ if (haveCert == isCert) {
modified[0] = null;
return true;
- } else if (!revoked) {
- // Server sent a different key
- modified[0] = current;
- // Keep going -- maybe there's another entry for this
- // host
}
- break;
+ }
+ if (haveCert == isCert && !haveCert && !revoked) {
+ // Server sent a different key.
+ if (modifiedKeyType == null) {
+ modified[0] = current;
+ modifiedKeyType = entry.getKeyEntry().getKeyType();
+ } else if (!keyType.equals(modifiedKeyType)) {
+ String thisKeyType = entry.getKeyEntry().getKeyType();
+ if (isBetterMatch(keyType, thisKeyType,
+ modifiedKeyType)) {
+ // Since we may replace the modified[0] key,
+ // prefer to report a key of the same key type
+ // as having been modified.
+ modified[0] = current;
+ modifiedKeyType = keyType;
+ }
+ }
+ // Keep going -- maybe there's another entry for this
+ // host
}
}
}
return false;
}
+ private static boolean isBetterMatch(String keyType, String thisType,
+ String modifiedType) {
+ if (keyType.equals(thisType)) {
+ return true;
+ }
+ // EC keys are a bit special because they encode the curve in the key
+ // type. If we have no exactly matching EC key type in known_hosts, we
+ // still prefer to update an existing EC key type over some other key
+ // type.
+ if (!keyType.startsWith("ecdsa") || !thisType.startsWith("ecdsa")) { //$NON-NLS-1$ //$NON-NLS-2$
+ return false;
+ }
+ if (!modifiedType.startsWith("ecdsa")) { //$NON-NLS-1$
+ return true;
+ }
+ // All three are EC keys. thisType doesn't match the size of keyType
+ // (otherwise the two would have compared equal above already), so it is
+ // not better than modifiedType.
+ return false;
+ }
+
private List<HostKeyFile> addUserHostKeyFiles(List<String> fileNames) {
if (fileNames == null || fileNames.isEmpty()) {
return Collections.emptyList();
@@ -317,6 +380,21 @@ private List<HostKeyFile> addUserHostKeyFiles(List<String> fileNames) {
return userFiles;
}
+ private void addKeyToFile(HostKeyFile file,
+ Collection<SshdSocketAddress> candidates, PublicKey serverKey,
+ AskUser ask, Configuration config) {
+ Path path = file.getPath();
+ try {
+ if (Files.exists(path) || !askAboutNewFile
+ || ask.createNewFile(path)) {
+ updateKnownHostsFile(candidates, serverKey, path, config);
+ file.resetReloadAttributes();
+ }
+ } catch (Exception e) {
+ LOG.warn(format(SshdText.get().knownHostsCouldNotUpdate, path), e);
+ }
+ }
+
private void updateKnownHostsFile(Collection<SshdSocketAddress> candidates,
PublicKey serverKey, Path path, Configuration config)
throws Exception {
@@ -453,15 +531,22 @@ public void revokedKey(SocketAddress remoteAddress, PublicKey serverKey,
return;
}
InetSocketAddress remote = (InetSocketAddress) remoteAddress;
+ boolean isCert = serverKey instanceof OpenSshCertificate;
+ PublicKey keyToReport = isCert
+ ? ((OpenSshCertificate) serverKey).getCaPubKey()
+ : serverKey;
URIish uri = JGitUserInteraction.toURI(config.getUsername(),
remote);
String sha256 = KeyUtils.getFingerPrint(BuiltinDigests.sha256,
- serverKey);
- String md5 = KeyUtils.getFingerPrint(BuiltinDigests.md5, serverKey);
- String keyAlgorithm = serverKey.getAlgorithm();
+ keyToReport);
+ String md5 = KeyUtils.getFingerPrint(BuiltinDigests.md5,
+ keyToReport);
+ String keyAlgorithm = keyToReport.getAlgorithm();
+ String msg = isCert
+ ? SshdText.get().knownHostsRevokedCertificateMsg
+ : SshdText.get().knownHostsRevokedKeyMsg;
askUser(provider, uri, null, //
- format(SshdText.get().knownHostsRevokedKeyMsg,
- remote.getHostString(), path),
+ format(msg, remote.getHostString(), path),
format(SshdText.get().knownHostsKeyFingerprints,
keyAlgorithm),
md5, sha256);
@@ -594,7 +679,7 @@ private List<HostEntryPair> reload(Path path) throws IOException {
}
try {
PublicKey serverKey = keyPart.resolvePublicKey(null,
- PublicKeyEntryResolver.IGNORING);
+ PublicKeyEntryResolver.UNSUPPORTED);
if (serverKey == null) {
LOG.warn(format(
SshdText.get().knownHostsUnknownKeyType,
@@ -625,7 +710,7 @@ private int parsePort(String s) {
private SshdSocketAddress toSshdSocketAddress(@NonNull String address) {
String host = null;
- int port = 0;
+ int port = SshConstants.DEFAULT_PORT;
if (HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM == address
.charAt(0)) {
int end = address.indexOf(
@@ -665,12 +750,23 @@ private Collection<SshdSocketAddress> getCandidates(
if (address != null) {
candidates.add(address);
}
- return candidates;
+ List<SshdSocketAddress> result = new ArrayList<>();
+ result.addAll(candidates);
+ if (!remoteAddress.isUnresolved()) {
+ SshdSocketAddress ip = new SshdSocketAddress(
+ remoteAddress.getAddress().getHostAddress(),
+ remoteAddress.getPort());
+ if (candidates.add(ip)) {
+ result.add(ip);
+ }
+ }
+ return result;
}
private String createHostKeyLine(Collection<SshdSocketAddress> patterns,
PublicKey key, Configuration config) throws Exception {
StringBuilder result = new StringBuilder();
+ Set<String> knownNames = new HashSet<>();
if (config.getHashKnownHosts()) {
// SHA1 is the only algorithm for host name hashing known to OpenSSH
// or to Apache MINA sshd.
@@ -680,10 +776,10 @@ private String createHostKeyLine(Collection<SshdSocketAddress> patterns,
prng = new SecureRandom();
}
byte[] salt = new byte[mac.getDefaultBlockSize()];
- for (SshdSocketAddress address : patterns) {
- if (result.length() > 0) {
- result.append(',');
- }
+ // For hashed hostnames, only one hashed pattern is allowed per
+ // https://man.openbsd.org/sshd.8#SSH_KNOWN_HOSTS_FILE_FORMAT
+ if (!patterns.isEmpty()) {
+ SshdSocketAddress address = patterns.iterator().next();
prng.nextBytes(salt);
KnownHostHashValue.append(result, digester, salt,
KnownHostHashValue.calculateHashValue(
@@ -692,6 +788,10 @@ private String createHostKeyLine(Collection<SshdSocketAddress> patterns,
}
} else {
for (SshdSocketAddress address : patterns) {
+ String tgt = address.getHostName() + ':' + address.getPort();
+ if (!knownNames.add(tgt)) {
+ continue;
+ }
if (result.length() > 0) {
result.append(',');
}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/PasswordProviderWrapper.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/PasswordProviderWrapper.java
index 2cd0669..900c9fb 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/PasswordProviderWrapper.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/PasswordProviderWrapper.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018, 2020 Thomas Wolf <thomas.wolf@paranor.ch> and others
+ * Copyright (C) 2018, 2024 Thomas Wolf <twolf@apache.org> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -47,6 +47,8 @@ private static class PerSessionState {
private final Supplier<KeyPasswordProvider> factory;
+ private PerSessionState noSessionState;
+
/**
* Creates a new {@link PasswordProviderWrapper}.
*
@@ -59,13 +61,18 @@ public PasswordProviderWrapper(
}
private PerSessionState getState(SessionContext context) {
- PerSessionState state = context.getAttribute(STATE);
+ PerSessionState state = context != null ? context.getAttribute(STATE)
+ : noSessionState;
if (state == null) {
state = new PerSessionState();
state.delegate = factory.get();
state.delegate.setAttempts(
PASSWORD_PROMPTS.getRequiredDefault().intValue());
- context.setAttribute(STATE, state);
+ if (context != null) {
+ context.setAttribute(STATE, state);
+ } else {
+ noSessionState = state;
+ }
}
return state;
}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/SshdText.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/SshdText.java
index 05f04ac..e401378 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/SshdText.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/SshdText.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018, 2022 Thomas Wolf <thomas.wolf@paranor.ch> and others
+ * Copyright (C) 2018, 2024 Thomas Wolf <twolf@apache.org> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -83,6 +83,7 @@ public static SshdText get() {
/***/ public String knownHostsModifiedKeyDenyMsg;
/***/ public String knownHostsModifiedKeyStorePrompt;
/***/ public String knownHostsModifiedKeyWarning;
+ /***/ public String knownHostsRevokedCertificateMsg;
/***/ public String knownHostsRevokedKeyMsg;
/***/ public String knownHostsUnknownKeyMsg;
/***/ public String knownHostsUnknownKeyPrompt;
@@ -147,6 +148,71 @@ public static SshdText get() {
/***/ public String sshCommandTimeout;
/***/ public String sshProcessStillRunning;
/***/ public String sshProxySessionCloseFailed;
+ /***/ public String signAllowedSignersCertAuthorityError;
+ /***/ public String signAllowedSignersEmptyIdentity;
+ /***/ public String signAllowedSignersEmptyNamespaces;
+ /***/ public String signAllowedSignersFormatError;
+ /***/ public String signAllowedSignersInvalidDate;
+ /***/ public String signAllowedSignersLineFormat;
+ /***/ public String signAllowedSignersMultiple;
+ /***/ public String signAllowedSignersNoIdentities;
+ /***/ public String signAllowedSignersPublicKeyParsing;
+ /***/ public String signAllowedSignersUnterminatedQuote;
+ /***/ public String signCertAlgorithmMismatch;
+ /***/ public String signCertAlgorithmUnknown;
+ /***/ public String signCertificateExpired;
+ /***/ public String signCertificateInvalid;
+ /***/ public String signCertificateNotForName;
+ /***/ public String signCertificateRevoked;
+ /***/ public String signCertificateTooEarly;
+ /***/ public String signCertificateWithoutPrincipals;
+ /***/ public String signDefaultKeyEmpty;
+ /***/ public String signDefaultKeyFailed;
+ /***/ public String signDefaultKeyInterrupted;
+ /***/ public String signGarbageAtEnd;
+ /***/ public String signInvalidAlgorithm;
+ /***/ public String signInvalidKeyDSA;
+ /***/ public String signInvalidMagic;
+ /***/ public String signInvalidNamespace;
+ /***/ public String signInvalidSignature;
+ /***/ public String signInvalidVersion;
+ /***/ public String signKeyExpired;
+ /***/ public String signKeyRevoked;
+ /***/ public String signKeyTooEarly;
+ /***/ public String signKrlBlobLeftover;
+ /***/ public String signKrlBlobLengthInvalid;
+ /***/ public String signKrlBlobLengthInvalidExpected;
+ /***/ public String signKrlCaKeyLengthInvalid;
+ /***/ public String signKrlCertificateLeftover;
+ /***/ public String signKrlCertificateSubsectionLeftover;
+ /***/ public String signKrlCertificateSubsectionLength;
+ /***/ public String signKrlEmptyRange;
+ /***/ public String signKrlInvalidBitSetLength;
+ /***/ public String signKrlInvalidKeyIdLength;
+ /***/ public String signKrlInvalidMagic;
+ /***/ public String signKrlInvalidReservedLength;
+ /***/ public String signKrlInvalidVersion;
+ /***/ public String signKrlNoCertificateSubsection;
+ /***/ public String signKrlSerialZero;
+ /***/ public String signKrlShortRange;
+ /***/ public String signKrlUnknownSection;
+ /***/ public String signKrlUnknownSubsection;
+ /***/ public String signLogFailure;
+ /***/ public String signMismatchedSignatureAlgorithm;
+ /***/ public String signNoAgent;
+ /***/ public String signNoPrincipalMatched;
+ /***/ public String signNoPublicKey;
+ /***/ public String signNoSigningKey;
+ /***/ public String signNotUserCertificate;
+ /***/ public String signPublicKeyError;
+ /***/ public String signSeeLog;
+ /***/ public String signSignatureError;
+ /***/ public String signStderr;
+ /***/ public String signTooManyPrivateKeys;
+ /***/ public String signTooManyPublicKeys;
+ /***/ public String signUnknownHashAlgorithm;
+ /***/ public String signUnknownSignatureAlgorithm;
+ /***/ public String signWrongNamespace;
/***/ public String unknownProxyProtocol;
}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/BasicAuthentication.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/BasicAuthentication.java
index 8866976..3e1fab3 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/BasicAuthentication.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/internal/transport/sshd/auth/BasicAuthentication.java
@@ -17,8 +17,6 @@
import java.net.PasswordAuthentication;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
import java.util.Arrays;
import java.util.concurrent.CancellationException;
@@ -113,13 +111,12 @@ public void process() throws Exception {
*/
protected void askCredentials() {
clearPassword();
- PasswordAuthentication auth = AccessController.doPrivileged(
- (PrivilegedAction<PasswordAuthentication>) () -> Authenticator
- .requestPasswordAuthentication(proxy.getHostString(),
- proxy.getAddress(), proxy.getPort(),
- SshConstants.SSH_SCHEME,
- SshdText.get().proxyPasswordPrompt, "Basic", //$NON-NLS-1$
- null, RequestorType.PROXY));
+ PasswordAuthentication auth = Authenticator
+ .requestPasswordAuthentication(proxy.getHostString(),
+ proxy.getAddress(), proxy.getPort(),
+ SshConstants.SSH_SCHEME,
+ SshdText.get().proxyPasswordPrompt, "Basic", //$NON-NLS-1$
+ null, RequestorType.PROXY);
if (auth == null) {
user = ""; //$NON-NLS-1$
throw new CancellationException(
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/signing/ssh/CachingSigningKeyDatabase.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/signing/ssh/CachingSigningKeyDatabase.java
new file mode 100644
index 0000000..4d2d8b6
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/signing/ssh/CachingSigningKeyDatabase.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.signing.ssh;
+
+/**
+ * A {@link SigningKeyDatabase} that caches data.
+ * <p>
+ * A signing key database may be used to check keys frequently; it may thus need
+ * to cache some data and it may need to cache data per repository. If an
+ * implementation does cache data, it is responsible itself for refreshing that
+ * cache at appropriate times. Clients can control the cache size somewhat via
+ * {@link #setCacheSize(int)}, although the meaning of the cache size (i.e., its
+ * unit) is left undefined here.
+ * </p>
+ *
+ * @since 7.1
+ */
+public interface CachingSigningKeyDatabase extends SigningKeyDatabase {
+
+ /**
+ * Retrieves the current cache size.
+ *
+ * @return the cache size, or -1 if this database has no cache.
+ */
+ int getCacheSize();
+
+ /**
+ * Sets the cache size to use.
+ *
+ * @param size
+ * the cache size, ignored if this database does not have a
+ * cache.
+ * @throws IllegalArgumentException
+ * if {@code size < 0}
+ */
+ void setCacheSize(int size);
+
+ /**
+ * Discards any cached data. A no-op if the database has no cache.
+ */
+ void clearCache();
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/signing/ssh/SigningKeyDatabase.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/signing/ssh/SigningKeyDatabase.java
new file mode 100644
index 0000000..eec64c3
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/signing/ssh/SigningKeyDatabase.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.signing.ssh;
+
+import java.io.IOException;
+import java.security.PublicKey;
+
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.internal.signing.ssh.SigningDatabase;
+import org.eclipse.jgit.lib.GpgConfig;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Repository;
+
+/**
+ * A database storing meta-information about signing keys and certificates.
+ *
+ * @since 7.1
+ */
+public interface SigningKeyDatabase {
+
+ /**
+ * Obtains the current global instance.
+ *
+ * @return the global {@link SigningKeyDatabase}
+ */
+ static SigningKeyDatabase getInstance() {
+ return SigningDatabase.getInstance();
+ }
+
+ /**
+ * Sets the global {@link SigningKeyDatabase}.
+ *
+ * @param database
+ * to set; if {@code null} a default database using the OpenSSH
+ * allowed signers file and the OpenSSH revocation list mechanism
+ * is used.
+ * @return the previously set {@link SigningKeyDatabase}
+ */
+ static SigningKeyDatabase setInstance(SigningKeyDatabase database) {
+ return SigningDatabase.setInstance(database);
+ }
+
+ /**
+ * Determines whether the gives key has been revoked.
+ *
+ * @param repository
+ * {@link Repository} the key is being used in
+ * @param config
+ * {@link GpgConfig} to use
+ * @param key
+ * {@link PublicKey} to check
+ * @return {@code true} if the key has been revoked, {@code false} otherwise
+ * @throws IOException
+ * if an I/O problem occurred
+ */
+ boolean isRevoked(@NonNull Repository repository, @NonNull GpgConfig config,
+ @NonNull PublicKey key) throws IOException;
+
+ /**
+ * Checks whether the given key is allowed to be used for signing, and if
+ * allowed returns the principal.
+ *
+ * @param repository
+ * {@link Repository} the key is being used in
+ * @param config
+ * {@link GpgConfig} to use
+ * @param key
+ * {@link PublicKey} to check
+ * @param namespace
+ * of the signature
+ * @param ident
+ * optional {@link PersonIdent} giving a signer's e-mail address
+ * and a signature time
+ * @return {@code null} if the database does not contain any information
+ * about the given key; the principal if it does and all checks
+ * passed
+ * @throws IOException
+ * if an I/O problem occurred
+ * @throws VerificationException
+ * if the database contains information about the key and the
+ * checks determined that the key is not allowed to be used for
+ * signing
+ */
+ String isAllowed(@NonNull Repository repository, @NonNull GpgConfig config,
+ @NonNull PublicKey key, @NonNull String namespace,
+ PersonIdent ident) throws IOException, VerificationException;
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/signing/ssh/SshSignatureVerifierFactory.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/signing/ssh/SshSignatureVerifierFactory.java
new file mode 100644
index 0000000..c315428
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/signing/ssh/SshSignatureVerifierFactory.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.signing.ssh;
+
+import org.eclipse.jgit.lib.GpgConfig.GpgFormat;
+import org.eclipse.jgit.lib.SignatureVerifier;
+import org.eclipse.jgit.internal.signing.ssh.SshSignatureVerifier;
+import org.eclipse.jgit.lib.SignatureVerifierFactory;
+
+/**
+ * Factory creating {@link SshSignatureVerifier}s.
+ *
+ * @since 7.1
+ */
+public final class SshSignatureVerifierFactory
+ implements SignatureVerifierFactory {
+
+ @Override
+ public GpgFormat getType() {
+ return GpgFormat.SSH;
+ }
+
+ @Override
+ public SignatureVerifier create() {
+ return new SshSignatureVerifier();
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/signing/ssh/SshSignerFactory.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/signing/ssh/SshSignerFactory.java
new file mode 100644
index 0000000..5459b53
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/signing/ssh/SshSignerFactory.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.signing.ssh;
+
+import org.eclipse.jgit.lib.GpgConfig.GpgFormat;
+import org.eclipse.jgit.lib.Signer;
+import org.eclipse.jgit.internal.signing.ssh.SshSigner;
+import org.eclipse.jgit.lib.SignerFactory;
+
+/**
+ * Factory creating {@link SshSigner}s.
+ *
+ * @since 7.1
+ */
+public final class SshSignerFactory implements SignerFactory {
+
+ @Override
+ public GpgFormat getType() {
+ return GpgFormat.SSH;
+ }
+
+ @Override
+ public Signer create() {
+ return new SshSigner();
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/signing/ssh/VerificationException.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/signing/ssh/VerificationException.java
new file mode 100644
index 0000000..cd77111
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/signing/ssh/VerificationException.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.signing.ssh;
+
+/**
+ * An exception giving details about a failed
+ * {@link SigningKeyDatabase#isAllowed(org.eclipse.jgit.lib.Repository, org.eclipse.jgit.lib.GpgConfig, java.security.PublicKey, String, org.eclipse.jgit.lib.PersonIdent)}
+ * validation.
+ *
+ * @since 7.1
+ */
+public class VerificationException extends Exception {
+
+ private static final long serialVersionUID = 313760495170326160L;
+
+ private final boolean expired;
+
+ private final String reason;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param expired
+ * whether the checked public key or certificate was expired
+ * @param reason
+ * describing the check failure
+ */
+ public VerificationException(boolean expired, String reason) {
+ this.expired = expired;
+ this.reason = reason;
+ }
+
+ @Override
+ public String getMessage() {
+ return reason;
+ }
+
+ /**
+ * Tells whether the check failed because the public key was expired.
+ *
+ * @return {@code true} if the check failed because the public key was
+ * expired, {@code false} otherwise
+ */
+ public boolean isExpired() {
+ return expired;
+ }
+
+ /**
+ * Retrieves the check failure reason.
+ *
+ * @return the reason description
+ */
+ public String getReason() {
+ return reason;
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/KeyPasswordProviderFactory.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/KeyPasswordProviderFactory.java
new file mode 100644
index 0000000..0537300
--- /dev/null
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/KeyPasswordProviderFactory.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.transport.sshd;
+
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Function;
+
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.transport.CredentialsProvider;
+
+/**
+ * Maintains a static singleton instance of a factory to create a
+ * {@link KeyPasswordProvider} from a {@link CredentialsProvider}.
+ *
+ * @since 7.1
+ */
+public final class KeyPasswordProviderFactory {
+
+ /**
+ * Creates a {@link KeyPasswordProvider} from a {@link CredentialsProvider}.
+ */
+ @FunctionalInterface
+ public interface KeyPasswordProviderCreator
+ extends Function<CredentialsProvider, KeyPasswordProvider> {
+ // Nothing
+ }
+
+ private static final KeyPasswordProviderCreator DEFAULT = IdentityPasswordProvider::new;
+
+ private static AtomicReference<KeyPasswordProviderCreator> INSTANCE = new AtomicReference<>(
+ DEFAULT);
+
+ private KeyPasswordProviderFactory() {
+ // No instantiation
+ }
+
+ /**
+ * Retrieves the currently set {@link KeyPasswordProviderCreator}.
+ *
+ * @return the {@link KeyPasswordProviderCreator}
+ */
+ @NonNull
+ public static KeyPasswordProviderCreator getInstance() {
+ return INSTANCE.get();
+ }
+
+ /**
+ * Sets a new {@link KeyPasswordProviderCreator}.
+ *
+ * @param provider
+ * to set; if {@code null}, sets a default provider.
+ * @return the previously set {@link KeyPasswordProviderCreator}
+ */
+ @NonNull
+ public static KeyPasswordProviderCreator setInstance(
+ KeyPasswordProviderCreator provider) {
+ if (provider == null) {
+ return INSTANCE.getAndSet(DEFAULT);
+ }
+ return INSTANCE.getAndSet(provider);
+ }
+}
diff --git a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java
index 2c3cbe5..4a2eb9c 100644
--- a/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java
+++ b/org.eclipse.jgit.ssh.apache/src/org/eclipse/jgit/transport/sshd/SshdSessionFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018, 2022 Thomas Wolf <thomas.wolf@paranor.ch> and others
+ * Copyright (C) 2018, 2024 Thomas Wolf <twolf@apache.org> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -210,7 +210,7 @@ public SshdSession getSession(URIish uri,
home, sshDir);
KeyIdentityProvider defaultKeysProvider = toKeyIdentityProvider(
getDefaultKeys(sshDir));
- Supplier<KeyPasswordProvider> keyPasswordProvider = () -> createKeyPasswordProvider(
+ Supplier<KeyPasswordProvider> keyPasswordProvider = newKeyPasswordProvider(
credentialsProvider);
SshClient client = ClientBuilder.builder()
.factory(JGitSshClient::new)
@@ -574,12 +574,24 @@ protected final KeyCache getKeyCache() {
* @param provider
* the {@link CredentialsProvider} to delegate to for user
* interactions
- * @return a new {@link KeyPasswordProvider}
+ * @return a new {@link KeyPasswordProvider}, or {@code null} to use the
+ * global {@link KeyPasswordProviderFactory}
*/
- @NonNull
protected KeyPasswordProvider createKeyPasswordProvider(
CredentialsProvider provider) {
- return new IdentityPasswordProvider(provider);
+ return null;
+ }
+
+ private Supplier<KeyPasswordProvider> newKeyPasswordProvider(
+ CredentialsProvider credentials) {
+ return () -> {
+ KeyPasswordProvider provider = createKeyPasswordProvider(
+ credentials);
+ if (provider != null) {
+ return provider;
+ }
+ return KeyPasswordProviderFactory.getInstance().apply(credentials);
+ };
}
/**
diff --git a/org.eclipse.jgit.ssh.apache/src/sun/security/x509/README.md b/org.eclipse.jgit.ssh.apache/src/sun/security/x509/README.md
deleted file mode 100644
index a84ee37..0000000
--- a/org.eclipse.jgit.ssh.apache/src/sun/security/x509/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-This dummy package is used to fix the error
-"Missing requirement: net.i2p.crypto.eddsa 0.3.0 requires 'java.package; sun.security.x509 0.0.0'"
-raised since eddsa falsely requires this import
\ No newline at end of file
diff --git a/org.eclipse.jgit.ssh.jsch.test/BUILD b/org.eclipse.jgit.ssh.jsch.test/BUILD
index 4a8b925..d4e6875 100644
--- a/org.eclipse.jgit.ssh.jsch.test/BUILD
+++ b/org.eclipse.jgit.ssh.jsch.test/BUILD
@@ -8,7 +8,9 @@
srcs = glob(["tst/**/*.java"]),
tags = ["jsch"],
deps = [
- "//lib:eddsa",
+ "//lib:bcpkix",
+ "//lib:bcprov",
+ "//lib:bcutil",
"//lib:jsch",
"//lib:junit",
"//org.eclipse.jgit:jgit",
diff --git a/org.eclipse.jgit.ssh.jsch.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.ssh.jsch.test/META-INF/MANIFEST.MF
index 3bf4edd..34f8e0a 100644
--- a/org.eclipse.jgit.ssh.jsch.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.jsch.test/META-INF/MANIFEST.MF
@@ -3,19 +3,20 @@
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.ssh.jsch.test
Bundle-SymbolicName: org.eclipse.jgit.ssh.jsch.test
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-Vendor: %Bundle-Vendor
Bundle-Localization: plugin
Bundle-RequiredExecutionEnvironment: JavaSE-17
Require-Bundle: org.hamcrest.core;bundle-version="[1.3.0,2.0.0)"
Import-Package: com.jcraft.jsch;version="[0.1.54,0.2.0)",
- org.eclipse.jgit.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.junit;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.junit.ssh;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport.ssh.jsch;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)",
+ org.eclipse.jgit.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.junit;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.junit.ssh;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport.ssh.jsch;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.0)",
org.junit;version="[4.13,5.0.0)",
org.junit.experimental.theories;version="[4.13,5.0.0)",
org.junit.runner;version="[4.13,5.0.0)"
diff --git a/org.eclipse.jgit.ssh.jsch.test/pom.xml b/org.eclipse.jgit.ssh.jsch.test/pom.xml
index 761b6c4..5f094a7 100644
--- a/org.eclipse.jgit.ssh.jsch.test/pom.xml
+++ b/org.eclipse.jgit.ssh.jsch.test/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.ssh.jsch.test</artifactId>
diff --git a/org.eclipse.jgit.ssh.jsch.test/tst/org/eclipse/jgit/transport/ssh/jsch/JSchSshProtocol2Test.java b/org.eclipse.jgit.ssh.jsch.test/tst/org/eclipse/jgit/transport/ssh/jsch/JSchSshProtocol2Test.java
index 611d4e8..8aa33e3 100644
--- a/org.eclipse.jgit.ssh.jsch.test/tst/org/eclipse/jgit/transport/ssh/jsch/JSchSshProtocol2Test.java
+++ b/org.eclipse.jgit.ssh.jsch.test/tst/org/eclipse/jgit/transport/ssh/jsch/JSchSshProtocol2Test.java
@@ -22,7 +22,6 @@
import org.eclipse.jgit.errors.TransportException;
import org.eclipse.jgit.junit.ssh.SshBasicTestBase;
import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.RemoteSession;
@@ -89,7 +88,8 @@ private OpenSshConfig createConfig(String... content) throws IOException {
@Override
public void setUp() throws Exception {
super.setUp();
- StoredConfig config = ((Repository) db).getConfig();
+ @SuppressWarnings("restriction")
+ StoredConfig config = db.getConfig();
config.setInt("protocol", null, "version", 2);
config.save();
}
diff --git a/org.eclipse.jgit.ssh.jsch/META-INF/MANIFEST.MF b/org.eclipse.jgit.ssh.jsch/META-INF/MANIFEST.MF
index 9da7452..6b0130c 100644
--- a/org.eclipse.jgit.ssh.jsch/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.jsch/META-INF/MANIFEST.MF
@@ -3,19 +3,19 @@
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.ssh.jsch
Bundle-SymbolicName: org.eclipse.jgit.ssh.jsch;singleton:=true
-Fragment-Host: org.eclipse.jgit;bundle-version="[7.0.0,7.1.0)"
+Fragment-Host: org.eclipse.jgit;bundle-version="[7.3.0,7.4.0)"
Bundle-Vendor: %Bundle-Vendor
Bundle-Localization: OSGI-INF/l10n/jsch
Bundle-ActivationPolicy: lazy
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-RequiredExecutionEnvironment: JavaSE-17
-Export-Package: org.eclipse.jgit.transport.ssh.jsch;version="7.0.0"
+Export-Package: org.eclipse.jgit.transport.ssh.jsch;version="7.3.0"
Import-Package: com.jcraft.jsch;version="[0.1.37,0.2.0)",
- org.eclipse.jgit.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.transport.ssh;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.nls;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util.io;version="[7.0.0,7.1.0)",
+ org.eclipse.jgit.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.transport.ssh;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.nls;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util.io;version="[7.3.0,7.4.0)",
org.slf4j;version="[1.7.0,3.0.0)"
diff --git a/org.eclipse.jgit.ssh.jsch/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.ssh.jsch/META-INF/SOURCE-MANIFEST.MF
index 1dc0e01..06d0ce3 100644
--- a/org.eclipse.jgit.ssh.jsch/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.ssh.jsch/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@
Bundle-Name: org.eclipse.jgit.ssh.jsch - Sources
Bundle-SymbolicName: org.eclipse.jgit.ssh.jsch.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.0.0.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.ssh.jsch;version="7.0.0.qualifier";roots="."
+Bundle-Version: 7.3.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.ssh.jsch;version="7.3.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.ssh.jsch/pom.xml b/org.eclipse.jgit.ssh.jsch/pom.xml
index a3db63c..03ae29d 100644
--- a/org.eclipse.jgit.ssh.jsch/pom.xml
+++ b/org.eclipse.jgit.ssh.jsch/pom.xml
@@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.ssh.jsch</artifactId>
diff --git a/org.eclipse.jgit.ssh.jsch/src/org/eclipse/jgit/transport/ssh/jsch/JschSession.java b/org.eclipse.jgit.ssh.jsch/src/org/eclipse/jgit/transport/ssh/jsch/JschSession.java
index 5f36dad..ad58ae1 100644
--- a/org.eclipse.jgit.ssh.jsch/src/org/eclipse/jgit/transport/ssh/jsch/JschSession.java
+++ b/org.eclipse.jgit.ssh.jsch/src/org/eclipse/jgit/transport/ssh/jsch/JschSession.java
@@ -34,7 +34,6 @@
import org.eclipse.jgit.transport.URIish;
import org.eclipse.jgit.util.io.IsolatedOutputStream;
-import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSchException;
@@ -86,22 +85,6 @@ public void disconnect() {
}
/**
- * A kludge to allow {@link org.eclipse.jgit.transport.TransportSftp} to get
- * an Sftp channel from Jsch. Ideally, this method would be generic, which
- * would require implementing generic Sftp channel operations in the
- * RemoteSession class.
- *
- * @return a channel suitable for Sftp operations.
- * @throws com.jcraft.jsch.JSchException
- * on problems getting the channel.
- * @deprecated since 5.2; use {@link #getFtpChannel()} instead
- */
- @Deprecated
- public Channel getSftpChannel() throws JSchException {
- return sock.openChannel("sftp"); //$NON-NLS-1$
- }
-
- /**
* {@inheritDoc}
*
* @since 5.2
diff --git a/org.eclipse.jgit.test/BUILD b/org.eclipse.jgit.test/BUILD
index 29f5b36..7755df0 100644
--- a/org.eclipse.jgit.test/BUILD
+++ b/org.eclipse.jgit.test/BUILD
@@ -53,6 +53,11 @@
exclude = HELPERS + DATA + EXCLUDED,
))
+
+tests(tests = glob(["exttst/**/*.java"]),
+ srcprefix = "exttst/",
+ extra_tags = ["ext"])
+
# Non abstract base classes used for tests by other test classes
BASE = [
PKG + "internal/storage/file/FileRepositoryBuilderTest.java",
diff --git a/org.eclipse.jgit.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
index ab70d68..7ac93c2 100644
--- a/org.eclipse.jgit.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.test
Bundle-SymbolicName: org.eclipse.jgit.test
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-Localization: plugin
Bundle-Vendor: %Bundle-Vendor
Bundle-RequiredExecutionEnvironment: JavaSE-17
@@ -20,65 +20,68 @@
org.apache.commons.compress.compressors.xz;version="[1.15.0,2.0)",
org.apache.commons.io;version="[2.15.0,3.0.0)",
org.apache.commons.io.output;version="[2.15.0,3.0.0)",
+ org.apache.commons.lang3;version="[3.17.0,4.0.0)",
org.assertj.core.api;version="[3.14.0,4.0.0)",
- org.eclipse.jgit.annotations;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.api;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.api.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.archive;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.attributes;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.awtui;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.blame;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.diff;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.dircache;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.events;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.fnmatch;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.gitrepo;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.hooks;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.ignore;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.ignore.internal;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.diff;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.diffmergetool;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.fsck;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.revwalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.commitgraph;version="7.0.0",
- org.eclipse.jgit.internal.storage.dfs;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.file;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.io;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.memory;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.pack;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.storage.reftable;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.transport.connectivity;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.transport.http;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.transport.parser;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.internal.transport.ssh;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.junit;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.junit.time;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lfs;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib.internal;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.logging;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.merge;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.nls;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.notes;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.patch;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.pgm;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.pgm.internal;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.revplot;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.revwalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.revwalk.filter;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.storage.file;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.storage.pack;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.submodule;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport.http;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport.resolver;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.treewalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.treewalk.filter;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util.io;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util.sha1;version="[7.0.0,7.1.0)",
+ org.eclipse.jgit.annotations;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.api;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.api.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.archive;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.attributes;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.awtui;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.blame;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.blame.cache;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.diff;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.dircache;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.events;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.fnmatch;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.gitrepo;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.hooks;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.ignore;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.ignore.internal;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.diff;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.diffmergetool;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.fsck;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.revwalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.commitgraph;version="7.3.0",
+ org.eclipse.jgit.internal.storage.dfs;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.io;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.memory;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.midx;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.storage.reftable;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.transport.connectivity;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.transport.http;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.transport.parser;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.internal.transport.ssh;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.junit;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.junit.time;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lfs;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib.internal;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.logging;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.merge;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.nls;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.notes;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.patch;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.pgm;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.pgm.internal;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.revplot;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.revwalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.revwalk.filter;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.storage.file;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.storage.pack;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.submodule;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport.http;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport.resolver;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.treewalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.treewalk.filter;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util.io;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util.sha1;version="[7.3.0,7.4.0)",
org.junit;version="[4.13,5.0.0)",
org.junit.experimental.theories;version="[4.13,5.0.0)",
org.junit.function;version="[4.13.0,5.0.0)",
diff --git a/org.eclipse.jgit.test/exttst/org/eclipse/jgit/internal/storage/midx/CgitMidxCompatibilityTest.java b/org.eclipse.jgit.test/exttst/org/eclipse/jgit/internal/storage/midx/CgitMidxCompatibilityTest.java
new file mode 100644
index 0000000..88f0806
--- /dev/null
+++ b/org.eclipse.jgit.test/exttst/org/eclipse/jgit/internal/storage/midx/CgitMidxCompatibilityTest.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2025, Google Inc.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.storage.midx;
+
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.CHUNK_LOOKUP_WIDTH;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.MIDX_CHUNKID_OBJECTOFFSETS;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.MIDX_CHUNKID_OIDFANOUT;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.MIDX_CHUNKID_OIDLOOKUP;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.MIDX_CHUNKID_PACKNAMES;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jgit.internal.storage.file.Pack;
+import org.eclipse.jgit.internal.storage.file.PackFile;
+import org.eclipse.jgit.internal.storage.file.PackIndex;
+import org.eclipse.jgit.internal.storage.pack.PackExt;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
+import org.eclipse.jgit.util.NB;
+import org.junit.Test;
+
+public class CgitMidxCompatibilityTest extends SampleDataRepositoryTestCase {
+
+ @Test
+ public void jgitMidx_verifyByCgit()
+ throws IOException, InterruptedException {
+ byte[] jgitMidxBytes = generateJGitMidx();
+ writeMidx(jgitMidxBytes);
+ assertEquals("cgit exit code", 0, run_cgit_multipackindex_verify());
+ }
+
+ @Test
+ public void compareBasicChunkSizes()
+ throws IOException, InterruptedException {
+ // We cannot compare byte-by-byte because there are optional chunks and
+ // it is not guaranteed what cgit and jgit will generate
+ byte[] jgitMidxBytes = generateJGitMidx();
+ assertEquals("cgit exit code", 0, run_cgit_multipackindex_write());
+ byte[] cgitMidxBytes = readCgitMidx();
+
+ RawMultiPackIndex jgitMidx = new RawMultiPackIndex(jgitMidxBytes);
+ RawMultiPackIndex cgitMidx = new RawMultiPackIndex(cgitMidxBytes);
+
+ // This is a fixed sized chunk
+ assertEquals(256 * 4, cgitMidx.getChunkSize(MIDX_CHUNKID_OIDFANOUT));
+ assertArrayEquals(cgitMidx.getRawChunk(MIDX_CHUNKID_OIDFANOUT),
+ jgitMidx.getRawChunk(MIDX_CHUNKID_OIDFANOUT));
+
+ assertArrayEquals(cgitMidx.getRawChunk(MIDX_CHUNKID_OIDLOOKUP),
+ jgitMidx.getRawChunk(MIDX_CHUNKID_OIDLOOKUP));
+
+ // The spec has changed from padding packnames to a multile of four, to
+ // move the packname chunk to the end of the file.
+ // git 2.48 pads the packs names to a multiple of 4
+ // jgit puts the chunk at the end
+ byte[] cgitPacknames = trimPadding(
+ cgitMidx.getRawChunk(MIDX_CHUNKID_PACKNAMES));
+ assertArrayEquals(cgitPacknames,
+ jgitMidx.getRawChunk(MIDX_CHUNKID_PACKNAMES));
+
+ assertArrayEquals(cgitMidx.getRawChunk(MIDX_CHUNKID_OBJECTOFFSETS),
+ jgitMidx.getRawChunk(MIDX_CHUNKID_OBJECTOFFSETS));
+
+ }
+
+ private byte[] generateJGitMidx() throws IOException {
+ Map<String, PackIndex> indexes = new HashMap<>();
+ for (Pack pack : db.getObjectDatabase().getPacks()) {
+ PackFile packFile = pack.getPackFile().create(PackExt.INDEX);
+ indexes.put(packFile.getName(), pack.getIndex());
+ }
+
+ MultiPackIndexWriter writer = new MultiPackIndexWriter();
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ writer.write(NullProgressMonitor.INSTANCE, out, indexes);
+ return out.toByteArray();
+ }
+
+ private int run_cgit_multipackindex_write()
+ throws IOException, InterruptedException {
+ String[] command = new String[] { "git", "multi-pack-index", "write" };
+ Process proc = Runtime.getRuntime().exec(command, new String[0],
+ db.getDirectory());
+ return proc.waitFor();
+ }
+
+ private int run_cgit_multipackindex_verify()
+ throws IOException, InterruptedException {
+ String[] command = new String[] { "git", "multi-pack-index", "verify" };
+ Process proc = Runtime.getRuntime().exec(command, new String[0],
+ db.getDirectory());
+ return proc.waitFor();
+ }
+
+ private byte[] readCgitMidx() throws IOException {
+ File midx = getMIdxStandardLocation();
+ assertTrue("cgit multi-pack-index exists", midx.exists());
+ return Files.readAllBytes(midx.toPath());
+ }
+
+ private void writeMidx(byte[] midx) throws IOException {
+ File midxFile = getMIdxStandardLocation();
+ Files.write(midxFile.toPath(), midx);
+ }
+
+ private File getMIdxStandardLocation() {
+ return new File(db.getObjectDatabase().getPackDirectory(),
+ "multi-pack-index");
+ }
+
+ private byte[] trimPadding(byte[] data) {
+ // Chunk MUST have one \0, we want to remove any extra \0
+ int newEnd = data.length - 1;
+ while (newEnd - 1 >= 0 && data[newEnd - 1] == 0) {
+ newEnd--;
+ }
+
+ if (newEnd == data.length - 1) {
+ return data;
+ }
+ return Arrays.copyOfRange(data, 0, newEnd + 1);
+ }
+
+ private static class RawMultiPackIndex {
+ private final List<ChunkSegment> chunks;
+
+ private final byte[] midx;
+
+ private RawMultiPackIndex(byte[] midx) {
+ this.chunks = readChunks(midx);
+ this.midx = midx;
+ }
+
+ long getChunkSize(int chunkId) {
+ int chunkPos = findChunkPosition(chunks, chunkId);
+ return chunks.get(chunkPos + 1).offset
+ - chunks.get(chunkPos).offset;
+ }
+
+ long getOffset(int chunkId) {
+ return chunks.get(findChunkPosition(chunks, chunkId)).offset;
+ }
+
+ private long getNextOffset(int chunkId) {
+ return chunks.get(findChunkPosition(chunks, chunkId) + 1).offset;
+ }
+
+ byte[] getRawChunk(int chunkId) {
+ int start = (int) getOffset(chunkId);
+ int end = (int) getNextOffset(chunkId);
+ return Arrays.copyOfRange(midx, start, end);
+ }
+
+ private static int findChunkPosition(List<ChunkSegment> chunks,
+ int id) {
+ int chunkPos = -1;
+ for (int i = 0; i < chunks.size(); i++) {
+ if (chunks.get(i).id() == id) {
+ chunkPos = i;
+ break;
+ }
+ }
+ if (chunkPos == -1) {
+ throw new IllegalStateException("Chunk doesn't exist");
+ }
+ return chunkPos;
+ }
+
+ private List<ChunkSegment> readChunks(byte[] midx) {
+ // Read the number of "chunkOffsets" (1 byte)
+ int chunkCount = midx[6];
+ byte[] lookupBuffer = new byte[CHUNK_LOOKUP_WIDTH
+ * (chunkCount + 1)];
+ System.arraycopy(midx, 12, lookupBuffer, 0, lookupBuffer.length);
+
+ List<ChunkSegment> chunks = new ArrayList<>(chunkCount + 1);
+ for (int i = 0; i <= chunkCount; i++) {
+ // chunks[chunkCount] is just a marker, in order to record the
+ // length of the last chunk.
+ int id = NB.decodeInt32(lookupBuffer, i * 12);
+ long offset = NB.decodeInt64(lookupBuffer, i * 12 + 4);
+ chunks.add(new ChunkSegment(id, offset));
+ }
+ return chunks;
+ }
+ }
+
+ private record ChunkSegment(int id, long offset) {
+ }
+}
diff --git a/org.eclipse.jgit.test/pom.xml b/org.eclipse.jgit.test/pom.xml
index 05f7c08..9cf21fd 100644
--- a/org.eclipse.jgit.test/pom.xml
+++ b/org.eclipse.jgit.test/pom.xml
@@ -19,7 +19,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.test</artifactId>
diff --git a/org.eclipse.jgit.test/tests.bzl b/org.eclipse.jgit.test/tests.bzl
index 170bf0c..41f76d0 100644
--- a/org.eclipse.jgit.test/tests.bzl
+++ b/org.eclipse.jgit.test/tests.bzl
@@ -1,11 +1,29 @@
+'''
+Expose each test as a bazel target
+'''
load(
"@com_googlesource_gerrit_bazlets//tools:junit.bzl",
"junit_tests",
)
-def tests(tests):
+def tests(tests, srcprefix="tst/", extra_tags=[]):
+ '''
+ Create a target each of the tests
+
+ Each target is the full push (removing srcprefix) replacing directory
+ separators with underscores.
+
+ e.g. a test under tst/a/b/c/A.test will become the target
+ //org.eclipse.jgit.tests:a_b_c_A
+
+ Args:
+ tests: a glob of tests files
+ srcprefix: prefix between org.eclipse.jgit.tests and the package
+ start
+ extra_tags: additional tags to add to the generated targets
+ '''
for src in tests:
- name = src[len("tst/"):len(src) - len(".java")].replace("/", "_")
+ name = src[len(srcprefix):len(src) - len(".java")].replace("/", "_")
labels = []
timeout = "moderate"
if name.startswith("org_eclipse_jgit_"):
@@ -20,6 +38,8 @@
if "lib" not in labels:
labels.append("lib")
+ labels.extend(extra_tags)
+
# TODO(http://eclip.se/534285): Make this test pass reliably
# and remove the flaky attribute.
flaky = src.endswith("CrissCrossMergeTest.java")
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ConflictOutOfBounds.patch b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ConflictOutOfBounds.patch
new file mode 100644
index 0000000..6e7448b
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ConflictOutOfBounds.patch
@@ -0,0 +1,10 @@
+diff --git a/ConflictOutOfBounds b/ConflictOutOfBounds
+index 0000000..de98044
+--- a/ConflictOutOfBounds
++++ b/ConflictOutOfBounds
+@@ -25,4 +25,4 @@
+ line3
+-lineA
++lineB
+ line5
+ line6
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ConflictOutOfBounds_PostImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ConflictOutOfBounds_PostImage
new file mode 100644
index 0000000..4e5d5b2
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ConflictOutOfBounds_PostImage
@@ -0,0 +1,15 @@
+line1
+line2
+line3
+line4
+line5
+line6
+line7
+line8
+<<<<<<< HEAD
+=======
+line3
+lineB
+line5
+line6
+>>>>>>> PATCH
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ConflictOutOfBounds_PreImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ConflictOutOfBounds_PreImage
new file mode 100644
index 0000000..f62562a
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ConflictOutOfBounds_PreImage
@@ -0,0 +1,8 @@
+line1
+line2
+line3
+line4
+line5
+line6
+line7
+line8
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/allowconflict.patch b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/allowconflict.patch
new file mode 100644
index 0000000..a99e636
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/allowconflict.patch
@@ -0,0 +1,10 @@
+diff --git a/allowconflict b/allowconflict
+index 0000000..de98044
+--- a/allowconflict
++++ b/allowconflict
+@@ -3,4 +3,4 @@
+ line3
+-lineA
++lineB
+ line5
+ line6
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/allowconflict_PostImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/allowconflict_PostImage
new file mode 100644
index 0000000..a963b40
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/allowconflict_PostImage
@@ -0,0 +1,15 @@
+line1
+line2
+<<<<<<< HEAD
+line3
+line4
+line5
+line6
+=======
+line3
+lineB
+line5
+line6
+>>>>>>> PATCH
+line7
+line8
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/allowconflict_PreImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/allowconflict_PreImage
new file mode 100644
index 0000000..f62562a
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/allowconflict_PreImage
@@ -0,0 +1,8 @@
+line1
+line2
+line3
+line4
+line5
+line6
+line7
+line8
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/allowconflict_file_deleted.patch b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/allowconflict_file_deleted.patch
new file mode 100644
index 0000000..c9655a5
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/allowconflict_file_deleted.patch
@@ -0,0 +1,10 @@
+diff --git a/allowconflict_file_deleted b/allowconflict_file_deleted
+index 0000000..de98044
+--- a/allowconflict_file_deleted
++++ b/allowconflict_file_deleted
+@@ -3,4 +3,4 @@
+ line3
+-lineA
++lineB
+ line5
+ line6
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.c b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.c
new file mode 100644
index 0000000..3661160
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.c
@@ -0,0 +1,43 @@
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+void getGreeting(char *result, const char *name) {
+ sprintf(result, "Hello, %s!", name);
+}
+
+void getFarewell(char *result, const char *name) {
+ sprintf(result, "Goodbye, %s. Have a great day!", name);
+}
+
+void toLower(char *str) {
+ for (int i = 0; str[i]; i++) {
+ str[i] = tolower(str[i]);
+ }
+}
+
+void getPersonalizedGreeting(char *result, const char *name, const char *timeOfDay) {
+ char timeOfDayLower[50];
+ strcpy(timeOfDayLower, timeOfDay);
+ toLower(timeOfDayLower);
+ if (strcmp(timeOfDayLower, "morning") == 0) {
+ sprintf(result, "Good morning, %s", name);
+ } else if (strcmp(timeOfDayLower, "afternoon") == 0) {
+ sprintf(result, "Good afternoon, %s", name);
+ } else if (strcmp(timeOfDayLower, "evening") == 0) {
+ sprintf(result, "Good evening, %s", name);
+ } else {
+ sprintf(result, "Good day, %s", name);
+ }
+}
+
+int main() {
+ char result[100];
+ getGreeting(result, "foo");
+ printf("%s\\n", result);
+ getFarewell(result, "bar");
+ printf("%s\\n", result);
+ getPersonalizedGreeting(result, "baz", "morning");
+ printf("%s\\n", result);
+ return 0;
+}
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.javasource b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.javasource
new file mode 100644
index 0000000..9659685
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.javasource
@@ -0,0 +1,37 @@
+public class Greeting {
+ public String getGreeting(String name) {
+ String msg = "Hello, " + name + "!";
+ return msg;
+ }
+
+ public String getFarewell(String name) {
+ String msg = "Goodbye, " + name + ". Have a great day!";
+ return msg;
+ }
+
+ public String getPersonalizedGreeting(String name, String timeOfDay) {
+ String msg;
+ switch (timeOfDay.toLowerCase()) {
+ case "morning":
+ msg = "Good morning, " + name;
+ break;
+ case "afternoon":
+ msg = "Good afternoon, " + name;
+ break;
+ case "evening":
+ msg = "Good evening, " + name;
+ break;
+ default:
+ msg = "Good day, " + name;
+ break;
+ }
+ return msg;
+ }
+
+ public static void main(String[] args) {
+ Greeting greeting = new Greeting();
+ System.out.println(greeting.getGreeting("foo"));
+ System.out.println(greeting.getFarewell("bar"));
+ System.out.println(greeting.getPersonalizedGreeting("baz", "morning"));
+ }
+}
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.py b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.py
new file mode 100644
index 0000000..9eda6cd
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.py
@@ -0,0 +1,26 @@
+class Greeting:
+ def get_greeting(self, name):
+ greeting_message = f"Hello, {name}!"
+ return greeting_message
+
+ def get_farewell(self, name):
+ farewell_message = f"Goodbye, {name}. Have a great day!"
+ return farewell_message
+
+ def get_personalized_greeting(self, name, time_of_day):
+ time_of_day = time_of_day.lower()
+ if time_of_day == "morning":
+ personalized_message = f"Good morning, {name}"
+ elif time_of_day == "afternoon":
+ personalized_message = f"Good afternoon, {name}"
+ elif time_of_day == "evening":
+ personalized_message = f"Good evening, {name}"
+ else:
+ personalized_message = f"Good day, {name}"
+ return personalized_message
+
+if __name__ == "__main__":
+ greeting = Greeting()
+ print(greeting.get_greeting("foo"))
+ print(greeting.get_farewell("bar"))
+ print(greeting.get_personalized_greeting("baz", "morning"))
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.rs b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.rs
new file mode 100644
index 0000000..a3aa5cb
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.rs
@@ -0,0 +1,27 @@
+struct Greeting;
+
+impl Greeting {
+ fn get_greeting(&self, name: &str) -> String {
+ format!("Hello, {}!", name)
+ }
+
+ fn get_farewell(&self, name: &str) -> String {
+ format!("Goodbye, {}. Have a great day!", name)
+ }
+
+ fn get_personalized_greeting(&self, name: &str, time_of_day: &str) -> String {
+ match time_of_day.to_lowercase().as_str() {
+ "morning" => format!("Good morning, {}", name),
+ "afternoon" => format!("Good afternoon, {}", name),
+ "evening" => format!("Good evening, {}", name),
+ _ => format!("Good day, {}", name),
+ }
+ }
+}
+
+fn main() {
+ let greeting = Greeting;
+ println!("{}", greeting.get_greeting("foo"));
+ println!("{}", greeting.get_farewell("bar"));
+ println!("{}", greeting.get_personalized_greeting("baz", "morning"));
+}
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/sample.dtsi b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/sample.dtsi
new file mode 100644
index 0000000..6aa4ecd
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/sample.dtsi
@@ -0,0 +1,25 @@
+/dts-v1/;
+
+/ {
+ model = "Example Board";
+ compatible = "example,board";
+ cpus {
+ cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a9";
+ reg = <0>;
+ };
+ };
+
+ memory {
+ device_type = "memory";
+ reg = <0x80000000 0x20000000>;
+ };
+
+ uart0: uart@101f1000 {
+ compatible = "ns16550a";
+ reg = <0x101f1000 0x1000>;
+ interrupts = <5>;
+ clock-frequency = <24000000>;
+ };
+};
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
index 1c2e995..2266772 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2010, Stefan Lay <stefan.lay@sap.com>
- * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com> and others
+ * Copyright (C) 2010, 2025 Christian Halstrick <christian.halstrick@sap.com> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -665,11 +665,13 @@ public void testAddRemovedFile() throws Exception {
FileUtils.delete(file);
// is supposed to do nothing
- dc = git.add().addFilepattern("a.txt").call();
+ dc = git.add().addFilepattern("a.txt").setAll(false).call();
assertEquals(oid, dc.getEntry(0).getObjectId());
assertEquals(
"[a.txt, mode:100644, content:content]",
indexState(CONTENT));
+ git.add().addFilepattern("a.txt").call();
+ assertEquals("", indexState(CONTENT));
}
}
@@ -690,11 +692,13 @@ public void testAddRemovedCommittedFile() throws Exception {
FileUtils.delete(file);
// is supposed to do nothing
- dc = git.add().addFilepattern("a.txt").call();
+ dc = git.add().addFilepattern("a.txt").setAll(false).call();
assertEquals(oid, dc.getEntry(0).getObjectId());
assertEquals(
"[a.txt, mode:100644, content:content]",
indexState(CONTENT));
+ git.add().addFilepattern("a.txt").call();
+ assertEquals("", indexState(CONTENT));
}
}
@@ -964,7 +968,7 @@ public void testAddWithoutParameterUpdate() throws Exception {
// file sub/b.txt is deleted
FileUtils.delete(file2);
- git.add().addFilepattern("sub").call();
+ git.add().addFilepattern("sub").setAll(false).call();
// change in sub/a.txt is staged
// deletion of sub/b.txt is not staged
// sub/c.txt is staged
@@ -973,6 +977,12 @@ public void testAddWithoutParameterUpdate() throws Exception {
"[sub/b.txt, mode:100644, content:content b]" +
"[sub/c.txt, mode:100644, content:content c]",
indexState(CONTENT));
+ git.add().addFilepattern("sub").call();
+ // deletion of sub/b.txt is staged
+ assertEquals(
+ "[sub/a.txt, mode:100644, content:modified content]"
+ + "[sub/c.txt, mode:100644, content:content c]",
+ indexState(CONTENT));
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ArchiveCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ArchiveCommandTest.java
index 3a4ea8e..9c2b16a 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ArchiveCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ArchiveCommandTest.java
@@ -267,7 +267,10 @@ private void archiveHeadAllFilesWithCompression(String fmt) throws Exception {
archive(git, archive, fmt, Map.of("compression-level", 9));
int sizeCompression9 = getNumBytes(archive);
- assertTrue(sizeCompression1 > sizeCompression9);
+ assertTrue(
+ "Expected sizeCompression1 = " + sizeCompression1
+ + " > sizeCompression9 = " + sizeCompression9,
+ sizeCompression1 > sizeCompression9);
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CherryPickCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CherryPickCommandTest.java
index be3b33a..3f5c5da 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CherryPickCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CherryPickCommandTest.java
@@ -34,6 +34,7 @@
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.lib.ReflogReader;
import org.eclipse.jgit.lib.RepositoryState;
import org.eclipse.jgit.merge.ContentMergeStrategy;
@@ -529,10 +530,11 @@ private void doCherryPickAndCheckResult(final Git git,
assertEquals(RepositoryState.SAFE, db.getRepositoryState());
if (reason == null) {
- ReflogReader reader = db.getReflogReader(Constants.HEAD);
+ RefDatabase refDb = db.getRefDatabase();
+ ReflogReader reader = refDb.getReflogReader(Constants.HEAD);
assertTrue(reader.getLastEntry().getComment()
.startsWith("cherry-pick: "));
- reader = db.getReflogReader(db.getBranch());
+ reader = refDb.getReflogReader(db.getFullBranch());
assertTrue(reader.getLastEntry().getComment()
.startsWith("cherry-pick: "));
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java
index 63ab809..661878f 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java
@@ -182,7 +182,8 @@ private static boolean isLocalHead(Ref ref) {
private static boolean hasRefLog(Repository repo, Ref ref) {
try {
- return repo.getReflogReader(ref.getName()).getLastEntry() != null;
+ return repo.getRefDatabase().getReflogReader(ref)
+ .getLastEntry() != null;
} catch (IOException ioe) {
throw new IllegalStateException(ioe);
}
@@ -647,7 +648,8 @@ public void testCloneRepositoryWithSubmodules() throws Exception {
new File(git.getRepository().getWorkTree(), walk.getPath()),
subRepo.getWorkTree());
assertEquals(new File(new File(git.getRepository().getDirectory(),
- "modules"), walk.getPath()), subRepo.getDirectory());
+ "modules"), walk.getPath()).getCanonicalPath(),
+ subRepo.getDirectory().getCanonicalPath());
}
File directory = createTempDirectory("testCloneRepositoryWithSubmodules");
@@ -681,8 +683,8 @@ public void testCloneRepositoryWithSubmodules() throws Exception {
walk.getPath()), clonedSub1.getWorkTree());
assertEquals(
new File(new File(git2.getRepository().getDirectory(),
- "modules"), walk.getPath()),
- clonedSub1.getDirectory());
+ "modules"), walk.getPath()).getCanonicalPath(),
+ clonedSub1.getDirectory().getCanonicalPath());
}
}
@@ -770,8 +772,8 @@ public void testCloneRepositoryWithNestedSubmodules() throws Exception {
walk.getPath()), clonedSub1.getWorkTree());
assertEquals(
new File(new File(git2.getRepository().getDirectory(),
- "modules"), walk.getPath()),
- clonedSub1.getDirectory());
+ "modules"), walk.getPath()).getCanonicalPath(),
+ clonedSub1.getDirectory().getCanonicalPath());
status = new SubmoduleStatusCommand(clonedSub1);
statuses = status.call();
}
@@ -795,7 +797,7 @@ public void testCloneWithAutoSetupRebase() throws Exception {
assertNull(git2.getRepository().getConfig().getEnum(
BranchRebaseMode.values(),
ConfigConstants.CONFIG_BRANCH_SECTION, "test",
- ConfigConstants.CONFIG_KEY_REBASE, null));
+ ConfigConstants.CONFIG_KEY_REBASE));
StoredConfig userConfig = SystemReader.getInstance()
.getUserConfig();
@@ -811,7 +813,6 @@ public void testCloneWithAutoSetupRebase() throws Exception {
addRepoToClose(git2.getRepository());
assertEquals(BranchRebaseMode.REBASE,
git2.getRepository().getConfig().getEnum(
- BranchRebaseMode.values(),
ConfigConstants.CONFIG_BRANCH_SECTION, "test",
ConfigConstants.CONFIG_KEY_REBASE,
BranchRebaseMode.NONE));
@@ -828,7 +829,6 @@ public void testCloneWithAutoSetupRebase() throws Exception {
addRepoToClose(git2.getRepository());
assertEquals(BranchRebaseMode.REBASE,
git2.getRepository().getConfig().getEnum(
- BranchRebaseMode.values(),
ConfigConstants.CONFIG_BRANCH_SECTION, "test",
ConfigConstants.CONFIG_KEY_REBASE,
BranchRebaseMode.NONE));
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTest.java
index 57e5d49..4e5f44e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTest.java
@@ -26,6 +26,7 @@
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.ReflogReader;
import org.eclipse.jgit.revwalk.RevCommit;
@@ -69,10 +70,11 @@ public void testSomeCommits() throws Exception {
l--;
}
assertEquals(l, -1);
- ReflogReader reader = db.getReflogReader(Constants.HEAD);
+ RefDatabase refDb = db.getRefDatabase();
+ ReflogReader reader = refDb.getReflogReader(Constants.HEAD);
assertTrue(
reader.getLastEntry().getComment().startsWith("commit:"));
- reader = db.getReflogReader(db.getBranch());
+ reader = refDb.getReflogReader(db.getFullBranch());
assertTrue(
reader.getLastEntry().getComment().startsWith("commit:"));
}
@@ -248,10 +250,11 @@ public void testCommitAmend() throws Exception {
c++;
}
assertEquals(1, c);
- ReflogReader reader = db.getReflogReader(Constants.HEAD);
+ RefDatabase refDb = db.getRefDatabase();
+ ReflogReader reader = refDb.getReflogReader(Constants.HEAD);
assertTrue(reader.getLastEntry().getComment()
.startsWith("commit (amend):"));
- reader = db.getReflogReader(db.getBranch());
+ reader = refDb.getReflogReader(db.getFullBranch());
assertTrue(reader.getLastEntry().getComment()
.startsWith("commit (amend):"));
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java
index 35de73e..21cfcc4 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java
@@ -19,14 +19,15 @@
import static org.junit.Assume.assumeTrue;
import java.io.File;
-import java.util.Date;
+import java.time.Instant;
+import java.time.ZoneOffset;
import java.util.List;
-import java.util.TimeZone;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.jgit.api.CherryPickResult.CherryPickStatus;
import org.eclipse.jgit.api.errors.CanceledException;
import org.eclipse.jgit.api.errors.EmptyCommitException;
+import org.eclipse.jgit.api.errors.UnsupportedSigningFormatException;
import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.dircache.DirCache;
@@ -34,19 +35,23 @@
import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.junit.time.TimeUtil;
-import org.eclipse.jgit.lib.CommitBuilder;
+import org.eclipse.jgit.lib.CommitConfig.CleanupMode;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
-import org.eclipse.jgit.lib.GpgSigner;
+import org.eclipse.jgit.lib.GpgConfig;
+import org.eclipse.jgit.lib.GpgConfig.GpgFormat;
+import org.eclipse.jgit.lib.GpgSignature;
+import org.eclipse.jgit.lib.ObjectBuilder;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.lib.ReflogEntry;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.Signer;
+import org.eclipse.jgit.lib.Signers;
import org.eclipse.jgit.lib.StoredConfig;
-import org.eclipse.jgit.lib.CommitConfig.CleanupMode;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.eclipse.jgit.submodule.SubmoduleWalk;
@@ -430,10 +435,12 @@ public void commitAfterSquashMerge() throws Exception {
assertEquals(1, squashedCommit.getParentCount());
assertNull(db.readSquashCommitMsg());
- assertEquals("commit: Squashed commit of the following:", db
- .getReflogReader(Constants.HEAD).getLastEntry().getComment());
- assertEquals("commit: Squashed commit of the following:", db
- .getReflogReader(db.getBranch()).getLastEntry().getComment());
+ assertEquals("commit: Squashed commit of the following:",
+ db.getRefDatabase().getReflogReader(Constants.HEAD)
+ .getLastEntry().getComment());
+ assertEquals("commit: Squashed commit of the following:",
+ db.getRefDatabase().getReflogReader(db.getFullBranch())
+ .getLastEntry().getComment());
}
}
@@ -450,12 +457,15 @@ public void testReflogs() throws Exception {
git.commit().setMessage("c3").setAll(true)
.setReflogComment("testRl").call();
- db.getReflogReader(Constants.HEAD).getReverseEntries();
+ db.getRefDatabase().getReflogReader(Constants.HEAD)
+ .getReverseEntries();
assertEquals("testRl;commit (initial): c1;", reflogComments(
- db.getReflogReader(Constants.HEAD).getReverseEntries()));
+ db.getRefDatabase().getReflogReader(Constants.HEAD)
+ .getReverseEntries()));
assertEquals("testRl;commit (initial): c1;", reflogComments(
- db.getReflogReader(db.getBranch()).getReverseEntries()));
+ db.getRefDatabase().getReflogReader(db.getFullBranch())
+ .getReverseEntries()));
}
}
@@ -481,11 +491,11 @@ public void commitAmendWithoutAuthorShouldSetOriginalAuthorAndAuthorTime()
writeTrashFile("file1", "file1");
git.add().addFilepattern("file1").call();
- final String authorName = "First Author";
- final String authorEmail = "author@example.org";
- final Date authorDate = new Date(1349621117000L);
+ String authorName = "First Author";
+ String authorEmail = "author@example.org";
+ Instant authorDate = Instant.ofEpochSecond(1349621117L);
PersonIdent firstAuthor = new PersonIdent(authorName, authorEmail,
- authorDate, TimeZone.getTimeZone("UTC"));
+ authorDate, ZoneOffset.UTC);
git.commit().setMessage("initial commit").setAuthor(firstAuthor).call();
RevCommit amended = git.commit().setAmend(true)
@@ -494,7 +504,8 @@ public void commitAmendWithoutAuthorShouldSetOriginalAuthorAndAuthorTime()
PersonIdent amendedAuthor = amended.getAuthorIdent();
assertEquals(authorName, amendedAuthor.getName());
assertEquals(authorEmail, amendedAuthor.getEmailAddress());
- assertEquals(authorDate.getTime(), amendedAuthor.getWhen().getTime());
+ assertEquals(authorDate.getEpochSecond(),
+ amendedAuthor.getWhenAsInstant().getEpochSecond());
}
}
@@ -839,21 +850,39 @@ public void callSignerWithProperSigningKey() throws Exception {
String[] signingKey = new String[1];
PersonIdent[] signingCommitters = new PersonIdent[1];
AtomicInteger callCount = new AtomicInteger();
- GpgSigner.setDefault(new GpgSigner() {
+ // Since GpgFormat defaults to OpenPGP just set a new signer for
+ // that.
+ Signers.set(GpgFormat.OPENPGP, new Signer() {
+
@Override
- public void sign(CommitBuilder commit, String gpgSigningKey,
- PersonIdent signingCommitter, CredentialsProvider credentialsProvider) {
- signingKey[0] = gpgSigningKey;
+ public void signObject(Repository repo, GpgConfig config,
+ ObjectBuilder builder, PersonIdent signingCommitter,
+ String signingKeySpec,
+ CredentialsProvider credentialsProvider)
+ throws CanceledException,
+ UnsupportedSigningFormatException {
+ signingKey[0] = signingKeySpec;
signingCommitters[0] = signingCommitter;
callCount.incrementAndGet();
}
@Override
- public boolean canLocateSigningKey(String gpgSigningKey,
- PersonIdent signingCommitter,
+ public GpgSignature sign(Repository repo, GpgConfig config,
+ byte[] data, PersonIdent signingCommitter,
+ String signingKeySpec,
+ CredentialsProvider credentialsProvider)
+ throws CanceledException,
+ UnsupportedSigningFormatException {
+ throw new CanceledException("Unexpected call");
+ }
+
+ @Override
+ public boolean canLocateSigningKey(Repository repo,
+ GpgConfig config, PersonIdent signingCommitter,
+ String signingKeySpec,
CredentialsProvider credentialsProvider)
throws CanceledException {
- return false;
+ throw new CanceledException("Unexpected call");
}
});
@@ -904,19 +933,37 @@ public void callSignerOnlyWhenSigning() throws Exception {
git.add().addFilepattern("file1").call();
AtomicInteger callCount = new AtomicInteger();
- GpgSigner.setDefault(new GpgSigner() {
+ // Since GpgFormat defaults to OpenPGP just set a new signer for
+ // that.
+ Signers.set(GpgFormat.OPENPGP, new Signer() {
+
@Override
- public void sign(CommitBuilder commit, String gpgSigningKey,
- PersonIdent signingCommitter, CredentialsProvider credentialsProvider) {
+ public void signObject(Repository repo, GpgConfig config,
+ ObjectBuilder builder, PersonIdent signingCommitter,
+ String signingKeySpec,
+ CredentialsProvider credentialsProvider)
+ throws CanceledException,
+ UnsupportedSigningFormatException {
callCount.incrementAndGet();
}
@Override
- public boolean canLocateSigningKey(String gpgSigningKey,
- PersonIdent signingCommitter,
+ public GpgSignature sign(Repository repo, GpgConfig config,
+ byte[] data, PersonIdent signingCommitter,
+ String signingKeySpec,
+ CredentialsProvider credentialsProvider)
+ throws CanceledException,
+ UnsupportedSigningFormatException {
+ throw new CanceledException("Unexpected call");
+ }
+
+ @Override
+ public boolean canLocateSigningKey(Repository repo,
+ GpgConfig config, PersonIdent signingCommitter,
+ String signingKeySpec,
CredentialsProvider credentialsProvider)
throws CanceledException {
- return false;
+ throw new CanceledException("Unexpected call");
}
});
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java
index ab87fa9..060e6d3 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java
@@ -12,6 +12,7 @@
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.eclipse.jgit.lib.Constants.OBJECT_ID_ABBREV_STRING_LENGTH;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -87,6 +88,9 @@ public void testDescribe() throws Exception {
assertEquals("alice-t1", describe(c2, "alice*"));
assertEquals("alice-t1", describe(c2, "a*", "b*", "c*"));
+ assertNotEquals("alice-t1", describeExcluding(c2, "alice*"));
+ assertNotEquals("alice-t1", describeCommand(c2).setMatch("*").setExclude("alice*").call());
+
assertEquals("bob-t2", describe(c3));
assertEquals("bob-t2-0-g44579eb", describe(c3, true, false));
assertEquals("alice-t1-1-g44579eb", describe(c3, "alice*"));
@@ -95,6 +99,15 @@ public void testDescribe() throws Exception {
assertEquals("bob-t2", describe(c3, "?ob*"));
assertEquals("bob-t2", describe(c3, "a*", "b*", "c*"));
+ assertNotEquals("alice-t1-1-g44579eb", describeExcluding(c3, "alice*"));
+ assertNotEquals("alice-t1-1-g44579eb", describeCommand(c3).setMatch("*").setExclude("alice*").call());
+ assertNotEquals("alice-t1-1-g44579eb", describeExcluding(c3, "a??c?-t*"));
+ assertNotEquals("alice-t1-1-g44579eb", describeCommand(c3).setMatch("bob*").setExclude("a??c?-t*").call());
+ assertNotEquals("bob-t2", describeExcluding(c3, "bob*"));
+ assertNotEquals("bob-t2", describeCommand(c3).setMatch("alice*").setExclude("bob*"));
+ assertNotEquals("bob-t2", describeExcluding(c3, "?ob*"));
+ assertNotEquals("bob-t2", describeCommand(c3).setMatch("a??c?-t*").setExclude("?ob*"));
+
// the value verified with git-describe(1)
assertEquals("bob-t2-1-g3e563c5", describe(c4));
assertEquals("bob-t2-1-g3e563c5", describe(c4, true, false));
@@ -518,6 +531,15 @@ private String describe(ObjectId c1, String... patterns) throws Exception {
.setMatch(patterns).call();
}
+ private String describeExcluding(ObjectId c1, String... patterns) throws Exception {
+ return git.describe().setTarget(c1).setTags(describeUseAllTags)
+ .setExclude(patterns).call();
+ }
+
+ private DescribeCommand describeCommand(ObjectId c1) throws Exception {
+ return git.describe().setTarget(c1).setTags(describeUseAllTags);
+ }
+
private static void assertNameStartsWith(ObjectId c4, String prefix) {
assertTrue(c4.name(), c4.name().startsWith(prefix));
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java
index b937b1f..4c971ff 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java
@@ -559,7 +559,7 @@ private void setupGitAndDoHardReset(AutoCRLF autoCRLF, EOL eol,
}
if (infoAttributesContent != null) {
- File f = new File(db.getDirectory(), Constants.INFO_ATTRIBUTES);
+ File f = new File(db.getCommonDirectory(), Constants.INFO_ATTRIBUTES);
write(f, infoAttributesContent);
}
config.save();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/FetchCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/FetchCommandTest.java
index 3ec454c..3731347 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/FetchCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/FetchCommandTest.java
@@ -92,8 +92,8 @@ public void testFetchHasRefLogForRemoteRef() throws Exception {
assertTrue(remoteRef.getName().startsWith(Constants.R_REMOTES));
assertEquals(defaultBranchSha1, remoteRef.getObjectId());
- assertNotNull(git.getRepository().getReflogReader(remoteRef.getName())
- .getLastEntry());
+ assertNotNull(git.getRepository().getRefDatabase()
+ .getReflogReader(remoteRef.getName()).getLastEntry());
}
@Test
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GarbageCollectCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GarbageCollectCommandTest.java
index f98db34..6090d5e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GarbageCollectCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GarbageCollectCommandTest.java
@@ -11,12 +11,11 @@
import static org.junit.Assert.assertTrue;
-import java.util.Date;
+import java.time.Instant;
import java.util.Properties;
import org.eclipse.jgit.junit.RepositoryTestCase;
-import org.eclipse.jgit.util.GitDateParser;
-import org.eclipse.jgit.util.SystemReader;
+import org.eclipse.jgit.util.GitTimeParser;
import org.junit.Before;
import org.junit.Test;
@@ -36,9 +35,8 @@ public void setUp() throws Exception {
@Test
public void testGConeCommit() throws Exception {
- Date expire = GitDateParser.parse("now", null, SystemReader
- .getInstance().getLocale());
- Properties res = git.gc().setExpire(expire).call();
+ Instant expireNow = GitTimeParser.parseInstant("now");
+ Properties res = git.gc().setExpire(expireNow).call();
assertTrue(res.size() == 8);
}
@@ -52,11 +50,8 @@ public void testGCmoreCommits() throws Exception {
writeTrashFile("b.txt", "a couple of words for gc to pack more 2");
writeTrashFile("c.txt", "a couple of words for gc to pack more 3");
git.commit().setAll(true).setMessage("commit3").call();
- Properties res = git
- .gc()
- .setExpire(
- GitDateParser.parse("now", null, SystemReader
- .getInstance().getLocale())).call();
+ Instant expireNow = GitTimeParser.parseInstant("now");
+ Properties res = git.gc().setExpire(expireNow).call();
assertTrue(res.size() == 8);
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GitConstructionTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GitConstructionTest.java
index 7693434..e847e72 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GitConstructionTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GitConstructionTest.java
@@ -14,6 +14,7 @@
import java.io.File;
import java.io.IOException;
+import java.time.Instant;
import org.eclipse.jgit.api.ListBranchCommand.ListMode;
import org.eclipse.jgit.api.errors.GitAPIException;
@@ -100,7 +101,7 @@ public void testClose() throws IOException, JGitInternalException,
GitAPIException {
File workTree = db.getWorkTree();
Git git = Git.open(workTree);
- git.gc().setExpire(null).call();
+ git.gc().setExpire((Instant) null).call();
git.checkout().setName(git.getRepository().resolve("HEAD^").getName())
.call();
try {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LinkedWorktreeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LinkedWorktreeTest.java
new file mode 100644
index 0000000..3b60e1b
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LinkedWorktreeTest.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2024, Broadcom and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.api;
+
+import static org.eclipse.jgit.lib.Constants.HEAD;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.eclipse.jgit.internal.storage.file.FileRepository;
+import org.eclipse.jgit.junit.JGitTestUtil;
+import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ReflogEntry;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.FS.ExecutionResult;
+import org.eclipse.jgit.util.RawParseUtils;
+import org.eclipse.jgit.util.TemporaryBuffer;
+import org.junit.Test;
+
+public class LinkedWorktreeTest extends RepositoryTestCase {
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ try (Git git = new Git(db)) {
+ git.commit().setMessage("Initial commit").call();
+ }
+ }
+
+ @Test
+ public void testWeCanReadFromLinkedWorktreeFromBare() throws Exception {
+ FS fs = db.getFS();
+ File directory = trash.getParentFile();
+ String dbDirName = db.getWorkTree().getName();
+ cloneBare(fs, directory, dbDirName, "bare");
+ File bareDirectory = new File(directory, "bare");
+ worktreeAddExisting(fs, bareDirectory, "master");
+
+ File worktreesDir = new File(bareDirectory, "worktrees");
+ File masterWorktreesDir = new File(worktreesDir, "master");
+
+ FileRepository repository = new FileRepository(masterWorktreesDir);
+ try (Git git = new Git(repository)) {
+ ObjectId objectId = repository.resolve(HEAD);
+ assertNotNull(objectId);
+
+ Iterator<RevCommit> log = git.log().all().call().iterator();
+ assertTrue(log.hasNext());
+ assertTrue("Initial commit".equals(log.next().getShortMessage()));
+
+ // we have reflog entry
+ // depending on git version we either have one or
+ // two entries where extra is zeroid entry with
+ // same message or no message
+ Collection<ReflogEntry> reflog = git.reflog().call();
+ assertNotNull(reflog);
+ assertTrue(reflog.size() > 0);
+ ReflogEntry[] reflogs = reflog.toArray(new ReflogEntry[0]);
+ assertEquals(reflogs[reflogs.length - 1].getComment(),
+ "reset: moving to HEAD");
+
+ // index works with file changes
+ File masterDir = new File(directory, "master");
+ File testFile = new File(masterDir, "test");
+
+ Status status = git.status().call();
+ assertTrue(status.getUncommittedChanges().size() == 0);
+ assertTrue(status.getUntracked().size() == 0);
+
+ JGitTestUtil.write(testFile, "test");
+ status = git.status().call();
+ assertTrue(status.getUncommittedChanges().size() == 0);
+ assertTrue(status.getUntracked().size() == 1);
+
+ git.add().addFilepattern("test").call();
+ status = git.status().call();
+ assertTrue(status.getUncommittedChanges().size() == 1);
+ assertTrue(status.getUntracked().size() == 0);
+ }
+ }
+
+ @Test
+ public void testWeCanReadFromLinkedWorktreeFromNonBare() throws Exception {
+ FS fs = db.getFS();
+ worktreeAddNew(fs, db.getWorkTree(), "wt");
+
+ File worktreesDir = new File(db.getDirectory(), "worktrees");
+ File masterWorktreesDir = new File(worktreesDir, "wt");
+
+ FileRepository repository = new FileRepository(masterWorktreesDir);
+ try (Git git = new Git(repository)) {
+ ObjectId objectId = repository.resolve(HEAD);
+ assertNotNull(objectId);
+
+ Iterator<RevCommit> log = git.log().all().call().iterator();
+ assertTrue(log.hasNext());
+ assertTrue("Initial commit".equals(log.next().getShortMessage()));
+
+ // we have reflog entry
+ Collection<ReflogEntry> reflog = git.reflog().call();
+ assertNotNull(reflog);
+ assertTrue(reflog.size() > 0);
+ ReflogEntry[] reflogs = reflog.toArray(new ReflogEntry[0]);
+ assertEquals(reflogs[reflogs.length - 1].getComment(),
+ "reset: moving to HEAD");
+
+ // index works with file changes
+ File directory = trash.getParentFile();
+ File wtDir = new File(directory, "wt");
+ File testFile = new File(wtDir, "test");
+
+ Status status = git.status().call();
+ assertTrue(status.getUncommittedChanges().size() == 0);
+ assertTrue(status.getUntracked().size() == 0);
+
+ JGitTestUtil.write(testFile, "test");
+ status = git.status().call();
+ assertTrue(status.getUncommittedChanges().size() == 0);
+ assertTrue(status.getUntracked().size() == 1);
+
+ git.add().addFilepattern("test").call();
+ status = git.status().call();
+ assertTrue(status.getUncommittedChanges().size() == 1);
+ assertTrue(status.getUntracked().size() == 0);
+ }
+
+ }
+
+ private static void cloneBare(FS fs, File directory, String from, String to) throws IOException, InterruptedException {
+ ProcessBuilder builder = fs.runInShell("git",
+ new String[] { "clone", "--bare", from, to });
+ builder.directory(directory);
+ builder.environment().put("HOME", fs.userHome().getAbsolutePath());
+ StringBuilder input = new StringBuilder();
+ ExecutionResult result = fs.execute(builder, new ByteArrayInputStream(
+ input.toString().getBytes(StandardCharsets.UTF_8)));
+ String stdOut = toString(result.getStdout());
+ String errorOut = toString(result.getStderr());
+ assertNotNull(stdOut);
+ assertNotNull(errorOut);
+ }
+
+ private static void worktreeAddExisting(FS fs, File directory, String name) throws IOException, InterruptedException {
+ ProcessBuilder builder = fs.runInShell("git",
+ new String[] { "worktree", "add", "../" + name, name });
+ builder.directory(directory);
+ builder.environment().put("HOME", fs.userHome().getAbsolutePath());
+ StringBuilder input = new StringBuilder();
+ ExecutionResult result = fs.execute(builder, new ByteArrayInputStream(
+ input.toString().getBytes(StandardCharsets.UTF_8)));
+ String stdOut = toString(result.getStdout());
+ String errorOut = toString(result.getStderr());
+ assertNotNull(stdOut);
+ assertNotNull(errorOut);
+ }
+
+ private static void worktreeAddNew(FS fs, File directory, String name) throws IOException, InterruptedException {
+ ProcessBuilder builder = fs.runInShell("git",
+ new String[] { "worktree", "add", "-b", name, "../" + name, "master"});
+ builder.directory(directory);
+ builder.environment().put("HOME", fs.userHome().getAbsolutePath());
+ StringBuilder input = new StringBuilder();
+ ExecutionResult result = fs.execute(builder, new ByteArrayInputStream(
+ input.toString().getBytes(StandardCharsets.UTF_8)));
+ String stdOut = toString(result.getStdout());
+ String errorOut = toString(result.getStderr());
+ assertNotNull(stdOut);
+ assertNotNull(errorOut);
+ }
+
+ private static String toString(TemporaryBuffer b) throws IOException {
+ return RawParseUtils.decode(b.toByteArray());
+ }
+
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java
index 917b6c3..1ec5067 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java
@@ -21,6 +21,9 @@
import static org.junit.Assume.assumeTrue;
import java.io.File;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.util.Iterator;
import java.util.regex.Pattern;
@@ -33,6 +36,7 @@
import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryState;
import org.eclipse.jgit.lib.Sets;
@@ -45,6 +49,7 @@
import org.eclipse.jgit.util.FileUtils;
import org.eclipse.jgit.util.GitDateFormatter;
import org.eclipse.jgit.util.GitDateFormatter.Format;
+import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.theories.DataPoints;
@@ -76,12 +81,12 @@ public void testMergeInItself() throws Exception {
assertEquals(MergeResult.MergeStatus.ALREADY_UP_TO_DATE, result.getMergeStatus());
}
// no reflog entry written by merge
- assertEquals("commit (initial): initial commit",
- db
+ RefDatabase refDb = db.getRefDatabase();
+ assertEquals("commit (initial): initial commit", refDb
.getReflogReader(Constants.HEAD).getLastEntry().getComment());
- assertEquals("commit (initial): initial commit",
- db
- .getReflogReader(db.getBranch()).getLastEntry().getComment());
+ assertEquals("commit (initial): initial commit", refDb
+ .getReflogReader(db.getFullBranch()).getLastEntry()
+ .getComment());
}
@Test
@@ -96,10 +101,11 @@ public void testAlreadyUpToDate() throws Exception {
assertEquals(second, result.getNewHead());
}
// no reflog entry written by merge
- assertEquals("commit: second commit", db
+ assertEquals("commit: second commit", db.getRefDatabase()
.getReflogReader(Constants.HEAD).getLastEntry().getComment());
- assertEquals("commit: second commit", db
- .getReflogReader(db.getBranch()).getLastEntry().getComment());
+ assertEquals("commit: second commit", db.getRefDatabase()
+ .getReflogReader(db.getFullBranch()).getLastEntry()
+ .getComment());
}
@Test
@@ -117,10 +123,13 @@ public void testFastForward() throws Exception {
assertEquals(MergeResult.MergeStatus.FAST_FORWARD, result.getMergeStatus());
assertEquals(second, result.getNewHead());
}
+ RefDatabase refDb = db.getRefDatabase();
assertEquals("merge refs/heads/master: Fast-forward",
- db.getReflogReader(Constants.HEAD).getLastEntry().getComment());
+ refDb.getReflogReader(Constants.HEAD)
+ .getLastEntry().getComment());
assertEquals("merge refs/heads/master: Fast-forward",
- db.getReflogReader(db.getBranch()).getLastEntry().getComment());
+ refDb.getReflogReader(db.getFullBranch())
+ .getLastEntry().getComment());
}
@Test
@@ -140,10 +149,12 @@ public void testFastForwardNoCommit() throws Exception {
result.getMergeStatus());
assertEquals(second, result.getNewHead());
}
- assertEquals("merge refs/heads/master: Fast-forward", db
+ RefDatabase refDb = db.getRefDatabase();
+ assertEquals("merge refs/heads/master: Fast-forward", refDb
.getReflogReader(Constants.HEAD).getLastEntry().getComment());
- assertEquals("merge refs/heads/master: Fast-forward", db
- .getReflogReader(db.getBranch()).getLastEntry().getComment());
+ assertEquals("merge refs/heads/master: Fast-forward", refDb
+ .getReflogReader(db.getFullBranch()).getLastEntry()
+ .getComment());
}
@Test
@@ -171,10 +182,12 @@ public void testFastForwardWithFiles() throws Exception {
assertEquals(MergeResult.MergeStatus.FAST_FORWARD, result.getMergeStatus());
assertEquals(second, result.getNewHead());
}
- assertEquals("merge refs/heads/master: Fast-forward",
- db.getReflogReader(Constants.HEAD).getLastEntry().getComment());
- assertEquals("merge refs/heads/master: Fast-forward",
- db.getReflogReader(db.getBranch()).getLastEntry().getComment());
+ RefDatabase refDb = db.getRefDatabase();
+ assertEquals("merge refs/heads/master: Fast-forward", refDb
+ .getReflogReader(Constants.HEAD).getLastEntry().getComment());
+ assertEquals("merge refs/heads/master: Fast-forward", refDb
+ .getReflogReader(db.getFullBranch()).getLastEntry()
+ .getComment());
}
@Test
@@ -229,14 +242,17 @@ public void testMergeSuccessAllStrategies(MergeStrategy mergeStrategy)
.include(db.exactRef(R_HEADS + MASTER)).call();
assertEquals(MergeStatus.MERGED, result.getMergeStatus());
}
+ RefDatabase refDb = db.getRefDatabase();
assertEquals(
"merge refs/heads/master: Merge made by "
+ mergeStrategy.getName() + ".",
- db.getReflogReader(Constants.HEAD).getLastEntry().getComment());
+ refDb.getReflogReader(Constants.HEAD).getLastEntry()
+ .getComment());
assertEquals(
"merge refs/heads/master: Merge made by "
+ mergeStrategy.getName() + ".",
- db.getReflogReader(db.getBranch()).getLastEntry().getComment());
+ refDb.getReflogReader(db.getFullBranch()).getLastEntry()
+ .getComment());
}
@Theory
@@ -662,14 +678,17 @@ public void testMultipleCreationsSameContent() throws Exception {
.setStrategy(MergeStrategy.RESOLVE).call();
assertEquals(MergeStatus.MERGED, result.getMergeStatus());
assertEquals("1\nb(1)\n3\n", read(new File(db.getWorkTree(), "b")));
- assertEquals("merge " + secondCommit.getId().getName()
- + ": Merge made by resolve.", db
- .getReflogReader(Constants.HEAD)
- .getLastEntry().getComment());
- assertEquals("merge " + secondCommit.getId().getName()
- + ": Merge made by resolve.", db
- .getReflogReader(db.getBranch())
- .getLastEntry().getComment());
+ RefDatabase refDb = db.getRefDatabase();
+ assertEquals(
+ "merge " + secondCommit.getId().getName()
+ + ": Merge made by resolve.",
+ refDb.getReflogReader(Constants.HEAD).getLastEntry()
+ .getComment());
+ assertEquals(
+ "merge " + secondCommit.getId().getName()
+ + ": Merge made by resolve.",
+ refDb.getReflogReader(db.getFullBranch()).getLastEntry()
+ .getComment());
}
}
@@ -2086,6 +2105,94 @@ public void testMergeConflictWithMessageAndCommentCharAuto()
}
}
+ @Test
+ public void testMergeCaseInsensitiveRename() throws Exception {
+ Assume.assumeTrue(
+ "Test makes only sense on a case-insensitive file system",
+ db.isWorkTreeCaseInsensitive());
+ try (Git git = new Git(db)) {
+ writeTrashFile("a", "aaa");
+ git.add().addFilepattern("a").call();
+ RevCommit initialCommit = git.commit().setMessage("initial").call();
+ // "Rename" "a" to "A"
+ git.rm().addFilepattern("a").call();
+ writeTrashFile("A", "aaa");
+ git.add().addFilepattern("A").call();
+ RevCommit master = git.commit().setMessage("rename to A").call();
+
+ createBranch(initialCommit, "refs/heads/side");
+ checkoutBranch("refs/heads/side");
+
+ writeTrashFile("b", "bbb");
+ git.add().addFilepattern("b").call();
+ git.commit().setMessage("side").call();
+
+ // Merge master into side
+ MergeResult result = git.merge().include(master)
+ .setStrategy(MergeStrategy.RECURSIVE).call();
+ assertEquals(MergeStatus.MERGED, result.getMergeStatus());
+ assertTrue(new File(db.getWorkTree(), "A").isFile());
+ // Double check
+ boolean found = true;
+ try (DirectoryStream<Path> dir = Files
+ .newDirectoryStream(db.getWorkTree().toPath())) {
+ for (Path p : dir) {
+ found = "A".equals(p.getFileName().toString());
+ if (found) {
+ break;
+ }
+ }
+ }
+ assertTrue(found);
+ }
+ }
+
+ @Test
+ public void testMergeCaseInsensitiveRenameConflict() throws Exception {
+ Assume.assumeTrue(
+ "Test makes only sense on a case-insensitive file system",
+ db.isWorkTreeCaseInsensitive());
+ try (Git git = new Git(db)) {
+ writeTrashFile("a", "aaa");
+ git.add().addFilepattern("a").call();
+ RevCommit initialCommit = git.commit().setMessage("initial").call();
+ // "Rename" "a" to "A" and change it
+ git.rm().addFilepattern("a").call();
+ writeTrashFile("A", "yyy");
+ git.add().addFilepattern("A").call();
+ RevCommit master = git.commit().setMessage("rename to A").call();
+
+ createBranch(initialCommit, "refs/heads/side");
+ checkoutBranch("refs/heads/side");
+
+ writeTrashFile("a", "xxx");
+ git.add().addFilepattern("a").call();
+ git.commit().setMessage("side").call();
+
+ // Merge master into side
+ MergeResult result = git.merge().include(master)
+ .setStrategy(MergeStrategy.RECURSIVE).call();
+ assertEquals(MergeStatus.CONFLICTING, result.getMergeStatus());
+ File a = new File(db.getWorkTree(), "A");
+ assertTrue(a.isFile());
+ // Double check
+ boolean found = true;
+ try (DirectoryStream<Path> dir = Files
+ .newDirectoryStream(db.getWorkTree().toPath())) {
+ for (Path p : dir) {
+ found = "A".equals(p.getFileName().toString());
+ if (found) {
+ break;
+ }
+ }
+ }
+ assertTrue(found);
+ assertEquals(1, result.getConflicts().size());
+ assertTrue(result.getConflicts().containsKey("a"));
+ checkFile(a, "yyy");
+ }
+ }
+
private static void setExecutable(Git git, String path, boolean executable) {
FS.DETECTED.setExecute(
new File(git.getRepository().getWorkTree(), path), executable);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java
index 12300b3..6d5e45c 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java
@@ -21,6 +21,7 @@
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.util.Map;
import java.util.concurrent.Callable;
import org.eclipse.jgit.api.CreateBranchCommand.SetupUpstreamMode;
@@ -29,6 +30,7 @@
import org.eclipse.jgit.junit.JGitTestUtil;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.IndexDiff.StageState;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
@@ -117,6 +119,7 @@ public void testPullMerge() throws Exception {
+ db.getWorkTree().getAbsolutePath();
assertEquals(message, mergeCommit.getShortMessage());
}
+ assertTrue(target.status().call().isClean());
}
@Test
@@ -153,6 +156,10 @@ public void testPullConflict() throws Exception {
assertFileContentsEqual(targetFile, result);
assertEquals(RepositoryState.MERGING, target.getRepository()
.getRepositoryState());
+ Status status = target.status().call();
+ Map<String, StageState> conflicting = status.getConflictingStageState();
+ assertEquals(1, conflicting.size());
+ assertEquals(StageState.BOTH_MODIFIED, conflicting.get("SomeFile.txt"));
}
@Test
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java
index 70e990d..d1696d6 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java
@@ -22,6 +22,7 @@
import java.io.PrintStream;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
+import java.time.Instant;
import java.util.Properties;
import org.eclipse.jgit.api.errors.DetachedHeadException;
@@ -1146,7 +1147,7 @@ public void testPushAfterGC() throws Exception {
RevCommit commit2 = git2.commit().setMessage("adding a").call();
// run a gc to ensure we have a bitmap index
- Properties res = git1.gc().setExpire(null).call();
+ Properties res = git1.gc().setExpire((Instant) null).call();
assertEquals(8, res.size());
// create another commit so we have something else to push
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java
index 02e3a2e..4c8cf06 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java
@@ -24,6 +24,8 @@
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
+import java.time.Instant;
+import java.time.ZoneOffset;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
@@ -55,6 +57,7 @@
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.RebaseTodoLine;
import org.eclipse.jgit.lib.RebaseTodoLine.Action;
+import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.ReflogEntry;
import org.eclipse.jgit.lib.RepositoryState;
@@ -131,11 +134,12 @@ public void testFastForwardWithNewFile() throws Exception {
checkFile(file2, "file2");
assertEquals(Status.FAST_FORWARD, res.getStatus());
- List<ReflogEntry> headLog = db.getReflogReader(Constants.HEAD)
+ RefDatabase refDb = db.getRefDatabase();
+ List<ReflogEntry> headLog = refDb.getReflogReader(Constants.HEAD)
.getReverseEntries();
- List<ReflogEntry> topicLog = db.getReflogReader("refs/heads/topic")
+ List<ReflogEntry> topicLog = refDb.getReflogReader("refs/heads/topic")
.getReverseEntries();
- List<ReflogEntry> masterLog = db.getReflogReader("refs/heads/master")
+ List<ReflogEntry> masterLog = refDb.getReflogReader("refs/heads/master")
.getReverseEntries();
assertEquals("rebase finished: returning to refs/heads/topic", headLog
.get(0).getComment());
@@ -177,11 +181,12 @@ public void testFastForwardWithMultipleCommits() throws Exception {
checkFile(file2, "file2 new content");
assertEquals(Status.FAST_FORWARD, res.getStatus());
- List<ReflogEntry> headLog = db.getReflogReader(Constants.HEAD)
+ RefDatabase refDb = db.getRefDatabase();
+ List<ReflogEntry> headLog = refDb.getReflogReader(Constants.HEAD)
.getReverseEntries();
- List<ReflogEntry> topicLog = db.getReflogReader("refs/heads/topic")
+ List<ReflogEntry> topicLog = refDb.getReflogReader("refs/heads/topic")
.getReverseEntries();
- List<ReflogEntry> masterLog = db.getReflogReader("refs/heads/master")
+ List<ReflogEntry> masterLog = refDb.getReflogReader("refs/heads/master")
.getReverseEntries();
assertEquals("rebase finished: returning to refs/heads/topic", headLog
.get(0).getComment());
@@ -445,13 +450,14 @@ public void testRebaseShouldIgnoreMergeCommits()
assertEquals(a, rw.next());
}
- List<ReflogEntry> headLog = db.getReflogReader(Constants.HEAD)
+ RefDatabase refDb = db.getRefDatabase();
+ List<ReflogEntry> headLog = refDb.getReflogReader(Constants.HEAD)
.getReverseEntries();
- List<ReflogEntry> sideLog = db.getReflogReader("refs/heads/side")
+ List<ReflogEntry> sideLog = refDb.getReflogReader("refs/heads/side")
.getReverseEntries();
- List<ReflogEntry> topicLog = db.getReflogReader("refs/heads/topic")
+ List<ReflogEntry> topicLog = refDb.getReflogReader("refs/heads/topic")
.getReverseEntries();
- List<ReflogEntry> masterLog = db.getReflogReader("refs/heads/master")
+ List<ReflogEntry> masterLog = refDb.getReflogReader("refs/heads/master")
.getReverseEntries();
assertEquals("rebase finished: returning to refs/heads/topic", headLog
.get(0).getComment());
@@ -766,9 +772,10 @@ public void testRebaseParentOntoHeadShouldBeUptoDate() throws Exception {
RebaseResult result = git.rebase().setUpstream(parent).call();
assertEquals(Status.UP_TO_DATE, result.getStatus());
- assertEquals(2, db.getReflogReader(Constants.HEAD).getReverseEntries()
- .size());
- assertEquals(2, db.getReflogReader("refs/heads/master")
+ RefDatabase refDb = db.getRefDatabase();
+ assertEquals(2, refDb.getReflogReader(Constants.HEAD)
+ .getReverseEntries().size());
+ assertEquals(2, refDb.getReflogReader("refs/heads/master")
.getReverseEntries().size());
}
@@ -784,9 +791,10 @@ public void testUpToDate() throws Exception {
RebaseResult res = git.rebase().setUpstream(first).call();
assertEquals(Status.UP_TO_DATE, res.getStatus());
- assertEquals(1, db.getReflogReader(Constants.HEAD).getReverseEntries()
- .size());
- assertEquals(1, db.getReflogReader("refs/heads/master")
+ RefDatabase refDb = db.getRefDatabase();
+ assertEquals(1, refDb.getReflogReader(Constants.HEAD)
+ .getReverseEntries().size());
+ assertEquals(1, refDb.getReflogReader("refs/heads/master")
.getReverseEntries().size());
}
@@ -844,11 +852,12 @@ public void testConflictFreeWithSingleFile() throws Exception {
db.resolve(Constants.HEAD)).getParent(0));
}
assertEquals(origHead, db.readOrigHead());
- List<ReflogEntry> headLog = db.getReflogReader(Constants.HEAD)
+ RefDatabase refDb = db.getRefDatabase();
+ List<ReflogEntry> headLog = refDb.getReflogReader(Constants.HEAD)
.getReverseEntries();
- List<ReflogEntry> topicLog = db.getReflogReader("refs/heads/topic")
+ List<ReflogEntry> topicLog = refDb.getReflogReader("refs/heads/topic")
.getReverseEntries();
- List<ReflogEntry> masterLog = db.getReflogReader("refs/heads/master")
+ List<ReflogEntry> masterLog = refDb.getReflogReader("refs/heads/master")
.getReverseEntries();
assertEquals(2, masterLog.size());
assertEquals(3, topicLog.size());
@@ -896,8 +905,8 @@ public void testDetachedHead() throws Exception {
db.resolve(Constants.HEAD)).getParent(0));
}
- List<ReflogEntry> headLog = db.getReflogReader(Constants.HEAD)
- .getReverseEntries();
+ List<ReflogEntry> headLog = db.getRefDatabase()
+ .getReflogReader(Constants.HEAD).getReverseEntries();
assertEquals(8, headLog.size());
assertEquals("rebase: change file1 in topic", headLog.get(0)
.getComment());
@@ -1603,7 +1612,7 @@ public void testStopOnConflictFileCreationAndDeletion() throws Exception {
public void testAuthorScriptConverter() throws Exception {
// -1 h timezone offset
PersonIdent ident = new PersonIdent("Author name", "a.mail@some.com",
- 123456789123L, -60);
+ Instant.ofEpochMilli(123456789123L), ZoneOffset.ofHours(-1));
String convertedAuthor = git.rebase().toAuthorScript(ident);
String[] lines = convertedAuthor.split("\n");
assertEquals("GIT_AUTHOR_NAME='Author name'", lines[0]);
@@ -1615,12 +1624,14 @@ public void testAuthorScriptConverter() throws Exception {
assertEquals(ident.getName(), parsedIdent.getName());
assertEquals(ident.getEmailAddress(), parsedIdent.getEmailAddress());
// this is rounded to the last second
- assertEquals(123456789000L, parsedIdent.getWhen().getTime());
- assertEquals(ident.getTimeZoneOffset(), parsedIdent.getTimeZoneOffset());
+ assertEquals(123456789000L,
+ parsedIdent.getWhenAsInstant().toEpochMilli());
+ assertEquals(ident.getZoneId(), parsedIdent.getZoneId());
// + 9.5h timezone offset
ident = new PersonIdent("Author name", "a.mail@some.com",
- 123456789123L, +570);
+ Instant.ofEpochMilli(123456789123L),
+ ZoneOffset.ofHoursMinutes(9, 30));
convertedAuthor = git.rebase().toAuthorScript(ident);
lines = convertedAuthor.split("\n");
assertEquals("GIT_AUTHOR_NAME='Author name'", lines[0]);
@@ -1631,8 +1642,9 @@ public void testAuthorScriptConverter() throws Exception {
convertedAuthor.getBytes(UTF_8));
assertEquals(ident.getName(), parsedIdent.getName());
assertEquals(ident.getEmailAddress(), parsedIdent.getEmailAddress());
- assertEquals(123456789000L, parsedIdent.getWhen().getTime());
- assertEquals(ident.getTimeZoneOffset(), parsedIdent.getTimeZoneOffset());
+ assertEquals(123456789000L,
+ parsedIdent.getWhenAsInstant().toEpochMilli());
+ assertEquals(ident.getZoneId(), parsedIdent.getZoneId());
}
@Test
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RenameBranchCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RenameBranchCommandTest.java
index 534ebd9..add5886 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RenameBranchCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RenameBranchCommandTest.java
@@ -118,23 +118,21 @@ public void renameBranchSingleConfigValue() throws Exception {
String branch = "b1";
assertEquals(BranchRebaseMode.REBASE,
- config.getEnum(BranchRebaseMode.values(),
- ConfigConstants.CONFIG_BRANCH_SECTION, Constants.MASTER,
- ConfigConstants.CONFIG_KEY_REBASE,
+ config.getEnum(ConfigConstants.CONFIG_BRANCH_SECTION,
+ Constants.MASTER, ConfigConstants.CONFIG_KEY_REBASE,
BranchRebaseMode.NONE));
assertNull(config.getEnum(BranchRebaseMode.values(),
ConfigConstants.CONFIG_BRANCH_SECTION, branch,
- ConfigConstants.CONFIG_KEY_REBASE, null));
+ ConfigConstants.CONFIG_KEY_REBASE));
assertNotNull(git.branchRename().setNewName(branch).call());
config = git.getRepository().getConfig();
assertNull(config.getEnum(BranchRebaseMode.values(),
ConfigConstants.CONFIG_BRANCH_SECTION, Constants.MASTER,
- ConfigConstants.CONFIG_KEY_REBASE, null));
+ ConfigConstants.CONFIG_KEY_REBASE));
assertEquals(BranchRebaseMode.REBASE,
- config.getEnum(BranchRebaseMode.values(),
- ConfigConstants.CONFIG_BRANCH_SECTION, branch,
+ config.getEnum(ConfigConstants.CONFIG_BRANCH_SECTION, branch,
ConfigConstants.CONFIG_KEY_REBASE,
BranchRebaseMode.NONE));
}
@@ -170,13 +168,12 @@ public void renameBranchMultipleConfigValues() throws Exception {
String branch = "b1";
assertEquals(BranchRebaseMode.REBASE,
- config.getEnum(BranchRebaseMode.values(),
- ConfigConstants.CONFIG_BRANCH_SECTION, Constants.MASTER,
- ConfigConstants.CONFIG_KEY_REBASE,
+ config.getEnum(ConfigConstants.CONFIG_BRANCH_SECTION,
+ Constants.MASTER, ConfigConstants.CONFIG_KEY_REBASE,
BranchRebaseMode.NONE));
assertNull(config.getEnum(BranchRebaseMode.values(),
ConfigConstants.CONFIG_BRANCH_SECTION, branch,
- ConfigConstants.CONFIG_KEY_REBASE, null));
+ ConfigConstants.CONFIG_KEY_REBASE));
assertTrue(config.getBoolean(ConfigConstants.CONFIG_BRANCH_SECTION,
Constants.MASTER, ConfigConstants.CONFIG_KEY_MERGE, true));
assertFalse(config.getBoolean(ConfigConstants.CONFIG_BRANCH_SECTION,
@@ -187,10 +184,9 @@ public void renameBranchMultipleConfigValues() throws Exception {
config = git.getRepository().getConfig();
assertNull(config.getEnum(BranchRebaseMode.values(),
ConfigConstants.CONFIG_BRANCH_SECTION, Constants.MASTER,
- ConfigConstants.CONFIG_KEY_REBASE, null));
+ ConfigConstants.CONFIG_KEY_REBASE));
assertEquals(BranchRebaseMode.REBASE,
- config.getEnum(BranchRebaseMode.values(),
- ConfigConstants.CONFIG_BRANCH_SECTION, branch,
+ config.getEnum(ConfigConstants.CONFIG_BRANCH_SECTION, branch,
ConfigConstants.CONFIG_KEY_REBASE,
BranchRebaseMode.NONE));
assertFalse(config.getBoolean(ConfigConstants.CONFIG_BRANCH_SECTION,
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java
index 8a479a0..99873e1 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java
@@ -36,11 +36,13 @@
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.util.FileUtils;
import org.junit.Assert;
+import org.junit.Assume;
import org.junit.Test;
public class ResetCommandTest extends RepositoryTestCase {
@@ -554,46 +556,73 @@ public void testHardResetOnUnbornBranch() throws Exception {
assertNull(db.resolve(Constants.HEAD));
}
+ @Test
+ public void testHardResetFileMode() throws Exception {
+ Assume.assumeTrue("Test must be able to set executable bit",
+ db.getFS().supportsExecute());
+ git = new Git(db);
+ File a = writeTrashFile("a.txt", "aaa");
+ File b = writeTrashFile("b.txt", "bbb");
+ db.getFS().setExecute(b, true);
+ assertFalse(db.getFS().canExecute(a));
+ assertTrue(db.getFS().canExecute(b));
+ git.add().addFilepattern("a.txt").addFilepattern("b.txt").call();
+ RevCommit commit = git.commit().setMessage("files created").call();
+ db.getFS().setExecute(a, true);
+ db.getFS().setExecute(b, false);
+ assertTrue(db.getFS().canExecute(a));
+ assertFalse(db.getFS().canExecute(b));
+ git.add().addFilepattern("a.txt").addFilepattern("b.txt").call();
+ git.commit().setMessage("change exe bits").call();
+ Ref ref = git.reset().setRef(commit.getName()).setMode(HARD).call();
+ assertSameAsHead(ref);
+ assertEquals(commit.getId(), ref.getObjectId());
+ assertFalse(db.getFS().canExecute(a));
+ assertTrue(db.getFS().canExecute(b));
+ }
+
private void assertReflog(ObjectId prevHead, ObjectId head)
throws IOException {
// Check the reflog for HEAD
- String actualHeadMessage = db.getReflogReader(Constants.HEAD)
+ RefDatabase refDb = db.getRefDatabase();
+ String actualHeadMessage = refDb.getReflogReader(Constants.HEAD)
.getLastEntry().getComment();
String expectedHeadMessage = head.getName() + ": updating HEAD";
assertEquals(expectedHeadMessage, actualHeadMessage);
- assertEquals(head.getName(), db.getReflogReader(Constants.HEAD)
+ assertEquals(head.getName(), refDb.getReflogReader(Constants.HEAD)
.getLastEntry().getNewId().getName());
- assertEquals(prevHead.getName(), db.getReflogReader(Constants.HEAD)
+ assertEquals(prevHead.getName(), refDb.getReflogReader(Constants.HEAD)
.getLastEntry().getOldId().getName());
// The reflog for master contains the same as the one for HEAD
- String actualMasterMessage = db.getReflogReader("refs/heads/master")
+ String actualMasterMessage = refDb.getReflogReader("refs/heads/master")
.getLastEntry().getComment();
String expectedMasterMessage = head.getName() + ": updating HEAD"; // yes!
assertEquals(expectedMasterMessage, actualMasterMessage);
- assertEquals(head.getName(), db.getReflogReader(Constants.HEAD)
+ assertEquals(head.getName(), refDb.getReflogReader(Constants.HEAD)
.getLastEntry().getNewId().getName());
- assertEquals(prevHead.getName(), db
- .getReflogReader("refs/heads/master").getLastEntry().getOldId()
- .getName());
+ assertEquals(prevHead.getName(),
+ refDb.getReflogReader("refs/heads/master").getLastEntry()
+ .getOldId().getName());
}
private void assertReflogDisabled(ObjectId head)
throws IOException {
+ RefDatabase refDb = db.getRefDatabase();
// Check the reflog for HEAD
- String actualHeadMessage = db.getReflogReader(Constants.HEAD)
+ String actualHeadMessage = refDb.getReflogReader(Constants.HEAD)
.getLastEntry().getComment();
String expectedHeadMessage = "commit: adding a.txt and dir/b.txt";
assertEquals(expectedHeadMessage, actualHeadMessage);
- assertEquals(head.getName(), db.getReflogReader(Constants.HEAD)
+ assertEquals(head.getName(), refDb.getReflogReader(Constants.HEAD)
.getLastEntry().getOldId().getName());
// The reflog for master contains the same as the one for HEAD
- String actualMasterMessage = db.getReflogReader("refs/heads/master")
+ String actualMasterMessage = refDb.getReflogReader("refs/heads/master")
.getLastEntry().getComment();
String expectedMasterMessage = "commit: adding a.txt and dir/b.txt";
assertEquals(expectedMasterMessage, actualMasterMessage);
- assertEquals(head.getName(), db.getReflogReader(Constants.HEAD)
+ assertEquals(head.getName(), refDb.getReflogReader(Constants.HEAD)
.getLastEntry().getOldId().getName());
}
/**
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RevertCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RevertCommandTest.java
index 4ebe994..89fdb32 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RevertCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RevertCommandTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011, Robin Rosenberg and others
+ * Copyright (C) 2011, 2024 Robin Rosenberg and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -29,6 +29,7 @@
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.lib.ReflogReader;
import org.eclipse.jgit.lib.RepositoryState;
import org.eclipse.jgit.merge.ResolveMerger.MergeFailureReason;
@@ -59,7 +60,9 @@ public void testRevert() throws IOException, JGitInternalException,
writeTrashFile("a",
"first line\nsecond line\nthird line\nfourth line\n");
git.add().addFilepattern("a").call();
- RevCommit fixingA = git.commit().setMessage("fixed a").call();
+ // Commit message with a non-empty second line on purpose
+ RevCommit fixingA = git.commit().setMessage("fixed a\nsecond line")
+ .call();
writeTrashFile("b", "first line\n");
git.add().addFilepattern("b").call();
@@ -78,16 +81,18 @@ public void testRevert() throws IOException, JGitInternalException,
+ "This reverts commit " + fixingA.getId().getName() + ".\n";
assertEquals(expectedMessage, revertCommit.getFullMessage());
assertEquals("fixed b", history.next().getFullMessage());
- assertEquals("fixed a", history.next().getFullMessage());
+ assertEquals("fixed a\nsecond line",
+ history.next().getFullMessage());
assertEquals("enlarged a", history.next().getFullMessage());
assertEquals("create b", history.next().getFullMessage());
assertEquals("create a", history.next().getFullMessage());
assertFalse(history.hasNext());
- ReflogReader reader = db.getReflogReader(Constants.HEAD);
+ RefDatabase refDb = db.getRefDatabase();
+ ReflogReader reader = refDb.getReflogReader(Constants.HEAD);
assertTrue(reader.getLastEntry().getComment()
.startsWith("revert: Revert \""));
- reader = db.getReflogReader(db.getBranch());
+ reader = refDb.getReflogReader(db.getFullBranch());
assertTrue(reader.getLastEntry().getComment()
.startsWith("revert: Revert \""));
}
@@ -167,10 +172,11 @@ public void testRevertMultiple() throws IOException, JGitInternalException,
assertEquals("add first", history.next().getFullMessage());
assertFalse(history.hasNext());
- ReflogReader reader = db.getReflogReader(Constants.HEAD);
+ RefDatabase refDb = db.getRefDatabase();
+ ReflogReader reader = refDb.getReflogReader(Constants.HEAD);
assertTrue(reader.getLastEntry().getComment()
.startsWith("revert: Revert \""));
- reader = db.getReflogReader(db.getBranch());
+ reader = refDb.getReflogReader(db.getFullBranch());
assertTrue(reader.getLastEntry().getComment()
.startsWith("revert: Revert \""));
}
@@ -220,10 +226,11 @@ public void testRevertMultipleWithFail() throws IOException,
assertEquals("add first", history.next().getFullMessage());
assertFalse(history.hasNext());
- ReflogReader reader = db.getReflogReader(Constants.HEAD);
+ RefDatabase refDb = db.getRefDatabase();
+ ReflogReader reader = refDb.getReflogReader(Constants.HEAD);
assertTrue(reader.getLastEntry().getComment()
.startsWith("revert: Revert \""));
- reader = db.getReflogReader(db.getBranch());
+ reader = refDb.getReflogReader(db.getFullBranch());
assertTrue(reader.getLastEntry().getComment()
.startsWith("revert: Revert \""));
}
@@ -428,12 +435,13 @@ private void doRevertAndCheckResult(final Git git,
assertEquals(RepositoryState.SAFE, db.getRepositoryState());
if (reason == null) {
- ReflogReader reader = db.getReflogReader(Constants.HEAD);
- assertTrue(reader.getLastEntry().getComment()
- .startsWith("revert: "));
- reader = db.getReflogReader(db.getBranch());
- assertTrue(reader.getLastEntry().getComment()
- .startsWith("revert: "));
+ RefDatabase refDb = db.getRefDatabase();
+ ReflogReader reader = refDb.getReflogReader(Constants.HEAD);
+ assertTrue(
+ reader.getLastEntry().getComment().startsWith("revert: "));
+ reader = refDb.getReflogReader(db.getFullBranch());
+ assertTrue(
+ reader.getLastEntry().getComment().startsWith("revert: "));
}
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/SecurityManagerMissingPermissionsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/SecurityManagerMissingPermissionsTest.java
deleted file mode 100644
index d0fbdbd..0000000
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/SecurityManagerMissingPermissionsTest.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (c) 2019 Alex Jitianu <alex_jitianu@sync.ro> and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-package org.eclipse.jgit.api;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.security.Policy;
-import java.util.Collections;
-
-import org.eclipse.jgit.junit.RepositoryTestCase;
-import org.eclipse.jgit.util.FileUtils;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-/**
- * Tests that using a SecurityManager does not result in errors logged.
- */
-public class SecurityManagerMissingPermissionsTest extends RepositoryTestCase {
-
- /**
- * Collects all logging sent to the logging system.
- */
- private final ByteArrayOutputStream errorOutput = new ByteArrayOutputStream();
-
- private SecurityManager originalSecurityManager;
-
- private PrintStream defaultErrorOutput;
-
- @Override
- @Before
- public void setUp() throws Exception {
- originalSecurityManager = System.getSecurityManager();
-
- // slf4j-simple logs to System.err, redirect it to enable asserting
- // logged errors
- defaultErrorOutput = System.err;
- System.setErr(new PrintStream(errorOutput));
-
- refreshPolicyAllPermission(Policy.getPolicy());
- System.setSecurityManager(new SecurityManager());
- super.setUp();
- }
-
- /**
- * If a SecurityManager is active a lot of {@link java.io.FilePermission}
- * errors are thrown and logged while initializing a repository.
- *
- * @throws Exception
- */
- @Test
- public void testCreateNewRepos_MissingPermissions() throws Exception {
- File wcTree = new File(getTemporaryDirectory(),
- "CreateNewRepositoryTest_testCreateNewRepos");
-
- File marker = new File(getTemporaryDirectory(), "marker");
- Files.write(marker.toPath(), Collections.singletonList("Can write"));
- assertTrue("Can write in test directory", marker.isFile());
- FileUtils.delete(marker);
- assertFalse("Can delete in test direcory", marker.exists());
-
- Git git = Git.init().setBare(false)
- .setDirectory(new File(wcTree.getAbsolutePath())).call();
-
- addRepoToClose(git.getRepository());
-
- assertEquals("", errorOutput.toString());
- }
-
- @Override
- @After
- public void tearDown() throws Exception {
- System.setSecurityManager(originalSecurityManager);
- System.setErr(defaultErrorOutput);
- super.tearDown();
- }
-
- /**
- * Refresh the Java Security Policy.
- *
- * @param policy
- * the policy object
- *
- * @throws IOException
- * if the temporary file that contains the policy could not be
- * created
- */
- private static void refreshPolicyAllPermission(Policy policy)
- throws IOException {
- // Starting with an all permissions policy.
- String policyString = "grant { permission java.security.AllPermission; };";
-
- // Do not use TemporaryFilesFactory, it will create a dependency cycle
- Path policyFile = Files.createTempFile("testpolicy", ".txt");
-
- try {
- Files.write(policyFile, Collections.singletonList(policyString));
- System.setProperty("java.security.policy",
- policyFile.toUri().toURL().toString());
- policy.refresh();
- } finally {
- try {
- Files.delete(policyFile);
- } catch (IOException e) {
- // Do not log; the test tests for no logging having occurred
- e.printStackTrace();
- }
- }
- }
-
-}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/SecurityManagerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/SecurityManagerTest.java
deleted file mode 100644
index 2b930a1..0000000
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/SecurityManagerTest.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (C) 2019 Nail Samatov <sanail@yandex.ru> and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-package org.eclipse.jgit.api;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import java.io.File;
-import java.io.FilePermission;
-import java.io.IOException;
-import java.lang.reflect.ReflectPermission;
-import java.nio.file.Files;
-import java.security.Permission;
-import java.security.SecurityPermission;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.PropertyPermission;
-import java.util.logging.LoggingPermission;
-
-import javax.security.auth.AuthPermission;
-
-import org.eclipse.jgit.api.errors.GitAPIException;
-import org.eclipse.jgit.junit.JGitTestUtil;
-import org.eclipse.jgit.junit.MockSystemReader;
-import org.eclipse.jgit.junit.SeparateClassloaderTestRunner;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.treewalk.TreeWalk;
-import org.eclipse.jgit.util.FileUtils;
-import org.eclipse.jgit.util.SystemReader;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * <p>
- * Tests if jgit works if SecurityManager is enabled.
- * </p>
- *
- * <p>
- * Note: JGit's classes shouldn't be used before SecurityManager is configured.
- * If you use some JGit's class before SecurityManager is replaced then part of
- * the code can be invoked outside of our custom SecurityManager and this test
- * becomes useless.
- * </p>
- *
- * <p>
- * For example the class {@link org.eclipse.jgit.util.FS} is used widely in jgit
- * sources. It contains DETECTED static field. At the first usage of the class
- * FS the field DETECTED is initialized and during initialization many system
- * operations that SecurityManager can forbid are invoked.
- * </p>
- *
- * <p>
- * For this reason this test doesn't extend LocalDiskRepositoryTestCase (it uses
- * JGit's classes in setUp() method) and other JGit's utility classes. It's done
- * to affect SecurityManager as less as possible.
- * </p>
- *
- * <p>
- * We use SeparateClassloaderTestRunner to isolate FS.DETECTED field
- * initialization between different tests run.
- * </p>
- */
-@RunWith(SeparateClassloaderTestRunner.class)
-public class SecurityManagerTest {
- private File root;
-
- private SecurityManager originalSecurityManager;
-
- private List<Permission> permissions = new ArrayList<>();
-
- @Before
- public void setUp() throws Exception {
- // Create working directory
- SystemReader.setInstance(new MockSystemReader());
- root = Files.createTempDirectory("jgit-security").toFile();
-
- // Add system permissions
- permissions.add(new RuntimePermission("*"));
- permissions.add(new SecurityPermission("*"));
- permissions.add(new AuthPermission("*"));
- permissions.add(new ReflectPermission("*"));
- permissions.add(new PropertyPermission("*", "read,write"));
- permissions.add(new LoggingPermission("control", null));
-
- permissions.add(new FilePermission(
- System.getProperty("java.home") + "/-", "read"));
-
- String tempDir = System.getProperty("java.io.tmpdir");
- permissions.add(new FilePermission(tempDir, "read,write,delete"));
- permissions
- .add(new FilePermission(tempDir + "/-", "read,write,delete"));
-
- // Add permissions to dependent jar files.
- String classPath = System.getProperty("java.class.path");
- if (classPath != null) {
- for (String path : classPath.split(File.pathSeparator)) {
- permissions.add(new FilePermission(path, "read"));
- }
- }
- // Add permissions to jgit class files.
- String jgitSourcesRoot = new File(System.getProperty("user.dir"))
- .getParent();
- permissions.add(new FilePermission(jgitSourcesRoot + "/-", "read"));
-
- // Add permissions to working dir for jgit. Our git repositories will be
- // initialized and cloned here.
- permissions.add(new FilePermission(root.getPath() + "/-",
- "read,write,delete,execute"));
-
- // Replace Security Manager
- originalSecurityManager = System.getSecurityManager();
- System.setSecurityManager(new SecurityManager() {
-
- @Override
- public void checkPermission(Permission requested) {
- for (Permission permission : permissions) {
- if (permission.implies(requested)) {
- return;
- }
- }
-
- super.checkPermission(requested);
- }
- });
- }
-
- @After
- public void tearDown() throws Exception {
- System.setSecurityManager(originalSecurityManager);
-
- // Note: don't use this method before security manager is replaced in
- // setUp() method. The method uses FS.DETECTED internally and can affect
- // the test.
- FileUtils.delete(root, FileUtils.RECURSIVE | FileUtils.RETRY);
- }
-
- @Test
- public void testInitAndClone() throws IOException, GitAPIException {
- File remote = new File(root, "remote");
- File local = new File(root, "local");
-
- try (Git git = Git.init().setDirectory(remote).call()) {
- JGitTestUtil.write(new File(remote, "hello.txt"), "Hello world!");
- git.add().addFilepattern(".").call();
- git.commit().setMessage("Initial commit").call();
- }
-
- try (Git git = Git.cloneRepository().setURI(remote.toURI().toString())
- .setDirectory(local).call()) {
- assertTrue(new File(local, ".git").exists());
-
- JGitTestUtil.write(new File(local, "hi.txt"), "Hi!");
- git.add().addFilepattern(".").call();
- RevCommit commit1 = git.commit().setMessage("Commit on local repo")
- .call();
- assertEquals("Commit on local repo", commit1.getFullMessage());
- assertNotNull(TreeWalk.forPath(git.getRepository(), "hello.txt",
- commit1.getTree()));
- }
-
- }
-
-}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashCreateCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashCreateCommandTest.java
index 5d0ab05..18cd21a 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashCreateCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashCreateCommandTest.java
@@ -409,8 +409,8 @@ public void refLogIncludesCommitMessage() throws Exception {
assertEquals("content", read(committedFile));
validateStashedCommit(stashed);
- ReflogReader reader = git.getRepository().getReflogReader(
- Constants.R_STASH);
+ ReflogReader reader = git.getRepository().getRefDatabase()
+ .getReflogReader(Constants.R_STASH);
ReflogEntry entry = reader.getLastEntry();
assertNotNull(entry);
assertEquals(ObjectId.zeroId(), entry.getOldId());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashDropCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashDropCommandTest.java
index c81731d..d937579 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashDropCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashDropCommandTest.java
@@ -92,8 +92,8 @@ public void dropSingleStashedCommit() throws Exception {
stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNull(stashRef);
- ReflogReader reader = git.getRepository().getReflogReader(
- Constants.R_STASH);
+ ReflogReader reader = git.getRepository().getRefDatabase()
+ .getReflogReader(Constants.R_STASH);
assertNull(reader);
}
@@ -120,8 +120,8 @@ public void dropAll() throws Exception {
assertNull(git.stashDrop().setAll(true).call());
assertNull(git.getRepository().exactRef(Constants.R_STASH));
- ReflogReader reader = git.getRepository().getReflogReader(
- Constants.R_STASH);
+ ReflogReader reader = git.getRepository().getRefDatabase()
+ .getReflogReader(Constants.R_STASH);
assertNull(reader);
}
@@ -150,8 +150,8 @@ public void dropFirstStashedCommit() throws Exception {
assertNotNull(stashRef);
assertEquals(firstStash, stashRef.getObjectId());
- ReflogReader reader = git.getRepository().getReflogReader(
- Constants.R_STASH);
+ ReflogReader reader = git.getRepository().getRefDatabase()
+ .getReflogReader(Constants.R_STASH);
List<ReflogEntry> entries = reader.getReverseEntries();
assertEquals(1, entries.size());
assertEquals(ObjectId.zeroId(), entries.get(0).getOldId());
@@ -192,8 +192,8 @@ public void dropMiddleStashCommit() throws Exception {
assertNotNull(stashRef);
assertEquals(thirdStash, stashRef.getObjectId());
- ReflogReader reader = git.getRepository().getReflogReader(
- Constants.R_STASH);
+ ReflogReader reader = git.getRepository().getRefDatabase()
+ .getReflogReader(Constants.R_STASH);
List<ReflogEntry> entries = reader.getReverseEntries();
assertEquals(2, entries.size());
assertEquals(ObjectId.zeroId(), entries.get(1).getOldId());
@@ -250,8 +250,8 @@ public void dropBoundaryStashedCommits() throws Exception {
assertNotNull(stashRef);
assertEquals(thirdStash, stashRef.getObjectId());
- ReflogReader reader = git.getRepository().getReflogReader(
- Constants.R_STASH);
+ ReflogReader reader = git.getRepository().getRefDatabase()
+ .getReflogReader(Constants.R_STASH);
List<ReflogEntry> entries = reader.getReverseEntries();
assertEquals(2, entries.size());
assertEquals(ObjectId.zeroId(), entries.get(1).getOldId());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/blame/BlameGeneratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/blame/BlameGeneratorTest.java
index f47f447..c2c06b2 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/blame/BlameGeneratorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/blame/BlameGeneratorTest.java
@@ -23,20 +23,22 @@
/** Unit tests of {@link BlameGenerator}. */
public class BlameGeneratorTest extends RepositoryTestCase {
+ private static final String FILE = "file.txt";
+
@Test
public void testBoundLineDelete() throws Exception {
try (Git git = new Git(db)) {
String[] content1 = new String[] { "first", "second" };
- writeTrashFile("file.txt", join(content1));
- git.add().addFilepattern("file.txt").call();
+ writeTrashFile(FILE, join(content1));
+ git.add().addFilepattern(FILE).call();
RevCommit c1 = git.commit().setMessage("create file").call();
String[] content2 = new String[] { "third", "first", "second" };
- writeTrashFile("file.txt", join(content2));
- git.add().addFilepattern("file.txt").call();
+ writeTrashFile(FILE, join(content2));
+ git.add().addFilepattern(FILE).call();
RevCommit c2 = git.commit().setMessage("create file").call();
- try (BlameGenerator generator = new BlameGenerator(db, "file.txt")) {
+ try (BlameGenerator generator = new BlameGenerator(db, FILE)) {
generator.push(null, db.resolve(Constants.HEAD));
assertEquals(3, generator.getResultContents().size());
@@ -47,7 +49,7 @@ public void testBoundLineDelete() throws Exception {
assertEquals(1, generator.getResultEnd());
assertEquals(0, generator.getSourceStart());
assertEquals(1, generator.getSourceEnd());
- assertEquals("file.txt", generator.getSourcePath());
+ assertEquals(FILE, generator.getSourcePath());
assertTrue(generator.next());
assertEquals(c1, generator.getSourceCommit());
@@ -56,7 +58,7 @@ public void testBoundLineDelete() throws Exception {
assertEquals(3, generator.getResultEnd());
assertEquals(0, generator.getSourceStart());
assertEquals(2, generator.getSourceEnd());
- assertEquals("file.txt", generator.getSourcePath());
+ assertEquals(FILE, generator.getSourcePath());
assertFalse(generator.next());
}
@@ -87,7 +89,8 @@ public void testRenamedBoundLineDelete() throws Exception {
git.add().addFilepattern(FILENAME_2).call();
RevCommit c2 = git.commit().setMessage("change file2").call();
- try (BlameGenerator generator = new BlameGenerator(db, FILENAME_2)) {
+ try (BlameGenerator generator = new BlameGenerator(db,
+ FILENAME_2)) {
generator.push(null, db.resolve(Constants.HEAD));
assertEquals(3, generator.getResultContents().size());
@@ -113,7 +116,8 @@ public void testRenamedBoundLineDelete() throws Exception {
}
// and test again with other BlameGenerator API:
- try (BlameGenerator generator = new BlameGenerator(db, FILENAME_2)) {
+ try (BlameGenerator generator = new BlameGenerator(db,
+ FILENAME_2)) {
generator.push(null, db.resolve(Constants.HEAD));
BlameResult result = generator.computeBlameResult();
@@ -136,21 +140,21 @@ public void testLinesAllDeletedShortenedWalk() throws Exception {
try (Git git = new Git(db)) {
String[] content1 = new String[] { "first", "second", "third" };
- writeTrashFile("file.txt", join(content1));
- git.add().addFilepattern("file.txt").call();
+ writeTrashFile(FILE, join(content1));
+ git.add().addFilepattern(FILE).call();
git.commit().setMessage("create file").call();
String[] content2 = new String[] { "" };
- writeTrashFile("file.txt", join(content2));
- git.add().addFilepattern("file.txt").call();
+ writeTrashFile(FILE, join(content2));
+ git.add().addFilepattern(FILE).call();
git.commit().setMessage("create file").call();
- writeTrashFile("file.txt", join(content1));
- git.add().addFilepattern("file.txt").call();
+ writeTrashFile(FILE, join(content1));
+ git.add().addFilepattern(FILE).call();
RevCommit c3 = git.commit().setMessage("create file").call();
- try (BlameGenerator generator = new BlameGenerator(db, "file.txt")) {
+ try (BlameGenerator generator = new BlameGenerator(db, FILE)) {
generator.push(null, db.resolve(Constants.HEAD));
assertEquals(3, generator.getResultContents().size());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesHandlerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesHandlerTest.java
index 7fb98ec..c41dd81 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesHandlerTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesHandlerTest.java
@@ -584,7 +584,7 @@ private void setupRepo(
}
if (infoAttributesContent != null) {
- File f = new File(db.getDirectory(), Constants.INFO_ATTRIBUTES);
+ File f = new File(db.getCommonDirectory(), Constants.INFO_ATTRIBUTES);
write(f, infoAttributesContent);
}
config.save();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java
index f23469e..35b9533 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java
@@ -26,6 +26,7 @@
import org.eclipse.jgit.dircache.DirCacheIterator;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.treewalk.CanonicalTreeParser;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.junit.Before;
import org.junit.Test;
@@ -230,10 +231,10 @@ private void assertAttributesNode(TreeWalk walk, String pathName,
else {
Attributes entryAttributes = new Attributes();
- new AttributesHandler(walk).mergeAttributes(attributesNode,
- pathName,
- false,
- entryAttributes);
+ new AttributesHandler(walk,
+ () -> walk.getTree(CanonicalTreeParser.class))
+ .mergeAttributes(attributesNode, pathName, false,
+ entryAttributes);
if (nodeAttrs != null && !nodeAttrs.isEmpty()) {
for (Attribute attribute : nodeAttrs) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java
index 1fcfbaf..dbbcb75 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java
@@ -20,6 +20,7 @@
import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
+import org.eclipse.jgit.treewalk.CanonicalTreeParser;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.junit.After;
import org.junit.Test;
@@ -156,8 +157,9 @@ public void testDoubleAsteriskAtEnd() throws IOException {
private void assertAttribute(String path, AttributesNode node,
Attributes attrs) throws IOException {
Attributes attributes = new Attributes();
- new AttributesHandler(DUMMY_WALK).mergeAttributes(node, path, false,
- attributes);
+ new AttributesHandler(DUMMY_WALK,
+ () -> DUMMY_WALK.getTree(CanonicalTreeParser.class))
+ .mergeAttributes(node, path, false, attributes);
assertEquals(attrs, attributes);
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java
index 7b573e1..c6c9138 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java
@@ -26,6 +26,7 @@
import org.eclipse.jgit.junit.JGitTestUtil;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.treewalk.CanonicalTreeParser;
import org.eclipse.jgit.treewalk.FileTreeIterator;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.WorkingTreeIterator;
@@ -194,9 +195,10 @@ private void assertAttributesNode(TreeWalk walk, String pathName,
else {
Attributes entryAttributes = new Attributes();
- new AttributesHandler(walk).mergeAttributes(attributesNode,
- pathName, false,
- entryAttributes);
+ new AttributesHandler(walk,
+ () -> walk.getTree(CanonicalTreeParser.class))
+ .mergeAttributes(attributesNode, pathName, false,
+ entryAttributes);
if (nodeAttrs != null && !nodeAttrs.isEmpty()) {
for (Attribute attribute : nodeAttrs) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/merge/MergeGitAttributeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/merge/MergeGitAttributeTest.java
index 009ca8a..ac30c6c 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/merge/MergeGitAttributeTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/merge/MergeGitAttributeTest.java
@@ -268,6 +268,51 @@ public void mergeTextualFile_SetBinaryMerge_Conflict()
}
@Test
+ public void mergeTextualFile_SetUnionMerge() throws NoWorkTreeException,
+ NoFilepatternException, GitAPIException, IOException {
+ try (Git git = createRepositoryBinaryConflict(g -> {
+ try {
+ writeTrashFile(".gitattributes", "*.cat merge=union");
+ writeTrashFile("main.cat", "A\n" + "B\n" + "C\n" + "D\n");
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }, g -> {
+ try {
+ writeTrashFile("main.cat", "A\n" + "G\n" + "C\n" + "F\n");
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }, g -> {
+ try {
+ writeTrashFile("main.cat", "A\n" + "E\n" + "C\n" + "D\n");
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ })) {
+ // Check that the merge attribute is set to union
+ assertAddMergeAttributeCustom(REFS_HEADS_LEFT, "main.cat", "union");
+ assertAddMergeAttributeCustom(REFS_HEADS_RIGHT, "main.cat",
+ "union");
+
+ checkoutBranch(REFS_HEADS_LEFT);
+ // Merge refs/heads/left -> refs/heads/right
+
+ MergeResult mergeResult = git.merge()
+ .include(git.getRepository().resolve(REFS_HEADS_RIGHT))
+ .call();
+ assertEquals(MergeStatus.MERGED, mergeResult.getMergeStatus());
+
+ // Check that the file is the union of both branches (no conflict
+ // marker added)
+ String result = read(writeTrashFile("res.cat",
+ "A\n" + "G\n" + "E\n" + "C\n" + "F\n"));
+ assertEquals(result, read(git.getRepository().getWorkTree().toPath()
+ .resolve("main.cat").toFile()));
+ }
+ }
+
+ @Test
public void mergeBinaryFile_NoAttr_Conflict() throws IllegalStateException,
IOException, NoHeadException, ConcurrentRefUpdateException,
CheckoutConflictException, InvalidMergeHeadsException,
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/blame/BlameGeneratorCacheTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/blame/BlameGeneratorCacheTest.java
new file mode 100644
index 0000000..65cac11
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/blame/BlameGeneratorCacheTest.java
@@ -0,0 +1,398 @@
+/*
+ * Copyright (C) 2025, Google LLC.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.blame;
+
+import static java.lang.String.join;
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.eclipse.jgit.blame.cache.BlameCache;
+import org.eclipse.jgit.blame.cache.CacheRegion;
+import org.eclipse.jgit.internal.storage.file.FileRepository;
+import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.junit.Test;
+
+public class BlameGeneratorCacheTest extends RepositoryTestCase {
+ private static final String FILE = "file.txt";
+
+ /**
+ * Simple history:
+ *
+ * <pre>
+ * C1 C2 C3 C4 C4 blame
+ * lines ----------------------------------
+ * L1 | C1 C1 C1 C1 C1
+ * L2 | C1 C1 *C3 *C4 C4
+ * L3 | C1 C1 *C3 C3 C3
+ * L4 | *C2 C2 *C4 C4
+ * </pre>
+ *
+ * @throws Exception any error
+ */
+ @Test
+ public void blame_simple_correctRegions() throws Exception {
+ RevCommit c1, c2, c3, c4;
+ try (TestRepository<FileRepository> r = new TestRepository<>(db)) {
+ c1 = commit(r, lines("L1C1", "L2C1", "L3C1"));
+ c2 = commit(r, lines("L1C1", "L2C1", "L3C1", "L4C2"), c1);
+ c3 = commit(r, lines("L1C1", "L2C3", "L3C3", "L4C2"), c2);
+ c4 = commit(r, lines("L1C1", "L2C4", "L3C3", "L4C4"), c3);
+ }
+
+ List<EmittedRegion> expectedRegions = Arrays.asList(
+ new EmittedRegion(c1, 0, 1),
+ new EmittedRegion(c4, 1, 2),
+ new EmittedRegion(c3, 2, 3),
+ new EmittedRegion(c4, 3, 4));
+
+ assertRegions(c4, null, expectedRegions, 4);
+ assertRegions(c4, emptyCache(), expectedRegions, 4);
+ assertRegions(c4, blameAndCache(c4), expectedRegions, 4);
+ assertRegions(c4, blameAndCache(c3), expectedRegions, 4);
+ assertRegions(c4, blameAndCache(c2), expectedRegions, 4);
+ assertRegions(c4, blameAndCache(c1), expectedRegions, 4);
+ }
+
+ @Test
+ public void blame_simple_cacheUsage() throws Exception {
+ RevCommit c1, c2, c3, c4;
+ try (TestRepository<FileRepository> r = new TestRepository<>(db)) {
+ c1 = commit(r, lines("L1C1", "L2C1", "L3C1"));
+ c2 = commit(r, lines("L1C1", "L2C1", "L3C1", "L4C2"), c1);
+ c3 = commit(r, lines("L1C1", "L2C3", "L3C3", "L4C2"), c2);
+ c4 = commit(r, lines("L1C1", "L2C4", "L3C3", "L4C4"), c3);
+ }
+
+ assertCacheUsage(c4, null, false, 4);
+ assertCacheUsage(c4, emptyCache(), false, 4);
+ assertCacheUsage(c4, blameAndCache(c4), true, 1);
+ assertCacheUsage(c4, blameAndCache(c3), true, 2);
+ assertCacheUsage(c4, blameAndCache(c2), true, 3);
+ assertCacheUsage(c4, blameAndCache(c1), true, 4);
+ }
+
+ /**
+ * Overwrite:
+ *
+ * <pre>
+ * C1 C2 C3 C3 blame
+ * lines ----------------------------------
+ * L1 | C1 C1 *C3 C3
+ * L2 | C1 C1 *C3 C3
+ * L3 | C1 C1 *C3 C3
+ * L4 | *C2
+ * </pre>
+ *
+ * @throws Exception any error
+ */
+ @Test
+ public void blame_ovewrite_correctRegions() throws Exception {
+ RevCommit c1, c2, c3;
+ try (TestRepository<FileRepository> r = new TestRepository<>(db)) {
+ c1 = commit(r, lines("L1C1", "L2C1", "L3C1"));
+ c2 = commit(r, lines("L1C1", "L2C1", "L3C1", "L4C2"), c1);
+ c3 = commit(r, lines("L1C3", "L2C3", "L3C3"), c2);
+ }
+
+ List<EmittedRegion> expectedRegions = Arrays.asList(
+ new EmittedRegion(c3, 0, 3));
+
+ assertRegions(c3, null, expectedRegions, 3);
+ assertRegions(c3, emptyCache(), expectedRegions, 3);
+ assertRegions(c3, blameAndCache(c3), expectedRegions, 3);
+ assertRegions(c3, blameAndCache(c2), expectedRegions, 3);
+ assertRegions(c3, blameAndCache(c1), expectedRegions, 3);
+ }
+
+ @Test
+ public void blame_overwrite_cacheUsage() throws Exception {
+ RevCommit c1, c2, c3;
+ try (TestRepository<FileRepository> r = new TestRepository<>(db)) {
+ c1 = commit(r, lines("L1C1", "L2C1", "L3C1"));
+ c2 = commit(r, lines("L1C1", "L2C1", "L3C1", "L4C2"), c1);
+ c3 = commit(r, lines("L1C3", "L2C3", "L3C3"), c2);
+ }
+
+ assertCacheUsage(c3, null, false, 1);
+ assertCacheUsage(c3, emptyCache(), false, 1);
+ assertCacheUsage(c3, blameAndCache(c3), true, 1);
+ assertCacheUsage(c3, blameAndCache(c2), false, 1);
+ assertCacheUsage(c3, blameAndCache(c1), false, 1);
+ }
+
+ /**
+ * Merge:
+ *
+ * <pre>
+ * root
+ * ----
+ * L1 -
+ * L2 -
+ * L3 -
+ * / \
+ * sideA sideB
+ * ----- -----
+ * *L1 a L1 -
+ * *L2 a L2 -
+ * *L3 a L3 -
+ * *L4 a *L4 b
+ * L5 - *L5 b
+ * L6 - *L6 b
+ * L7 - *L7 b
+ * \ /
+ * merge
+ * -----
+ * L1-L4 a (from sideA)
+ * L5-L7 - (common, from root)
+ * L8-L11 b (from sideB)
+ * </pre>
+ *
+ * @throws Exception any error
+ */
+ @Test
+ public void blame_merge_correctRegions() throws Exception {
+ RevCommit root, sideA, sideB, mergedTip;
+ try (TestRepository<FileRepository> r = new TestRepository<>(db)) {
+ root = commitAsLines(r, "---");
+ sideA = commitAsLines(r, "aaaa---", root);
+ sideB = commitAsLines(r, "---bbbb", root);
+ mergedTip = commitAsLines(r, "aaaa---bbbb", sideA, sideB);
+ }
+
+ List<EmittedRegion> expectedRegions = Arrays.asList(
+ new EmittedRegion(sideA, 0, 4),
+ new EmittedRegion(root, 4, 7),
+ new EmittedRegion(sideB, 7, 11));
+
+ assertRegions(mergedTip, null, expectedRegions, 11);
+ assertRegions(mergedTip, emptyCache(), expectedRegions, 11);
+ assertRegions(mergedTip, blameAndCache(root), expectedRegions, 11);
+ assertRegions(mergedTip, blameAndCache(sideA), expectedRegions, 11);
+ assertRegions(mergedTip, blameAndCache(sideB), expectedRegions, 11);
+ assertRegions(mergedTip, blameAndCache(mergedTip), expectedRegions, 11);
+ }
+
+ @Test
+ public void blame_merge_cacheUsage() throws Exception {
+ RevCommit root, sideA, sideB, mergedTip;
+ try (TestRepository<FileRepository> r = new TestRepository<>(db)) {
+ root = commitAsLines(r, "---");
+ sideA = commitAsLines(r, "aaaa---", root);
+ sideB = commitAsLines(r, "---bbbb", root);
+ mergedTip = commitAsLines(r, "aaaa---bbbb", sideA, sideB);
+ }
+
+ assertCacheUsage(mergedTip, null, /* cacheUsed */ false,
+ /* candidates */ 4);
+ assertCacheUsage(mergedTip, emptyCache(), false, 4);
+ assertCacheUsage(mergedTip, blameAndCache(mergedTip), true, 1);
+
+ // While splitting unblamed regions to parents, sideA comes first
+ // and gets "aaaa----". Processing is by commit time, so sideB is
+ // explored first
+ assertCacheUsage(mergedTip, blameAndCache(sideA), true, 3);
+ assertCacheUsage(mergedTip, blameAndCache(sideB), true, 4);
+ assertCacheUsage(mergedTip, blameAndCache(root), true, 4);
+ }
+
+ /**
+ * Moving block (insertion)
+ *
+ * <pre>
+ * C1 C2 C3 C3 blame
+ * lines ----------------------------------
+ * L1 | C1 C1 C1 C1
+ * L2 | C1 *C2 C2 C2
+ * L3 | C1 *C3 C3
+ * L4 | C1 C1
+ * </pre>
+ *
+ * @throws Exception any error
+ */
+ @Test
+ public void blame_movingBlock_correctRegions() throws Exception {
+ RevCommit c1, c2, c3;
+ try (TestRepository<FileRepository> r = new TestRepository<>(db)) {
+ c1 = commit(r, lines("L1C1", "L2C1"));
+ c2 = commit(r, lines("L1C1", "middle", "L2C1"), c1);
+ c3 = commit(r, lines("L1C1", "middle", "extra", "L2C1"), c2);
+ }
+
+ List<EmittedRegion> expectedRegions = Arrays.asList(
+ new EmittedRegion(c1, 0, 1),
+ new EmittedRegion(c2, 1, 2),
+ new EmittedRegion(c3, 2, 3),
+ new EmittedRegion(c1, 3, 4));
+
+ assertRegions(c3, null, expectedRegions, 4);
+ assertRegions(c3, emptyCache(), expectedRegions, 4);
+ assertRegions(c3, blameAndCache(c3), expectedRegions, 4);
+ assertRegions(c3, blameAndCache(c2), expectedRegions, 4);
+ assertRegions(c3, blameAndCache(c1), expectedRegions, 4);
+ }
+
+ @Test
+ public void blame_movingBlock_cacheUsage() throws Exception {
+ RevCommit c1, c2, c3;
+ try (TestRepository<FileRepository> r = new TestRepository<>(db)) {
+ c1 = commitAsLines(r, "root---");
+ c2 = commitAsLines(r, "rootXXX---", c1);
+ c3 = commitAsLines(r, "rootYYYXXX---", c2);
+ }
+
+ assertCacheUsage(c3, null, false, 3);
+ assertCacheUsage(c3, emptyCache(), false, 3);
+ assertCacheUsage(c3, blameAndCache(c3), true, 1);
+ assertCacheUsage(c3, blameAndCache(c2), true, 2);
+ assertCacheUsage(c3, blameAndCache(c1), true, 3);
+ }
+
+ private void assertRegions(RevCommit commit, InMemoryBlameCache cache,
+ List<EmittedRegion> expectedRegions, int resultLineCount)
+ throws IOException {
+ try (BlameGenerator gen = new BlameGenerator(db, FILE, cache)) {
+ gen.push(null, db.parseCommit(commit));
+ List<EmittedRegion> regions = consume(gen);
+ assertRegionsEquals(expectedRegions, regions);
+ assertAllLinesCovered(/* lines= */ resultLineCount, regions);
+ }
+ }
+
+ private void assertCacheUsage(RevCommit commit, InMemoryBlameCache cache,
+ boolean useCache, int candidatesVisited) throws IOException {
+ try (BlameGenerator gen = new BlameGenerator(db, FILE, cache)) {
+ gen.push(null, db.parseCommit(commit));
+ consume(gen);
+ assertEquals(useCache, gen.getStats().isCacheHit());
+ assertEquals(candidatesVisited,
+ gen.getStats().getCandidatesVisited());
+ }
+ }
+
+ private static void assertAllLinesCovered(int lines,
+ List<EmittedRegion> regions) {
+ Collections.sort(regions);
+ assertEquals("Starts in first line", 0, regions.get(0).resultStart());
+ for (int i = 1; i < regions.size(); i++) {
+ assertEquals("No gaps", regions.get(i).resultStart(),
+ regions.get(i - 1).resultEnd());
+ }
+ assertEquals("Ends in last line", lines,
+ regions.get(regions.size() - 1).resultEnd());
+ }
+
+ private static void assertRegionsEquals(
+ List<EmittedRegion> expected, List<EmittedRegion> actual) {
+ assertEquals(expected.size(), actual.size());
+ Collections.sort(actual);
+ for (int i = 0; i < expected.size(); i++) {
+ assertEquals(String.format("List differ in element %d", i),
+ expected.get(i), actual.get(i));
+ }
+ }
+
+ private static InMemoryBlameCache emptyCache() {
+ return new InMemoryBlameCache("<empty>");
+ }
+
+ private List<EmittedRegion> consume(BlameGenerator generator)
+ throws IOException {
+ List<EmittedRegion> result = new ArrayList<>();
+ while (generator.next()) {
+ EmittedRegion genRegion = new EmittedRegion(
+ generator.getSourceCommit().toObjectId(),
+ generator.getResultStart(), generator.getResultEnd());
+ result.add(genRegion);
+ }
+ return result;
+ }
+
+ private InMemoryBlameCache blameAndCache(RevCommit commit)
+ throws IOException {
+ List<CacheRegion> regions;
+ try (BlameGenerator generator = new BlameGenerator(db, FILE)) {
+ generator.push(null, commit);
+ regions = consume(generator).stream()
+ .map(EmittedRegion::asCacheRegion)
+ .collect(Collectors.toUnmodifiableList());
+ }
+ InMemoryBlameCache cache = new InMemoryBlameCache("<x>");
+ cache.put(commit, FILE, regions);
+ return cache;
+ }
+
+ private static RevCommit commitAsLines(TestRepository<?> r,
+ String charPerLine, RevCommit... parents) throws Exception {
+ return commit(r, charPerLine.replaceAll("\\S", "$0\n"), parents);
+ }
+
+ private static RevCommit commit(TestRepository<?> r, String contents,
+ RevCommit... parents) throws Exception {
+ return r.commit(r.tree(r.file(FILE, r.blob(contents))), parents);
+ }
+
+ private static String lines(String... l) {
+ return join("\n", l);
+ }
+
+ private record EmittedRegion(ObjectId oid, int resultStart, int resultEnd)
+ implements Comparable<EmittedRegion> {
+ @Override
+ public int compareTo(EmittedRegion o) {
+ return resultStart - o.resultStart;
+ }
+
+ CacheRegion asCacheRegion() {
+ return new CacheRegion(FILE, oid, resultStart, resultEnd);
+ }
+ }
+
+ private static class InMemoryBlameCache implements BlameCache {
+
+ private final Map<Key, List<CacheRegion>> cache = new HashMap<>();
+
+ private final String description;
+
+ public InMemoryBlameCache(String description) {
+ this.description = description;
+ }
+
+ @Override
+ public List<CacheRegion> get(Repository repo, ObjectId commitId,
+ String path) throws IOException {
+ return cache.get(new Key(commitId.name(), path));
+ }
+
+ public void put(ObjectId commitId, String path,
+ List<CacheRegion> cachedRegions) {
+ cache.put(new Key(commitId.name(), path), cachedRegions);
+ }
+
+ @Override
+ public String toString() {
+ return "InMemoryCache: " + description;
+ }
+
+ record Key(String commitId, String path) {
+ }
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/blame/BlameRegionMergerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/blame/BlameRegionMergerTest.java
new file mode 100644
index 0000000..1b28676
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/blame/BlameRegionMergerTest.java
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2025, Google LLC.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.blame;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import org.eclipse.jgit.blame.cache.CacheRegion;
+import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.junit.Test;
+
+public class BlameRegionMergerTest extends RepositoryTestCase {
+
+ private static final ObjectId O1 = ObjectId
+ .fromString("ff6dd8db6edc9aa0ac58fea1d14a55be46c3eb14");
+
+ private static final ObjectId O2 = ObjectId
+ .fromString("c3c7f680c6bee238617f25f6aa85d0b565fc8ecb");
+
+ private static final ObjectId O3 = ObjectId
+ .fromString("29e014aad0399fe8ede7c101d01b6e440ac9966b");
+
+ List<RevCommit> fakeCommits = List.of(new FakeRevCommit(O1),
+ new FakeRevCommit(O2), new FakeRevCommit(O3));
+
+ // In reverse order, so the code doesn't assume a sorted list
+ List<CacheRegion> cachedRegions = List.of(
+ new CacheRegion("README", O3, 20, 30),
+ new CacheRegion("README", O2, 10, 20),
+ new CacheRegion("README", O1, 0, 10));
+
+ BlameRegionMerger blamer = new BlameRegionMergerFakeCommits(fakeCommits,
+ cachedRegions);
+
+ @Test
+ public void intersectRegions_allInside() {
+ Region unblamed = new Region(15, 18, 10);
+ CacheRegion blamed = new CacheRegion("README", O1, 10, 90);
+
+ Region result = BlameRegionMerger.intersectRegions(unblamed, blamed);
+ // Same lines in result and source
+ assertEquals(15, result.resultStart);
+ assertEquals(18, result.sourceStart);
+ assertEquals(10, result.length);
+ assertNull(result.next);
+ }
+
+ @Test
+ public void intersectRegions_startsBefore() {
+ // Intesecting [4, 14) with [10, 90)
+ Region unblamed = new Region(30, 4, 10);
+ CacheRegion blamed = new CacheRegion("README", O1, 10, 90);
+
+ Region result = BlameRegionMerger.intersectRegions(unblamed, blamed);
+
+ // The unblamed region starting at 4 (sourceStart), starts at 30 in the
+ // original file (resultStart). e.g. some commit introduced
+ // lines. If we take the second portion of the region, we need to move
+ // the result start accordingly.
+ assertEquals(36, result.resultStart);
+ assertEquals(10, result.sourceStart);
+ assertEquals(4, result.length);
+ assertNull(result.next);
+ }
+
+ @Test
+ public void intersectRegions_endsAfter() {
+ // Intesecting [85, 95) with [10, 90)
+ Region unblamed = new Region(30, 85, 10);
+ CacheRegion blamed = new CacheRegion("README", O1, 10, 90);
+
+ Region result = BlameRegionMerger.intersectRegions(unblamed, blamed);
+
+ assertEquals(30, result.resultStart);
+ assertEquals(85, result.sourceStart);
+ assertEquals(5, result.length);
+ assertNull(result.next);
+ }
+
+ @Test
+ public void intersectRegions_spillOverBothSides() {
+ // Intesecting [5, 100) with [10, 90)
+ Region unblamed = new Region(30, 5, 95);
+ CacheRegion blamed = new CacheRegion("README", O1, 10, 90);
+
+ Region result = BlameRegionMerger.intersectRegions(unblamed, blamed);
+
+ assertEquals(35, result.resultStart);
+ assertEquals(10, result.sourceStart);
+ assertEquals(80, result.length);
+ assertNull(result.next);
+ }
+
+ @Test
+ public void intersectRegions_exactMatch() {
+ // Intesecting [5, 100) with [10, 90)
+ Region unblamed = new Region(30, 10, 80);
+ CacheRegion blamed = new CacheRegion("README", O1, 10, 90);
+
+ Region result = BlameRegionMerger.intersectRegions(unblamed, blamed);
+
+ assertEquals(30, result.resultStart);
+ assertEquals(10, result.sourceStart);
+ assertEquals(80, result.length);
+ assertNull(result.next);
+ }
+
+ @Test
+ public void findOverlaps_allInside() {
+ Region unblamed = new Region(0, 11, 4);
+ List<CacheRegion> overlaps = blamer.findOverlaps(unblamed);
+ assertEquals(1, overlaps.size());
+ assertEquals(10, overlaps.get(0).getStart());
+ assertEquals(20, overlaps.get(0).getEnd());
+ }
+
+ @Test
+ public void findOverlaps_overTwoRegions() {
+ Region unblamed = new Region(0, 8, 4);
+ List<CacheRegion> overlaps = blamer.findOverlaps(unblamed);
+ assertEquals(2, overlaps.size());
+ assertEquals(0, overlaps.get(0).getStart());
+ assertEquals(10, overlaps.get(0).getEnd());
+ assertEquals(10, overlaps.get(1).getStart());
+ assertEquals(20, overlaps.get(1).getEnd());
+ }
+
+ @Test
+ public void findOverlaps_overThreeRegions() {
+ Region unblamed = new Region(0, 8, 15);
+ List<CacheRegion> overlaps = blamer.findOverlaps(unblamed);
+ assertEquals(3, overlaps.size());
+ assertEquals(0, overlaps.get(0).getStart());
+ assertEquals(10, overlaps.get(0).getEnd());
+ assertEquals(10, overlaps.get(1).getStart());
+ assertEquals(20, overlaps.get(1).getEnd());
+ assertEquals(20, overlaps.get(2).getStart());
+ assertEquals(30, overlaps.get(2).getEnd());
+ }
+
+ @Test
+ public void blame_exactOverlap() throws IOException {
+ Region unblamed = new Region(0, 10, 10);
+ List<Candidate> blamed = blamer.mergeOneRegion(unblamed);
+
+ assertEquals(1, blamed.size());
+ Candidate c = blamed.get(0);
+ assertEquals(c.sourceCommit.name(), O2.name());
+ assertEquals(c.regionList.resultStart, unblamed.resultStart);
+ assertEquals(c.regionList.sourceStart, unblamed.sourceStart);
+ assertEquals(10, c.regionList.length);
+ assertNull(c.regionList.next);
+ }
+
+ @Test
+ public void blame_corruptedIndex() {
+ Region outOfRange = new Region(0, 43, 4);
+ // This region is out of the blamed area
+ assertThrows(IOException.class,
+ () -> blamer.mergeOneRegion(outOfRange));
+ }
+
+ @Test
+ public void blame_allInsideOneBlamedRegion() throws IOException {
+ Region unblamed = new Region(0, 5, 3);
+ // This region if fully blamed to O1
+ List<Candidate> blamed = blamer.mergeOneRegion(unblamed);
+ assertEquals(1, blamed.size());
+ Candidate c = blamed.get(0);
+ assertEquals(c.sourceCommit.name(), O1.name());
+ assertEquals(c.regionList.resultStart, unblamed.resultStart);
+ assertEquals(c.regionList.sourceStart, unblamed.sourceStart);
+ assertEquals(3, c.regionList.length);
+ assertNull(c.regionList.next);
+ }
+
+ @Test
+ public void blame_overTwoBlamedRegions() throws IOException {
+ Region unblamed = new Region(0, 8, 5);
+ // (8, 10) belongs go C1, (10, 13) to C2
+ List<Candidate> blamed = blamer.mergeOneRegion(unblamed);
+ assertEquals(2, blamed.size());
+ Candidate c = blamed.get(0);
+ assertEquals(c.sourceCommit.name(), O1.name());
+ assertEquals(unblamed.resultStart, c.regionList.resultStart);
+ assertEquals(unblamed.sourceStart, c.regionList.sourceStart);
+ assertEquals(2, c.regionList.length);
+ assertNull(c.regionList.next);
+
+ c = blamed.get(1);
+ assertEquals(c.sourceCommit.name(), O2.name());
+ assertEquals(2, c.regionList.resultStart);
+ assertEquals(10, c.regionList.sourceStart);
+ assertEquals(3, c.regionList.length);
+ assertNull(c.regionList.next);
+ }
+
+ @Test
+ public void blame_all() throws IOException {
+ Region unblamed = new Region(0, 0, 30);
+ List<Candidate> blamed = blamer.mergeOneRegion(unblamed);
+ assertEquals(3, blamed.size());
+ Candidate c = blamed.get(0);
+ assertEquals(c.sourceCommit.name(), O1.name());
+ assertEquals(unblamed.resultStart, c.regionList.resultStart);
+ assertEquals(unblamed.sourceStart, c.regionList.sourceStart);
+ assertEquals(10, c.regionList.length);
+ assertNull(c.regionList.next);
+
+ c = blamed.get(1);
+ assertEquals(c.sourceCommit.name(), O2.name());
+ assertEquals(10, c.regionList.resultStart);
+ assertEquals(10, c.regionList.sourceStart);
+ assertEquals(10, c.regionList.length);
+ assertNull(c.regionList.next);
+
+ c = blamed.get(2);
+ assertEquals(c.sourceCommit.name(), O3.name());
+ assertEquals(20, c.regionList.resultStart);
+ assertEquals(20, c.regionList.sourceStart);
+ assertEquals(10, c.regionList.length);
+ assertNull(c.regionList.next);
+ }
+
+ @Test
+ public void blame_fromCandidate() {
+ // We don't use anything from the candidate besides the
+ // regionList
+ Candidate c = new Candidate(null, null, null);
+ c.regionList = new Region(0, 8, 5);
+ c.regionList.next = new Region(22, 22, 4);
+
+ Candidate blamed = blamer.mergeCandidate(c);
+ // Three candidates
+ assertNotNull(blamed);
+ assertNotNull(blamed.queueNext);
+ assertNotNull(blamed.queueNext.queueNext);
+ assertNull(blamed.queueNext.queueNext.queueNext);
+
+ assertEquals(O1.name(), blamed.sourceCommit.name());
+
+ Candidate second = blamed.queueNext;
+ assertEquals(O2.name(), second.sourceCommit.name());
+
+ Candidate third = blamed.queueNext.queueNext;
+ assertEquals(O3.name(), third.sourceCommit.name());
+ }
+
+ @Test
+ public void blame_fromCandidate_twiceCandidateInOutput() {
+ Candidate c = new Candidate(null, null, null);
+ // This produces O1 and O2
+ c.regionList = new Region(0, 8, 5);
+ // This produces O2 and O3
+ c.regionList.next = new Region(20, 15, 7);
+
+ Candidate blamed = blamer.mergeCandidate(c);
+ assertCandidateSingleRegion(O1, 2, blamed);
+ blamed = blamed.queueNext;
+ assertCandidateSingleRegion(O2, 3, blamed);
+ // We do not merge candidates afterwards, so these are
+ // two different candidates to the same source
+ blamed = blamed.queueNext;
+ assertCandidateSingleRegion(O2, 5, blamed);
+ blamed = blamed.queueNext;
+ assertCandidateSingleRegion(O3, 2, blamed);
+ assertNull(blamed.queueNext);
+ }
+
+ private static void assertCandidateSingleRegion(ObjectId expectedOid,
+ int expectedLength, Candidate actual) {
+ assertNotNull("candidate", actual);
+ assertNotNull("region list not empty", actual.regionList);
+ assertNull("region list has only one element", actual.regionList.next);
+ assertEquals(expectedOid, actual.sourceCommit);
+ assertEquals(expectedLength, actual.regionList.length);
+ }
+
+ private static final class BlameRegionMergerFakeCommits
+ extends BlameRegionMerger {
+
+ private final Map<ObjectId, RevCommit> cache;
+
+ BlameRegionMergerFakeCommits(List<RevCommit> commits,
+ List<CacheRegion> blamedRegions) {
+ super(null, null, blamedRegions);
+ cache = commits.stream().collect(Collectors
+ .toMap(RevCommit::toObjectId, Function.identity()));
+ }
+
+ @Override
+ protected RevCommit parse(ObjectId oid) {
+ return cache.get(oid);
+ }
+ }
+
+ private static final class FakeRevCommit extends RevCommit {
+ FakeRevCommit(AnyObjectId id) {
+ super(id);
+ }
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterBuiltInDriverTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterBuiltInDriverTest.java
new file mode 100644
index 0000000..1352871
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterBuiltInDriverTest.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.eclipse.jgit.diff;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.stream.Collectors;
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.junit.JGitTestUtil;
+import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.treewalk.CanonicalTreeParser;
+import org.junit.Test;
+
+public class DiffFormatterBuiltInDriverTest extends RepositoryTestCase {
+ @Test
+ public void testCppDriver() throws Exception {
+ String fileName = "greeting.c";
+ String body = Files.readString(
+ Path.of(JGitTestUtil.getTestResourceFile(fileName)
+ .getAbsolutePath()));
+ RevCommit c1;
+ RevCommit c2;
+ try (Git git = new Git(db)) {
+ createCommit(git, ".gitattributes", "*.c diff=cpp");
+ c1 = createCommit(git, fileName, body);
+ c2 = createCommit(git, fileName,
+ body.replace("Good day", "Greetings")
+ .replace("baz", "qux"));
+ }
+ try (ByteArrayOutputStream os = new ByteArrayOutputStream();
+ DiffFormatter diffFormatter = new DiffFormatter(os)) {
+ String actual = getHunkHeaders(c1, c2, os, diffFormatter);
+ String expected =
+ "@@ -27,7 +27,7 @@ void getPersonalizedGreeting(char *result, const char *name, const char *timeOfD\n"
+ + "@@ -37,7 +37,7 @@ int main() {";
+ assertEquals(expected, actual);
+ }
+ }
+
+ @Test
+ public void testDtsDriver() throws Exception {
+ String fileName = "sample.dtsi";
+ String body = Files.readString(
+ Path.of(JGitTestUtil.getTestResourceFile(fileName)
+ .getAbsolutePath()));
+ RevCommit c1;
+ RevCommit c2;
+ try (Git git = new Git(db)) {
+ createCommit(git, ".gitattributes", "*.dtsi diff=dts");
+ c1 = createCommit(git, fileName, body);
+ c2 = createCommit(git, fileName,
+ body.replace("clock-frequency = <24000000>",
+ "clock-frequency = <48000000>"));
+ }
+ try (ByteArrayOutputStream os = new ByteArrayOutputStream();
+ DiffFormatter diffFormatter = new DiffFormatter(os)) {
+ String actual = getHunkHeaders(c1, c2, os, diffFormatter);
+ String expected = "@@ -20,6 +20,6 @@ uart0: uart@101f1000 {";
+ assertEquals(expected, actual);
+ }
+ }
+
+ @Test
+ public void testJavaDriver() throws Exception {
+ String resourceName = "greeting.javasource";
+ String body = Files.readString(
+ Path.of(JGitTestUtil.getTestResourceFile(resourceName)
+ .getAbsolutePath()));
+ RevCommit c1;
+ RevCommit c2;
+ try (Git git = new Git(db)) {
+ createCommit(git, ".gitattributes", "*.java diff=java");
+ String fileName = "Greeting.java";
+ c1 = createCommit(git, fileName, body);
+ c2 = createCommit(git, fileName,
+ body.replace("Good day", "Greetings")
+ .replace("baz", "qux"));
+ }
+ try (ByteArrayOutputStream os = new ByteArrayOutputStream();
+ DiffFormatter diffFormatter = new DiffFormatter(os)) {
+ String actual = getHunkHeaders(c1, c2, os, diffFormatter);
+ String expected =
+ "@@ -22,7 +22,7 @@ public String getPersonalizedGreeting(String name, String timeOfDay) {\n"
+ + "@@ -32,6 +32,6 @@ public static void main(String[] args) {";
+ assertEquals(expected, actual);
+ }
+ }
+
+ @Test
+ public void testPythonDriver() throws Exception {
+ String fileName = "greeting.py";
+ String body = Files.readString(
+ Path.of(JGitTestUtil.getTestResourceFile(fileName)
+ .getAbsolutePath()));
+ RevCommit c1;
+ RevCommit c2;
+ try (Git git = new Git(db)) {
+ createCommit(git, ".gitattributes", "*.py diff=python");
+ c1 = createCommit(git, fileName, body);
+ c2 = createCommit(git, fileName,
+ body.replace("Good day", "Greetings"));
+ }
+ try (ByteArrayOutputStream os = new ByteArrayOutputStream();
+ DiffFormatter diffFormatter = new DiffFormatter(os)) {
+ String actual = getHunkHeaders(c1, c2, os, diffFormatter);
+ String expected = "@@ -16,7 +16,7 @@ def get_personalized_greeting(self, name, time_of_day):";
+ assertEquals(expected, actual);
+ }
+ }
+
+ @Test
+ public void testRustDriver() throws Exception {
+ String fileName = "greeting.rs";
+ String body = Files.readString(
+ Path.of(JGitTestUtil.getTestResourceFile(fileName)
+ .getAbsolutePath()));
+ RevCommit c1;
+ RevCommit c2;
+ try (Git git = new Git(db)) {
+ createCommit(git, ".gitattributes", "*.rs diff=rust");
+ c1 = createCommit(git, fileName, body);
+ c2 = createCommit(git, fileName,
+ body.replace("Good day", "Greetings")
+ .replace("baz", "qux"));
+ }
+ try (ByteArrayOutputStream os = new ByteArrayOutputStream();
+ DiffFormatter diffFormatter = new DiffFormatter(os)) {
+ String actual = getHunkHeaders(c1, c2, os, diffFormatter);
+ String expected =
+ "@@ -14,7 +14,7 @@ fn get_personalized_greeting(&self, name: &str, time_of_day: &str) -> String {\n"
+ + "@@ -23,5 +23,5 @@ fn main() {";
+ assertEquals(expected, actual);
+ }
+ }
+
+ private String getHunkHeaders(RevCommit c1, RevCommit c2,
+ ByteArrayOutputStream os, DiffFormatter diffFormatter)
+ throws IOException {
+ diffFormatter.setRepository(db);
+ diffFormatter.format(new CanonicalTreeParser(null, db.newObjectReader(),
+ c1.getTree()),
+ new CanonicalTreeParser(null, db.newObjectReader(),
+ c2.getTree()));
+ diffFormatter.flush();
+ return Arrays.stream(os.toString(StandardCharsets.UTF_8).split("\n"))
+ .filter(line -> line.startsWith("@@"))
+ .collect(Collectors.joining("\n"));
+ }
+
+ private RevCommit createCommit(Git git, String fileName, String body)
+ throws IOException, GitAPIException {
+ writeTrashFile(fileName, body);
+ git.add().addFilepattern(".").call();
+ return git.commit().setMessage("message").call();
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/BareSuperprojectWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/BareSuperprojectWriterTest.java
index c3b9387..5065b57 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/BareSuperprojectWriterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/BareSuperprojectWriterTest.java
@@ -12,6 +12,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.nullValue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -22,6 +23,7 @@
import org.eclipse.jgit.gitrepo.BareSuperprojectWriter.BareWriterConfig;
import org.eclipse.jgit.gitrepo.RepoCommand.RemoteReader;
import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Repository;
@@ -68,6 +70,49 @@ public void write_setGitModulesContents() throws Exception {
}
@Test
+ public void write_setGitModulesContents_pinned() throws Exception {
+ try (Repository bareRepo = createBareRepository()) {
+ RepoProject pinWithUpstream = new RepoProject("pinWithUpstream",
+ "path/x", "cbc0fae7e1911d27e1de37d364698dba4411c78b",
+ "remote", "");
+ pinWithUpstream.setUrl("http://example.com/a");
+ pinWithUpstream.setUpstream("branchX");
+
+ RepoProject pinWithoutUpstream = new RepoProject(
+ "pinWithoutUpstream", "path/y",
+ "cbc0fae7e1911d27e1de37d364698dba4411c78b", "remote", "");
+ pinWithoutUpstream.setUrl("http://example.com/b");
+
+ RemoteReader mockRemoteReader = mock(RemoteReader.class);
+
+ BareSuperprojectWriter w = new BareSuperprojectWriter(bareRepo,
+ null, "refs/heads/master", author, mockRemoteReader,
+ BareWriterConfig.getDefault(), List.of());
+
+ RevCommit commit = w
+ .write(Arrays.asList(pinWithUpstream, pinWithoutUpstream));
+
+ String contents = readContents(bareRepo, commit, ".gitmodules");
+ Config cfg = new Config();
+ cfg.fromText(contents);
+
+ assertThat(cfg.getString("submodule", "pinWithUpstream", "path"),
+ is("path/x"));
+ assertThat(cfg.getString("submodule", "pinWithUpstream", "url"),
+ is("http://example.com/a"));
+ assertThat(cfg.getString("submodule", "pinWithUpstream", "ref"),
+ is("branchX"));
+
+ assertThat(cfg.getString("submodule", "pinWithoutUpstream", "path"),
+ is("path/y"));
+ assertThat(cfg.getString("submodule", "pinWithoutUpstream", "url"),
+ is("http://example.com/b"));
+ assertThat(cfg.getString("submodule", "pinWithoutUpstream", "ref"),
+ nullValue());
+ }
+ }
+
+ @Test
public void write_setExtraContents() throws Exception {
try (Repository bareRepo = createBareRepository()) {
RepoProject repoProject = new RepoProject("subprojectX", "path/to",
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/ManifestParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/ManifestParserTest.java
index 20958a8..fca27d3 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/ManifestParserTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/ManifestParserTest.java
@@ -11,6 +11,7 @@
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -18,7 +19,9 @@
import java.io.IOException;
import java.net.URI;
import java.util.HashSet;
+import java.util.Map;
import java.util.Set;
+import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -138,6 +141,72 @@ public void testRemoveProject() throws Exception {
.collect(Collectors.toSet()));
}
+ @Test
+ public void testPinProjectWithUpstream() throws Exception {
+ StringBuilder xmlContent = new StringBuilder();
+ xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+ .append("<manifest>")
+ .append("<remote name=\"remote1\" fetch=\".\" />")
+ .append("<default revision=\"master\" remote=\"remote1\" />")
+ .append("<project path=\"foo\" name=\"pin-with-upstream\"")
+ .append(" revision=\"9b2fe85c0279f4d5ac69f07ddcd48566c3555405\"")
+ .append(" upstream=\"branchX\"/>")
+ .append("<project path=\"bar\" name=\"pin-without-upstream\"")
+ .append(" revision=\"76ce6d91a2e07fdfcbfc8df6970c9e98a98e36a0\" />")
+ .append("</manifest>");
+
+ ManifestParser parser = new ManifestParser(null, null, "master",
+ "https://git.google.com/", null, null);
+ parser.read(new ByteArrayInputStream(
+ xmlContent.toString().getBytes(UTF_8)));
+
+ Map<String, RepoProject> repos = parser.getProjects().stream().collect(
+ Collectors.toMap(RepoProject::getName, Function.identity()));
+ assertEquals(2, repos.size());
+
+ RepoProject foo = repos.get("pin-with-upstream");
+ assertEquals("pin-with-upstream", foo.getName());
+ assertEquals("9b2fe85c0279f4d5ac69f07ddcd48566c3555405",
+ foo.getRevision());
+ assertEquals("branchX", foo.getUpstream());
+
+ RepoProject bar = repos.get("pin-without-upstream");
+ assertEquals("pin-without-upstream", bar.getName());
+ assertEquals("76ce6d91a2e07fdfcbfc8df6970c9e98a98e36a0",
+ bar.getRevision());
+ assertNull(bar.getUpstream());
+ }
+
+ @Test
+ public void testWithDestBranch() throws Exception {
+ StringBuilder xmlContent = new StringBuilder();
+ xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+ .append("<manifest>")
+ .append("<remote name=\"remote1\" fetch=\".\" />")
+ .append("<default revision=\"master\" remote=\"remote1\" />")
+ .append("<project path=\"foo\" name=\"foo\"")
+ .append(" dest-branch=\"branchX\"/>")
+ .append("<project path=\"bar\" name=\"bar\"/>")
+ .append("</manifest>");
+
+ ManifestParser parser = new ManifestParser(null, null, "master",
+ "https://git.google.com/", null, null);
+ parser.read(new ByteArrayInputStream(
+ xmlContent.toString().getBytes(UTF_8)));
+
+ Map<String, RepoProject> repos = parser.getProjects().stream().collect(
+ Collectors.toMap(RepoProject::getName, Function.identity()));
+ assertEquals(2, repos.size());
+
+ RepoProject foo = repos.get("foo");
+ assertEquals("foo", foo.getName());
+ assertEquals("branchX", foo.getDestBranch());
+
+ RepoProject bar = repos.get("bar");
+ assertEquals("bar", bar.getName());
+ assertNull(bar.getDestBranch());
+ }
+
void testNormalize(String in, String want) {
URI got = ManifestParser.normalizeEmptyPath(URI.create(in));
if (!got.toString().equals(want)) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java
index ca6f2e1..3162e79 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java
@@ -1171,6 +1171,94 @@ public void testRecordRemoteBranch() throws Exception {
}
}
+ @Test
+ public void testRecordRemoteBranch_pinned() throws Exception {
+ Repository remoteDb = createBareRepository();
+ Repository tempDb = createWorkRepository();
+
+ StringBuilder xmlContent = new StringBuilder();
+ xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+ .append("<manifest>")
+ .append("<remote name=\"remote1\" fetch=\".\" />")
+ .append("<default revision=\"master\" remote=\"remote1\" />")
+ .append("<project path=\"pin-noupstream\"")
+ .append(" name=\"pin-noupstream\"")
+ .append(" revision=\"76ce6d91a2e07fdfcbfc8df6970c9e98a98e36a0\" />")
+ .append("<project path=\"pin-upstream\"")
+ .append(" name=\"pin-upstream\"")
+ .append(" upstream=\"branchX\"")
+ .append(" revision=\"76ce6d91a2e07fdfcbfc8df6970c9e98a98e36a0\" />")
+ .append("</manifest>");
+ JGitTestUtil.writeTrashFile(tempDb, "manifest.xml",
+ xmlContent.toString());
+
+ RepoCommand command = new RepoCommand(remoteDb);
+ command.setPath(
+ tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
+ .setURI(rootUri).setRecordRemoteBranch(true).call();
+ // Clone it
+ File directory = createTempDirectory("testBareRepo");
+ try (Repository localDb = Git.cloneRepository().setDirectory(directory)
+ .setURI(remoteDb.getDirectory().toURI().toString()).call()
+ .getRepository();) {
+ // The .gitmodules file should exist
+ File gitmodules = new File(localDb.getWorkTree(), ".gitmodules");
+ assertTrue("The .gitmodules file should exist",
+ gitmodules.exists());
+ FileBasedConfig c = new FileBasedConfig(gitmodules, FS.DETECTED);
+ c.load();
+ assertEquals("Pinned submodule with upstream records the ref",
+ "branchX", c.getString("submodule", "pin-upstream", "ref"));
+ assertNull("Pinned submodule without upstream don't have ref",
+ c.getString("submodule", "pin-noupstream", "ref"));
+ }
+ }
+
+ @Test
+ public void testRecordRemoteBranch_pinned_nameConflict() throws Exception {
+ Repository remoteDb = createBareRepository();
+ Repository tempDb = createWorkRepository();
+
+ StringBuilder xmlContent = new StringBuilder();
+ xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+ .append("<manifest>")
+ .append("<remote name=\"remote1\" fetch=\".\" />")
+ .append("<default revision=\"master\" remote=\"remote1\" />")
+ .append("<project path=\"pin-upstream\"")
+ .append(" name=\"pin-upstream\"")
+ .append(" upstream=\"branchX\"")
+ .append(" revision=\"76ce6d91a2e07fdfcbfc8df6970c9e98a98e36a0\" />")
+ .append("<project path=\"pin-upstream-name-conflict\"")
+ .append(" name=\"pin-upstream\"")
+ .append(" upstream=\"branchX\"")
+ .append(" revision=\"76ce6d91a2e07fdfcbfc8df6970c9e98a98e36a0\" />")
+ .append("</manifest>");
+ JGitTestUtil.writeTrashFile(tempDb, "manifest.xml",
+ xmlContent.toString());
+
+ RepoCommand command = new RepoCommand(remoteDb);
+ command.setPath(
+ tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
+ .setURI(rootUri).setRecordRemoteBranch(true).call();
+ // Clone it
+ File directory = createTempDirectory("testBareRepo");
+ try (Repository localDb = Git.cloneRepository().setDirectory(directory)
+ .setURI(remoteDb.getDirectory().toURI().toString()).call()
+ .getRepository();) {
+ // The .gitmodules file should exist
+ File gitmodules = new File(localDb.getWorkTree(), ".gitmodules");
+ assertTrue("The .gitmodules file should exist",
+ gitmodules.exists());
+ FileBasedConfig c = new FileBasedConfig(gitmodules, FS.DETECTED);
+ c.load();
+ assertEquals("Upstream is preserved in name conflict", "branchX",
+ c.getString("submodule", "pin-upstream/pin-upstream",
+ "ref"));
+ assertEquals("Upstream is preserved in name conflict (other side)",
+ "branchX", c.getString("submodule",
+ "pin-upstream/pin-upstream-name-conflict", "ref"));
+ }
+ }
@Test
public void testRecordSubmoduleLabels() throws Exception {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphWriterTest.java
index 9f65ee2..80a0f0c 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphWriterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphWriterTest.java
@@ -10,6 +10,7 @@
package org.eclipse.jgit.internal.storage.commitgraph;
+import static java.util.stream.Collectors.toList;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.junit.Assert.assertArrayEquals;
@@ -19,8 +20,12 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
import java.util.Set;
import org.eclipse.jgit.dircache.DirCacheEntry;
@@ -413,6 +418,27 @@ public void testReuseBloomFilters() throws Exception {
"119,69,63,-8,0,"));
}
+ @Test
+ public void testPathDiffCalculator_skipUnchangedTree() throws Exception {
+ RevCommit root = tr.commit(tr.tree(
+ tr.file("d/sd1/f1", tr.blob("f1")),
+ tr.file("d/sd2/f2", tr.blob("f2"))));
+ RevCommit tip = tr.commit(tr.tree(
+ tr.file("d/sd1/f1", tr.blob("f1")),
+ tr.file("d/sd2/f2", tr.blob("f2B"))), root);
+ CommitGraphWriter.PathDiffCalculator c = new CommitGraphWriter.PathDiffCalculator();
+
+ Optional<HashSet<ByteBuffer>> byteBuffers = c.changedPaths(walk.getObjectReader(), tip);
+
+ assertTrue(byteBuffers.isPresent());
+ List<String> asString = byteBuffers.get().stream()
+ .map(b -> StandardCharsets.UTF_8.decode(b).toString())
+ .collect(toList());
+ assertThat(asString, containsInAnyOrder("d", "d/sd2", "d/sd2/f2"));
+ // We don't walk into d/sd1/f1
+ assertEquals(1, c.stepCounter);
+ }
+
RevCommit commit(RevCommit... parents) throws Exception {
return tr.commit(parents);
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/AggregatedBlockCacheStatsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/AggregatedBlockCacheStatsTest.java
new file mode 100644
index 0000000..2c4b432
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/AggregatedBlockCacheStatsTest.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2024, Google LLC and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.eclipse.jgit.internal.storage.dfs;
+
+import static org.eclipse.jgit.internal.storage.dfs.DfsBlockCacheTable.BlockCacheStats;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.Assert.assertArrayEquals;
+
+import java.util.List;
+
+import org.eclipse.jgit.internal.storage.pack.PackExt;
+import org.junit.Test;
+
+public class AggregatedBlockCacheStatsTest {
+ @Test
+ public void getName() {
+ BlockCacheStats aggregatedBlockCacheStats = AggregatedBlockCacheStats
+ .fromStatsList(List.of());
+
+ assertThat(aggregatedBlockCacheStats.getName(),
+ equalTo(AggregatedBlockCacheStats.class.getName()));
+ }
+
+ @Test
+ public void getCurrentSize_aggregatesCurrentSizes() {
+ long[] currentSizes = createEmptyStatsArray();
+
+ DfsBlockCacheStats packStats = new DfsBlockCacheStats();
+ packStats.addToLiveBytes(new TestKey(PackExt.PACK), 5);
+ currentSizes[PackExt.PACK.getPosition()] = 5;
+
+ DfsBlockCacheStats bitmapStats = new DfsBlockCacheStats();
+ bitmapStats.addToLiveBytes(new TestKey(PackExt.BITMAP_INDEX), 6);
+ currentSizes[PackExt.BITMAP_INDEX.getPosition()] = 6;
+
+ DfsBlockCacheStats indexStats = new DfsBlockCacheStats();
+ indexStats.addToLiveBytes(new TestKey(PackExt.INDEX), 7);
+ currentSizes[PackExt.INDEX.getPosition()] = 7;
+
+ BlockCacheStats aggregatedBlockCacheStats = AggregatedBlockCacheStats
+ .fromStatsList(List.of(packStats, bitmapStats, indexStats));
+
+ assertArrayEquals(aggregatedBlockCacheStats.getCurrentSize(),
+ currentSizes);
+ }
+
+ @Test
+ public void getHitCount_aggregatesHitCounts() {
+ long[] hitCounts = createEmptyStatsArray();
+
+ DfsBlockCacheStats packStats = new DfsBlockCacheStats();
+ incrementCounter(5,
+ () -> packStats.incrementHit(new TestKey(PackExt.PACK)));
+ hitCounts[PackExt.PACK.getPosition()] = 5;
+
+ DfsBlockCacheStats bitmapStats = new DfsBlockCacheStats();
+ incrementCounter(6, () -> bitmapStats
+ .incrementHit(new TestKey(PackExt.BITMAP_INDEX)));
+ hitCounts[PackExt.BITMAP_INDEX.getPosition()] = 6;
+
+ DfsBlockCacheStats indexStats = new DfsBlockCacheStats();
+ incrementCounter(7,
+ () -> indexStats.incrementHit(new TestKey(PackExt.INDEX)));
+ hitCounts[PackExt.INDEX.getPosition()] = 7;
+
+ BlockCacheStats aggregatedBlockCacheStats = AggregatedBlockCacheStats
+ .fromStatsList(List.of(packStats, bitmapStats, indexStats));
+
+ assertArrayEquals(aggregatedBlockCacheStats.getHitCount(), hitCounts);
+ }
+
+ @Test
+ public void getMissCount_aggregatesMissCounts() {
+ long[] missCounts = createEmptyStatsArray();
+
+ DfsBlockCacheStats packStats = new DfsBlockCacheStats();
+ incrementCounter(5,
+ () -> packStats.incrementMiss(new TestKey(PackExt.PACK)));
+ missCounts[PackExt.PACK.getPosition()] = 5;
+
+ DfsBlockCacheStats bitmapStats = new DfsBlockCacheStats();
+ incrementCounter(6, () -> bitmapStats
+ .incrementMiss(new TestKey(PackExt.BITMAP_INDEX)));
+ missCounts[PackExt.BITMAP_INDEX.getPosition()] = 6;
+
+ DfsBlockCacheStats indexStats = new DfsBlockCacheStats();
+ incrementCounter(7,
+ () -> indexStats.incrementMiss(new TestKey(PackExt.INDEX)));
+ missCounts[PackExt.INDEX.getPosition()] = 7;
+
+ BlockCacheStats aggregatedBlockCacheStats = AggregatedBlockCacheStats
+ .fromStatsList(List.of(packStats, bitmapStats, indexStats));
+
+ assertArrayEquals(aggregatedBlockCacheStats.getMissCount(), missCounts);
+ }
+
+ @Test
+ public void getTotalRequestCount_aggregatesRequestCounts() {
+ long[] totalRequestCounts = createEmptyStatsArray();
+
+ DfsBlockCacheStats packStats = new DfsBlockCacheStats();
+ incrementCounter(5, () -> {
+ packStats.incrementHit(new TestKey(PackExt.PACK));
+ packStats.incrementMiss(new TestKey(PackExt.PACK));
+ });
+ totalRequestCounts[PackExt.PACK.getPosition()] = 10;
+
+ DfsBlockCacheStats bitmapStats = new DfsBlockCacheStats();
+ incrementCounter(6, () -> {
+ bitmapStats.incrementHit(new TestKey(PackExt.BITMAP_INDEX));
+ bitmapStats.incrementMiss(new TestKey(PackExt.BITMAP_INDEX));
+ });
+ totalRequestCounts[PackExt.BITMAP_INDEX.getPosition()] = 12;
+
+ DfsBlockCacheStats indexStats = new DfsBlockCacheStats();
+ incrementCounter(7, () -> {
+ indexStats.incrementHit(new TestKey(PackExt.INDEX));
+ indexStats.incrementMiss(new TestKey(PackExt.INDEX));
+ });
+ totalRequestCounts[PackExt.INDEX.getPosition()] = 14;
+
+ BlockCacheStats aggregatedBlockCacheStats = AggregatedBlockCacheStats
+ .fromStatsList(List.of(packStats, bitmapStats, indexStats));
+
+ assertArrayEquals(aggregatedBlockCacheStats.getTotalRequestCount(),
+ totalRequestCounts);
+ }
+
+ @Test
+ public void getHitRatio_aggregatesHitRatios() {
+ long[] hitRatios = createEmptyStatsArray();
+
+ DfsBlockCacheStats packStats = new DfsBlockCacheStats();
+ incrementCounter(5,
+ () -> packStats.incrementHit(new TestKey(PackExt.PACK)));
+ hitRatios[PackExt.PACK.getPosition()] = 100;
+
+ DfsBlockCacheStats bitmapStats = new DfsBlockCacheStats();
+ incrementCounter(6, () -> {
+ bitmapStats.incrementHit(new TestKey(PackExt.BITMAP_INDEX));
+ bitmapStats.incrementMiss(new TestKey(PackExt.BITMAP_INDEX));
+ });
+ hitRatios[PackExt.BITMAP_INDEX.getPosition()] = 50;
+
+ DfsBlockCacheStats indexStats = new DfsBlockCacheStats();
+ incrementCounter(7,
+ () -> indexStats.incrementMiss(new TestKey(PackExt.INDEX)));
+ hitRatios[PackExt.INDEX.getPosition()] = 0;
+
+ BlockCacheStats aggregatedBlockCacheStats = AggregatedBlockCacheStats
+ .fromStatsList(List.of(packStats, bitmapStats, indexStats));
+
+ assertArrayEquals(aggregatedBlockCacheStats.getHitRatio(), hitRatios);
+ }
+
+ @Test
+ public void getEvictions_aggregatesEvictions() {
+ long[] evictions = createEmptyStatsArray();
+
+ DfsBlockCacheStats packStats = new DfsBlockCacheStats();
+ incrementCounter(5,
+ () -> packStats.incrementEvict(new TestKey(PackExt.PACK)));
+ evictions[PackExt.PACK.getPosition()] = 5;
+
+ DfsBlockCacheStats bitmapStats = new DfsBlockCacheStats();
+ incrementCounter(6, () -> bitmapStats
+ .incrementEvict(new TestKey(PackExt.BITMAP_INDEX)));
+ evictions[PackExt.BITMAP_INDEX.getPosition()] = 6;
+
+ DfsBlockCacheStats indexStats = new DfsBlockCacheStats();
+ incrementCounter(7,
+ () -> indexStats.incrementEvict(new TestKey(PackExt.INDEX)));
+ evictions[PackExt.INDEX.getPosition()] = 7;
+
+ BlockCacheStats aggregatedBlockCacheStats = AggregatedBlockCacheStats
+ .fromStatsList(List.of(packStats, bitmapStats, indexStats));
+
+ assertArrayEquals(aggregatedBlockCacheStats.getEvictions(), evictions);
+ }
+
+ private static void incrementCounter(int amount, Runnable fn) {
+ for (int i = 0; i < amount; i++) {
+ fn.run();
+ }
+ }
+
+ private static long[] createEmptyStatsArray() {
+ return new long[PackExt.values().length];
+ }
+
+ private static class TestKey extends DfsStreamKey {
+ TestKey(PackExt packExt) {
+ super(0, packExt);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/ClockBlockCacheTableTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/ClockBlockCacheTableTest.java
new file mode 100644
index 0000000..2e2f86b
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/ClockBlockCacheTableTest.java
@@ -0,0 +1,67 @@
+package org.eclipse.jgit.internal.storage.dfs;
+
+import static org.eclipse.jgit.internal.storage.dfs.DfsBlockCacheConfig.DEFAULT_NAME;
+import static org.eclipse.jgit.internal.storage.dfs.DfsBlockCacheTable.BlockCacheStats;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.isA;
+
+import java.util.List;
+
+import org.junit.Test;
+
+public class ClockBlockCacheTableTest {
+ private static final String NAME = "name";
+
+ @Test
+ public void getName_nameNotConfigured_returnsDefaultName() {
+ ClockBlockCacheTable cacheTable = new ClockBlockCacheTable(
+ createBlockCacheConfig());
+
+ assertThat(cacheTable.getName(), equalTo(DEFAULT_NAME));
+ }
+
+ @Test
+ public void getName_nameConfigured_returnsConfiguredName() {
+ ClockBlockCacheTable cacheTable = new ClockBlockCacheTable(
+ createBlockCacheConfig().setName(NAME));
+
+ assertThat(cacheTable.getName(), equalTo(NAME));
+ }
+
+ @Test
+ public void getBlockCacheStats_nameNotConfigured_returnsBlockCacheStatsWithDefaultName() {
+ ClockBlockCacheTable cacheTable = new ClockBlockCacheTable(
+ createBlockCacheConfig());
+
+ assertThat(cacheTable.getBlockCacheStats(), hasSize(1));
+ assertThat(cacheTable.getBlockCacheStats().get(0).getName(),
+ equalTo(DEFAULT_NAME));
+ }
+
+ @Test
+ public void getBlockCacheStats_nameConfigured_returnsBlockCacheStatsWithConfiguredName() {
+ ClockBlockCacheTable cacheTable = new ClockBlockCacheTable(
+ createBlockCacheConfig().setName(NAME));
+
+ assertThat(cacheTable.getBlockCacheStats(), hasSize(1));
+ assertThat(cacheTable.getBlockCacheStats().get(0).getName(),
+ equalTo(NAME));
+ }
+
+ @Test
+ public void getAllBlockCacheStats() {
+ ClockBlockCacheTable cacheTable = new ClockBlockCacheTable(
+ createBlockCacheConfig());
+
+ List<BlockCacheStats> blockCacheStats = cacheTable.getBlockCacheStats();
+ assertThat(blockCacheStats, contains(isA(BlockCacheStats.class)));
+ }
+
+ private static DfsBlockCacheConfig createBlockCacheConfig() {
+ return new DfsBlockCacheConfig().setBlockSize(512)
+ .setConcurrencyLevel(4).setBlockLimit(1024);
+ }
+}
\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfigTest.java
index 2df0ba1..afa3179 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfigTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfigTest.java
@@ -38,13 +38,37 @@
package org.eclipse.jgit.internal.storage.dfs;
+import static org.eclipse.jgit.internal.storage.dfs.DfsBlockCacheConfig.DEFAULT_NAME;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_CORE_SECTION;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_DFS_CACHE_PREFIX;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_DFS_SECTION;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_BLOCK_LIMIT;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_BLOCK_SIZE;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_CONCURRENCY_LEVEL;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_PACK_EXTENSIONS;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_STREAM_RATIO;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.closeTo;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThrows;
+import java.io.ByteArrayOutputStream;
+import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.dfs.DfsBlockCacheConfig.DfsBlockCachePackExtConfig;
+import org.eclipse.jgit.internal.storage.pack.PackExt;
+import org.eclipse.jgit.lib.Config;
import org.junit.Test;
+@SuppressWarnings("boxing")
public class DfsBlockCacheConfigTest {
@Test
@@ -55,7 +79,6 @@ public void blockSizeNotPowerOfTwoExpectsException() {
}
@Test
- @SuppressWarnings("boxing")
public void negativeBlockSizeIsConvertedToDefault() {
DfsBlockCacheConfig config = new DfsBlockCacheConfig();
config.setBlockSize(-1);
@@ -64,7 +87,6 @@ public void negativeBlockSizeIsConvertedToDefault() {
}
@Test
- @SuppressWarnings("boxing")
public void tooSmallBlockSizeIsConvertedToDefault() {
DfsBlockCacheConfig config = new DfsBlockCacheConfig();
config.setBlockSize(10);
@@ -73,11 +95,295 @@ public void tooSmallBlockSizeIsConvertedToDefault() {
}
@Test
- @SuppressWarnings("boxing")
public void validBlockSize() {
DfsBlockCacheConfig config = new DfsBlockCacheConfig();
config.setBlockSize(65536);
assertThat(config.getBlockSize(), is(65536));
}
+
+ @Test
+ public void fromConfigs() {
+ Config config = new Config();
+ config.setLong(CONFIG_CORE_SECTION, CONFIG_DFS_SECTION,
+ CONFIG_KEY_BLOCK_LIMIT, 50 * 1024);
+ config.setInt(CONFIG_CORE_SECTION, CONFIG_DFS_SECTION,
+ CONFIG_KEY_BLOCK_SIZE, 1024);
+ config.setInt(CONFIG_CORE_SECTION, CONFIG_DFS_SECTION,
+ CONFIG_KEY_CONCURRENCY_LEVEL, 3);
+ config.setString(CONFIG_CORE_SECTION, CONFIG_DFS_SECTION,
+ CONFIG_KEY_STREAM_RATIO, "0.5");
+
+ DfsBlockCacheConfig cacheConfig = new DfsBlockCacheConfig()
+ .fromConfig(config);
+ assertThat(cacheConfig.getBlockLimit(), is(50L * 1024L));
+ assertThat(cacheConfig.getBlockSize(), is(1024));
+ assertThat(cacheConfig.getConcurrencyLevel(), is(3));
+ assertThat(cacheConfig.getStreamRatio(), closeTo(0.5, 0.0001));
+ }
+
+ @Test
+ public void fromConfig_blockLimitNotAMultipleOfBlockSize_throws() {
+ Config config = new Config();
+ config.setLong(CONFIG_CORE_SECTION, CONFIG_DFS_SECTION,
+ CONFIG_KEY_BLOCK_LIMIT, 1025);
+ config.setInt(CONFIG_CORE_SECTION, CONFIG_DFS_SECTION,
+ CONFIG_KEY_BLOCK_SIZE, 1024);
+
+ assertThrows(IllegalArgumentException.class,
+ () -> new DfsBlockCacheConfig().fromConfig(config));
+ }
+
+ @Test
+ public void fromConfig_streamRatioInvalidFormat_throws() {
+ Config config = new Config();
+ config.setString(CONFIG_CORE_SECTION, CONFIG_DFS_SECTION,
+ CONFIG_KEY_STREAM_RATIO, "0.a5");
+
+ assertThrows(IllegalArgumentException.class,
+ () -> new DfsBlockCacheConfig().fromConfig(config));
+ }
+
+ @Test
+ public void fromConfig_generatesDfsBlockCachePackExtConfigs() {
+ Config config = new Config();
+ addPackExtConfigEntry(config, "pack", List.of(PackExt.PACK),
+ /* blockLimit= */ 20 * 512, /* blockSize= */ 512);
+
+ addPackExtConfigEntry(config, "bitmap", List.of(PackExt.BITMAP_INDEX),
+ /* blockLimit= */ 25 * 1024, /* blockSize= */ 1024);
+
+ addPackExtConfigEntry(config, "index",
+ List.of(PackExt.INDEX, PackExt.OBJECT_SIZE_INDEX,
+ PackExt.REVERSE_INDEX),
+ /* blockLimit= */ 30 * 1024, /* blockSize= */ 1024);
+
+ DfsBlockCacheConfig cacheConfig = new DfsBlockCacheConfig()
+ .fromConfig(config);
+ var configs = cacheConfig.getPackExtCacheConfigurations();
+ assertThat(configs, hasSize(3));
+ var packConfig = getConfigForExt(configs, PackExt.PACK);
+ assertThat(packConfig.getBlockLimit(), is(20L * 512L));
+ assertThat(packConfig.getBlockSize(), is(512));
+
+ var bitmapConfig = getConfigForExt(configs, PackExt.BITMAP_INDEX);
+ assertThat(bitmapConfig.getBlockLimit(), is(25L * 1024L));
+ assertThat(bitmapConfig.getBlockSize(), is(1024));
+
+ var indexConfig = getConfigForExt(configs, PackExt.INDEX);
+ assertThat(indexConfig.getBlockLimit(), is(30L * 1024L));
+ assertThat(indexConfig.getBlockSize(), is(1024));
+ assertThat(getConfigForExt(configs, PackExt.OBJECT_SIZE_INDEX),
+ is(indexConfig));
+ assertThat(getConfigForExt(configs, PackExt.REVERSE_INDEX),
+ is(indexConfig));
+ }
+
+ @Test
+ public void fromConfig_withExistingCacheHotMap_configWithPackExtConfigsHasHotMaps() {
+ Config config = new Config();
+ addPackExtConfigEntry(config, "pack", List.of(PackExt.PACK),
+ /* blockLimit= */ 20 * 512, /* blockSize= */ 512);
+
+ addPackExtConfigEntry(config, "bitmap", List.of(PackExt.BITMAP_INDEX),
+ /* blockLimit= */ 25 * 1024, /* blockSize= */ 1024);
+
+ addPackExtConfigEntry(config, "index",
+ List.of(PackExt.INDEX, PackExt.OBJECT_SIZE_INDEX,
+ PackExt.REVERSE_INDEX),
+ /* blockLimit= */ 30 * 1024, /* blockSize= */ 1024);
+
+ Map<PackExt, Integer> cacheHotMap = Map.of(PackExt.PACK, 1,
+ PackExt.BITMAP_INDEX, 2, PackExt.INDEX, 3, PackExt.REFTABLE, 4);
+
+ DfsBlockCacheConfig cacheConfig = new DfsBlockCacheConfig();
+ cacheConfig.setCacheHotMap(cacheHotMap);
+ cacheConfig.fromConfig(config);
+
+ var configs = cacheConfig.getPackExtCacheConfigurations();
+ assertThat(cacheConfig.getCacheHotMap(), is(cacheHotMap));
+ assertThat(configs, hasSize(3));
+ var packConfig = getConfigForExt(configs, PackExt.PACK);
+ assertThat(packConfig.getCacheHotMap(), is(Map.of(PackExt.PACK, 1)));
+
+ var bitmapConfig = getConfigForExt(configs, PackExt.BITMAP_INDEX);
+ assertThat(bitmapConfig.getCacheHotMap(),
+ is(Map.of(PackExt.BITMAP_INDEX, 2)));
+
+ var indexConfig = getConfigForExt(configs, PackExt.INDEX);
+ assertThat(indexConfig.getCacheHotMap(), is(Map.of(PackExt.INDEX, 3)));
+ }
+
+ @Test
+ public void setCacheHotMap_configWithPackExtConfigs_setsHotMaps() {
+ Config config = new Config();
+ addPackExtConfigEntry(config, "pack", List.of(PackExt.PACK),
+ /* blockLimit= */ 20 * 512, /* blockSize= */ 512);
+
+ addPackExtConfigEntry(config, "bitmap", List.of(PackExt.BITMAP_INDEX),
+ /* blockLimit= */ 25 * 1024, /* blockSize= */ 1024);
+
+ addPackExtConfigEntry(config, "index",
+ List.of(PackExt.INDEX, PackExt.OBJECT_SIZE_INDEX,
+ PackExt.REVERSE_INDEX),
+ /* blockLimit= */ 30 * 1024, /* blockSize= */ 1024);
+
+ Map<PackExt, Integer> cacheHotMap = Map.of(PackExt.PACK, 1,
+ PackExt.BITMAP_INDEX, 2, PackExt.INDEX, 3, PackExt.REFTABLE, 4);
+
+ DfsBlockCacheConfig cacheConfig = new DfsBlockCacheConfig()
+ .fromConfig(config);
+ cacheConfig.setCacheHotMap(cacheHotMap);
+
+ var configs = cacheConfig.getPackExtCacheConfigurations();
+ assertThat(cacheConfig.getCacheHotMap(), is(cacheHotMap));
+ assertThat(configs, hasSize(3));
+ var packConfig = getConfigForExt(configs, PackExt.PACK);
+ assertThat(packConfig.getCacheHotMap(), is(Map.of(PackExt.PACK, 1)));
+
+ var bitmapConfig = getConfigForExt(configs, PackExt.BITMAP_INDEX);
+ assertThat(bitmapConfig.getCacheHotMap(),
+ is(Map.of(PackExt.BITMAP_INDEX, 2)));
+
+ var indexConfig = getConfigForExt(configs, PackExt.INDEX);
+ assertThat(indexConfig.getCacheHotMap(), is(Map.of(PackExt.INDEX, 3)));
+ }
+
+ @Test
+ public void fromConfigs_baseConfigOnly_nameSetFromConfigDfsSubSection() {
+ Config config = new Config();
+
+ DfsBlockCacheConfig blockCacheConfig = new DfsBlockCacheConfig()
+ .fromConfig(config);
+ assertThat(blockCacheConfig.getName(), equalTo(DEFAULT_NAME));
+ }
+
+ @Test
+ public void fromConfigs_namesSetFromConfigDfsCachePrefixSubSections() {
+ Config config = new Config();
+ config.setString(CONFIG_CORE_SECTION, CONFIG_DFS_SECTION,
+ CONFIG_KEY_STREAM_RATIO, "0.5");
+ config.setString(CONFIG_CORE_SECTION, CONFIG_DFS_CACHE_PREFIX + "name1",
+ CONFIG_KEY_PACK_EXTENSIONS, PackExt.PACK.name());
+ config.setString(CONFIG_CORE_SECTION, CONFIG_DFS_CACHE_PREFIX + "name2",
+ CONFIG_KEY_PACK_EXTENSIONS, PackExt.BITMAP_INDEX.name());
+
+ DfsBlockCacheConfig blockCacheConfig = new DfsBlockCacheConfig()
+ .fromConfig(config);
+ assertThat(blockCacheConfig.getName(), equalTo("dfs"));
+ assertThat(
+ blockCacheConfig.getPackExtCacheConfigurations().get(0)
+ .getPackExtCacheConfiguration().getName(),
+ equalTo("dfs.name1"));
+ assertThat(
+ blockCacheConfig.getPackExtCacheConfigurations().get(1)
+ .getPackExtCacheConfiguration().getName(),
+ equalTo("dfs.name2"));
+ }
+
+ @Test
+ public void fromConfigs_dfsBlockCachePackExtConfigWithDuplicateExtensions_throws() {
+ Config config = new Config();
+ config.setString(CONFIG_CORE_SECTION, CONFIG_DFS_CACHE_PREFIX + "pack1",
+ CONFIG_KEY_PACK_EXTENSIONS, PackExt.PACK.name());
+
+ config.setString(CONFIG_CORE_SECTION, CONFIG_DFS_CACHE_PREFIX + "pack2",
+ CONFIG_KEY_PACK_EXTENSIONS, PackExt.PACK.name());
+
+ assertThrows(IllegalArgumentException.class,
+ () -> new DfsBlockCacheConfig().fromConfig(config));
+ }
+
+ @Test
+ public void fromConfigs_dfsBlockCachePackExtConfigWithEmptyExtensions_throws() {
+ Config config = new Config();
+ config.setString(CONFIG_CORE_SECTION, CONFIG_DFS_CACHE_PREFIX + "pack1",
+ CONFIG_KEY_PACK_EXTENSIONS, "");
+
+ assertThrows(IllegalArgumentException.class,
+ () -> new DfsBlockCacheConfig().fromConfig(config));
+ }
+
+ @Test
+ public void fromConfigs_dfsBlockCachePackExtConfigWithNoExtensions_throws() {
+ Config config = new Config();
+ config.setInt(CONFIG_CORE_SECTION, CONFIG_DFS_CACHE_PREFIX + "pack1",
+ CONFIG_KEY_BLOCK_SIZE, 0);
+
+ assertThrows(IllegalArgumentException.class,
+ () -> new DfsBlockCacheConfig().fromConfig(config));
+ }
+
+ @Test
+ public void fromConfigs_dfsBlockCachePackExtConfigWithUnknownExtensions_throws() {
+ Config config = new Config();
+ config.setString(CONFIG_CORE_SECTION,
+ CONFIG_DFS_CACHE_PREFIX + "unknownExt",
+ CONFIG_KEY_PACK_EXTENSIONS, "NotAKnownExt");
+
+ assertThrows(IllegalArgumentException.class,
+ () -> new DfsBlockCacheConfig().fromConfig(config));
+ }
+
+ @Test
+ public void writeConfigurationDebug_writesConfigsToWriter()
+ throws Exception {
+ Config config = new Config();
+ config.setLong(CONFIG_CORE_SECTION, CONFIG_DFS_SECTION,
+ CONFIG_KEY_BLOCK_LIMIT, 50 * 1024);
+ config.setInt(CONFIG_CORE_SECTION, CONFIG_DFS_SECTION,
+ CONFIG_KEY_BLOCK_SIZE, 1024);
+ config.setInt(CONFIG_CORE_SECTION, CONFIG_DFS_SECTION,
+ CONFIG_KEY_CONCURRENCY_LEVEL, 3);
+ config.setString(CONFIG_CORE_SECTION, CONFIG_DFS_SECTION,
+ CONFIG_KEY_STREAM_RATIO, "0.5");
+ addPackExtConfigEntry(config, "pack", List.of(PackExt.PACK),
+ /* blockLimit= */ 20 * 512, /* blockSize= */ 512);
+
+ DfsBlockCacheConfig cacheConfig = new DfsBlockCacheConfig()
+ .fromConfig(config);
+ Map<PackExt, Integer> hotmap = Map.of(PackExt.PACK, 10);
+ cacheConfig.setCacheHotMap(hotmap);
+
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ cacheConfig.print(new PrintWriter(byteArrayOutputStream, true,
+ StandardCharsets.UTF_8));
+
+ String writenConfig = byteArrayOutputStream
+ .toString(StandardCharsets.UTF_8);
+
+ List<String> writenLines = Arrays.asList(writenConfig.split("\n"));
+ assertThat(writenLines,
+ equalTo(List.of("Name: dfs", " BlockLimit: " + (50 * 1024),
+ " BlockSize: 1024", " StreamRatio: 0.5",
+ " ConcurrencyLevel: 3",
+ " CacheHotMapEntry: " + PackExt.PACK + " : " + 10,
+ " Name: dfs.pack", " BlockLimit: " + 20 * 512,
+ " BlockSize: 512", " StreamRatio: 0.3",
+ " ConcurrencyLevel: 32",
+ " CacheHotMapEntry: " + PackExt.PACK + " : " + 10,
+ " PackExts: " + List.of(PackExt.PACK))));
+ }
+
+ private static void addPackExtConfigEntry(Config config, String configName,
+ List<PackExt> packExts, long blockLimit, int blockSize) {
+ String packExtConfigName = CONFIG_DFS_CACHE_PREFIX + configName;
+ config.setString(CONFIG_CORE_SECTION, packExtConfigName,
+ CONFIG_KEY_PACK_EXTENSIONS, packExts.stream().map(PackExt::name)
+ .collect(Collectors.joining(" ")));
+ config.setLong(CONFIG_CORE_SECTION, packExtConfigName,
+ CONFIG_KEY_BLOCK_LIMIT, blockLimit);
+ config.setInt(CONFIG_CORE_SECTION, packExtConfigName,
+ CONFIG_KEY_BLOCK_SIZE, blockSize);
+ }
+
+ private static DfsBlockCacheConfig getConfigForExt(
+ List<DfsBlockCachePackExtConfig> configs, PackExt packExt) {
+ for (DfsBlockCachePackExtConfig config : configs) {
+ if (config.getPackExts().contains(packExt)) {
+ return config.getPackExtCacheConfiguration();
+ }
+ }
+ return null;
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheTest.java
index fef0563..3c7cc07 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheTest.java
@@ -13,20 +13,24 @@
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.time.Duration;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.stream.LongStream;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.LongStream;
+import org.eclipse.jgit.internal.storage.dfs.DfsBlockCacheConfig.DfsBlockCachePackExtConfig;
import org.eclipse.jgit.internal.storage.dfs.DfsBlockCacheConfig.IndexEventConsumer;
import org.eclipse.jgit.internal.storage.pack.PackExt;
import org.eclipse.jgit.junit.TestRepository;
@@ -39,14 +43,35 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+@RunWith(Parameterized.class)
public class DfsBlockCacheTest {
@Rule
public TestName testName = new TestName();
+
private TestRng rng;
+
private DfsBlockCache cache;
+
private ExecutorService pool;
+ private enum CacheType {
+ SINGLE_TABLE_CLOCK_BLOCK_CACHE, EXT_SPLIT_TABLE_CLOCK_BLOCK_CACHE
+ }
+
+ @Parameters(name = "cache type: {0}")
+ public static Iterable<? extends Object> data() {
+ return Arrays.asList(CacheType.SINGLE_TABLE_CLOCK_BLOCK_CACHE,
+ CacheType.EXT_SPLIT_TABLE_CLOCK_BLOCK_CACHE);
+ }
+
+ @Parameter
+ public CacheType cacheType;
+
@Before
public void setUp() {
rng = new TestRng(testName.getMethodName());
@@ -448,8 +473,28 @@ private void resetCache() {
}
private void resetCache(int concurrencyLevel) {
- DfsBlockCache.reconfigure(new DfsBlockCacheConfig().setBlockSize(512)
- .setConcurrencyLevel(concurrencyLevel).setBlockLimit(1 << 20));
+ DfsBlockCacheConfig cacheConfig = new DfsBlockCacheConfig()
+ .setBlockSize(512).setConcurrencyLevel(concurrencyLevel)
+ .setBlockLimit(1 << 20);
+ switch (cacheType) {
+ case SINGLE_TABLE_CLOCK_BLOCK_CACHE:
+ // SINGLE_TABLE_CLOCK_BLOCK_CACHE doesn't modify the config.
+ break;
+ case EXT_SPLIT_TABLE_CLOCK_BLOCK_CACHE:
+ List<DfsBlockCachePackExtConfig> packExtCacheConfigs = new ArrayList<>();
+ for (PackExt packExt : PackExt.values()) {
+ DfsBlockCacheConfig extCacheConfig = new DfsBlockCacheConfig()
+ .setBlockSize(512).setConcurrencyLevel(concurrencyLevel)
+ .setBlockLimit(1 << 20)
+ .setPackExtCacheConfigurations(packExtCacheConfigs);
+ packExtCacheConfigs.add(new DfsBlockCachePackExtConfig(
+ EnumSet.of(packExt), extCacheConfig));
+ }
+ cacheConfig.setPackExtCacheConfigurations(packExtCacheConfigs);
+ break;
+ }
+ assertNotNull(cacheConfig);
+ DfsBlockCache.reconfigure(cacheConfig);
cache = DfsBlockCache.getInstance();
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollectorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollectorTest.java
index e193de9..00a3760 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollectorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollectorTest.java
@@ -6,6 +6,7 @@
import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.UNREACHABLE_GARBAGE;
import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
import static org.eclipse.jgit.internal.storage.pack.PackExt.REFTABLE;
+import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -15,14 +16,18 @@
import static org.junit.Assert.fail;
import java.io.IOException;
+import java.time.Instant;
+import java.time.ZoneOffset;
import java.util.Arrays;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
+
import org.eclipse.jgit.internal.storage.commitgraph.CommitGraph;
import org.eclipse.jgit.internal.storage.commitgraph.CommitGraphWriter;
import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource;
import org.eclipse.jgit.internal.storage.file.PackBitmapIndex;
import org.eclipse.jgit.internal.storage.pack.PackExt;
+import org.eclipse.jgit.internal.storage.reftable.LogCursor;
import org.eclipse.jgit.internal.storage.reftable.RefCursor;
import org.eclipse.jgit.internal.storage.reftable.ReftableConfig;
import org.eclipse.jgit.internal.storage.reftable.ReftableReader;
@@ -36,6 +41,7 @@
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdRef;
+import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevBlob;
@@ -43,6 +49,7 @@
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.storage.pack.PackConfig;
import org.eclipse.jgit.transport.ReceiveCommand;
+import org.eclipse.jgit.util.GitTimeParser;
import org.eclipse.jgit.util.SystemReader;
import org.junit.After;
import org.junit.Before;
@@ -1171,6 +1178,7 @@ public void objectSizeIdx_reachableBlob_bigEnough_indexed() throws Exception {
gcWithObjectSizeIndex(10);
+ odb.getReaderOptions().setUseObjectSizeIndex(true);
DfsReader reader = odb.newReader();
DfsPackFile gcPack = findFirstBySource(odb.getPacks(), GC);
assertTrue(gcPack.hasObjectSizeIndex(reader));
@@ -1191,6 +1199,7 @@ public void objectSizeIdx_reachableBlob_tooSmall_notIndexed() throws Exception {
gcWithObjectSizeIndex(10);
+ odb.getReaderOptions().setUseObjectSizeIndex(true);
DfsReader reader = odb.newReader();
DfsPackFile gcPack = findFirstBySource(odb.getPacks(), GC);
assertTrue(gcPack.hasObjectSizeIndex(reader));
@@ -1272,6 +1281,87 @@ public void bitmapIndexWrittenDuringGc() throws Exception {
bitmapIndex.getXorBitmapCount() > 0);
}
+ @Test
+ public void gitGCWithRefLogExpire() throws Exception {
+ String master = "refs/heads/master";
+ RevCommit commit0 = commit().message("0").create();
+ RevCommit commit1 = commit().message("1").parent(commit0).create();
+ git.update(master, commit1);
+ DfsGarbageCollector gc = new DfsGarbageCollector(repo);
+ gc.setReftableConfig(new ReftableConfig());
+ run(gc);
+ DfsPackDescription t1 = odb.newPack(INSERT);
+ Ref next = new ObjectIdRef.PeeledNonTag(Ref.Storage.LOOSE,
+ "refs/heads/next", commit0.copy());
+ Instant currentDay = Instant.now();
+ Instant ten_days_ago = GitTimeParser.parseInstant("10 days ago");
+ Instant twenty_days_ago = GitTimeParser.parseInstant("20 days ago");
+ Instant thirty_days_ago = GitTimeParser.parseInstant("30 days ago");
+ Instant fifty_days_ago = GitTimeParser.parseInstant("50 days ago");
+ final ZoneOffset offset = ZoneOffset.ofHours(-8);
+ PersonIdent who2 = new PersonIdent("J.Author", "authemail", currentDay,
+ offset);
+ PersonIdent who3 = new PersonIdent("J.Author", "authemail",
+ ten_days_ago, offset);
+ PersonIdent who4 = new PersonIdent("J.Author", "authemail",
+ twenty_days_ago, offset);
+ PersonIdent who5 = new PersonIdent("J.Author", "authemail",
+ thirty_days_ago, offset);
+ PersonIdent who6 = new PersonIdent("J.Author", "authemail",
+ fifty_days_ago, offset);
+
+ try (DfsOutputStream out = odb.writeFile(t1, REFTABLE)) {
+ ReftableWriter w = new ReftableWriter(out);
+ w.setMinUpdateIndex(42);
+ w.setMaxUpdateIndex(42);
+ w.begin();
+ w.sortAndWriteRefs(Collections.singleton(next));
+ w.writeLog("refs/heads/branch", 1, who2, ObjectId.zeroId(),id(2), "Branch Message");
+ w.writeLog("refs/heads/branch1", 2, who3, ObjectId.zeroId(),id(3), "Branch Message1");
+ w.writeLog("refs/heads/branch2", 2, who4, ObjectId.zeroId(),id(4), "Branch Message2");
+ w.writeLog("refs/heads/branch3", 2, who5, ObjectId.zeroId(),id(5), "Branch Message3");
+ w.writeLog("refs/heads/branch4", 2, who6, ObjectId.zeroId(),id(6), "Branch Message4");
+ w.finish();
+ t1.addFileExt(REFTABLE);
+ t1.setReftableStats(w.getStats());
+ }
+ odb.commitPack(Collections.singleton(t1), null);
+
+ gc = new DfsGarbageCollector(repo);
+ gc.setReftableConfig(new ReftableConfig());
+ // Expire ref log entries older than 30 days
+ gc.setRefLogExpire(thirty_days_ago);
+ run(gc);
+
+ // Single GC pack present with all objects.
+ assertEquals(1, odb.getPacks().length);
+ DfsPackFile pack = odb.getPacks()[0];
+ DfsPackDescription desc = pack.getPackDescription();
+
+ DfsReftable table = new DfsReftable(DfsBlockCache.getInstance(), desc);
+ try (DfsReader ctx = odb.newReader();
+ ReftableReader rr = table.open(ctx);
+ RefCursor rc = rr.allRefs();
+ LogCursor lc = rr.allLogs()) {
+ assertTrue(rc.next());
+ assertEquals(master, rc.getRef().getName());
+ assertEquals(commit1, rc.getRef().getObjectId());
+ assertTrue(rc.next());
+ assertEquals(next.getName(), rc.getRef().getName());
+ assertEquals(commit0, rc.getRef().getObjectId());
+ assertFalse(rc.next());
+ assertTrue(lc.next());
+ assertEquals(lc.getRefName(),"refs/heads/branch");
+ assertTrue(lc.next());
+ assertEquals(lc.getRefName(),"refs/heads/branch1");
+ assertTrue(lc.next());
+ assertEquals(lc.getRefName(),"refs/heads/branch2");
+ // Old entries are purged
+ assertFalse(lc.next());
+ }
+ }
+
+
private RevCommit commitChain(RevCommit parent, int length)
throws Exception {
for (int i = 0; i < length; i++) {
@@ -1361,4 +1451,12 @@ private int countPacks(PackSource source) throws IOException {
}
return cnt;
}
+ private static ObjectId id(int i) {
+ byte[] buf = new byte[OBJECT_ID_LENGTH];
+ buf[0] = (byte) (i & 0xff);
+ buf[1] = (byte) ((i >>> 8) & 0xff);
+ buf[2] = (byte) ((i >>> 16) & 0xff);
+ buf[3] = (byte) (i >>> 24);
+ return ObjectId.fromRaw(buf);
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsInserterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsInserterTest.java
index b84a0b0..0b558ed 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsInserterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsInserterTest.java
@@ -295,6 +295,7 @@ public void testObjectSizePopulated() throws IOException {
public void testObjectSizeIndexOnInsert() throws IOException {
db.getConfig().setInt(CONFIG_PACK_SECTION, null,
CONFIG_KEY_MIN_BYTES_OBJ_SIZE_INDEX, 0);
+ db.getObjectDatabase().getReaderOptions().setUseObjectSizeIndex(true);
byte[] contents = Constants.encode("foo");
ObjectId fooId;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackCompacterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackCompacterTest.java
index c516e30..c3b6aa8 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackCompacterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackCompacterTest.java
@@ -12,13 +12,18 @@
import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.COMPACT;
import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.INSERT;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.OBJECT_SIZE_INDEX;
import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
+import java.util.Arrays;
+import java.util.Optional;
import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.revwalk.RevCommit;
import org.junit.Before;
import org.junit.Test;
@@ -98,6 +103,40 @@ public void testEstimateGcPackSizeWithAnExistingGcPack() throws Exception {
pack.getPackDescription().getEstimatedPackSize());
}
+ @Test
+ public void testObjectSizeIndexWritten() throws Exception {
+ writeObjectSizeIndex(repo, true);
+ RevCommit commit0 = commit().message("0").create();
+ RevCommit commit1 = commit().message("1").parent(commit0).create();
+ git.update("master", commit1);
+
+ compact();
+
+ Optional<DfsPackFile> compactPack = Arrays.stream(odb.getPacks())
+ .filter(pack -> pack.getPackDescription()
+ .getPackSource() == COMPACT)
+ .findFirst();
+ assertTrue(compactPack.isPresent());
+ assertTrue(compactPack.get().getPackDescription().hasFileExt(OBJECT_SIZE_INDEX));
+ }
+
+ @Test
+ public void testObjectSizeIndexNotWritten() throws Exception {
+ writeObjectSizeIndex(repo, false);
+ RevCommit commit0 = commit().message("0").create();
+ RevCommit commit1 = commit().message("1").parent(commit0).create();
+ git.update("master", commit1);
+
+ compact();
+
+ Optional<DfsPackFile> compactPack = Arrays.stream(odb.getPacks())
+ .filter(pack -> pack.getPackDescription()
+ .getPackSource() == COMPACT)
+ .findFirst();
+ assertTrue(compactPack.isPresent());
+ assertFalse(compactPack.get().getPackDescription().hasFileExt(OBJECT_SIZE_INDEX));
+ }
+
private TestRepository<InMemoryRepository>.CommitBuilder commit() {
return git.commit();
}
@@ -108,4 +147,9 @@ private void compact() throws IOException {
compactor.compact(null);
odb.clearCache();
}
+
+ private static void writeObjectSizeIndex(DfsRepository repo, boolean should) {
+ repo.getConfig().setInt(ConfigConstants.CONFIG_PACK_SECTION, null,
+ ConfigConstants.CONFIG_KEY_MIN_BYTES_OBJ_SIZE_INDEX, should ? 0 : -1);
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackFileTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackFileTest.java
index d21e51f..9680019 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackFileTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackFileTest.java
@@ -41,6 +41,7 @@
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.pack.PackConfig;
import org.eclipse.jgit.transport.ReceiveCommand;
import org.junit.Before;
import org.junit.Test;
@@ -126,6 +127,7 @@ public void testLoadObjectSizeIndex() throws IOException {
setObjectSizeIndexMinBytes(0);
ObjectId blobId = setupPack(512, 800);
+ db.getObjectDatabase().getReaderOptions().setUseObjectSizeIndex(true);
DfsReader reader = db.getObjectDatabase().newReader();
DfsPackFile pack = db.getObjectDatabase().getPacks()[0];
assertTrue(pack.hasObjectSizeIndex(reader));
@@ -308,7 +310,7 @@ private ObjectId setupPack(int bs, int ps) throws IOException {
private void assertPackSize() throws IOException {
try (DfsReader ctx = db.getObjectDatabase().newReader();
- PackWriter pw = new PackWriter(ctx);
+ PackWriter pw = new PackWriter(new PackConfig(), ctx);
ByteArrayOutputStream os = new ByteArrayOutputStream();
PackOutputStream out = new PackOutputStream(
NullProgressMonitor.INSTANCE, os, pw)) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackParserTest.java
index 130af27..c1cd231 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackParserTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackParserTest.java
@@ -61,6 +61,7 @@ public void parse_writeObjSizeIdx() throws IOException {
ins.flush();
}
+ repo.getObjectDatabase().getReaderOptions().setUseObjectSizeIndex(true);
DfsReader reader = repo.getObjectDatabase().newReader();
PackList packList = repo.getObjectDatabase().getPackList();
assertEquals(1, packList.packs.length);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsReaderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsReaderTest.java
index 254184e..a0c2289 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsReaderTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsReaderTest.java
@@ -37,6 +37,8 @@ public class DfsReaderTest {
@Before
public void setUp() {
db = new InMemoryRepository(new DfsRepositoryDescription("test"));
+ // These tests assume the object size index is enabled.
+ db.getObjectDatabase().getReaderOptions().setUseObjectSizeIndex(true);
}
@Test
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/PackExtBlockCacheTableTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/PackExtBlockCacheTableTest.java
new file mode 100644
index 0000000..e7627bc
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/PackExtBlockCacheTableTest.java
@@ -0,0 +1,679 @@
+/*
+ * Copyright (c) 2024, Google LLC and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.eclipse.jgit.internal.storage.dfs;
+
+import static org.eclipse.jgit.internal.storage.dfs.DfsBlockCacheTable.BlockCacheStats;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.sameInstance;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.when;
+
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jgit.internal.storage.dfs.DfsBlockCache.Ref;
+import org.eclipse.jgit.internal.storage.dfs.DfsBlockCache.RefLoader;
+import org.eclipse.jgit.internal.storage.dfs.DfsBlockCacheConfig.DfsBlockCachePackExtConfig;
+import org.eclipse.jgit.internal.storage.pack.PackExt;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+@SuppressWarnings({ "boxing", "unchecked" })
+public class PackExtBlockCacheTableTest {
+ private static final String CACHE_NAME = "CacheName";
+
+ @Test
+ public void fromBlockCacheConfigs_createsDfsPackExtBlockCacheTables() {
+ DfsBlockCacheConfig cacheConfig = new DfsBlockCacheConfig();
+ cacheConfig.setPackExtCacheConfigurations(
+ List.of(new DfsBlockCachePackExtConfig(EnumSet.of(PackExt.PACK),
+ new DfsBlockCacheConfig())));
+ assertNotNull(
+ PackExtBlockCacheTable.fromBlockCacheConfigs(cacheConfig));
+ }
+
+ @Test
+ public void fromBlockCacheConfigs_noPackExtConfigurationGiven_packExtCacheConfigurationsIsEmpty_throws() {
+ DfsBlockCacheConfig cacheConfig = new DfsBlockCacheConfig();
+ cacheConfig.setPackExtCacheConfigurations(List.of());
+ assertThrows(IllegalArgumentException.class,
+ () -> PackExtBlockCacheTable
+ .fromBlockCacheConfigs(cacheConfig));
+ }
+
+ @Test
+ public void hasBlock0_packExtMapsToCacheTable_callsBitmapIndexCacheTable() {
+ DfsStreamKey streamKey = new TestKey(PackExt.BITMAP_INDEX);
+ DfsBlockCacheTable defaultBlockCacheTable = mock(
+ DfsBlockCacheTable.class);
+ DfsBlockCacheTable bitmapIndexCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(bitmapIndexCacheTable.hasBlock0(any(DfsStreamKey.class)))
+ .thenReturn(true);
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable.fromCacheTables(
+ defaultBlockCacheTable,
+ Map.of(PackExt.BITMAP_INDEX, bitmapIndexCacheTable));
+
+ assertTrue(tables.hasBlock0(streamKey));
+ }
+
+ @Test
+ public void hasBlock0_packExtDoesNotMapToCacheTable_callsDefaultCache() {
+ DfsStreamKey streamKey = new TestKey(PackExt.PACK);
+ DfsBlockCacheTable defaultBlockCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(defaultBlockCacheTable.hasBlock0(any(DfsStreamKey.class)))
+ .thenReturn(true);
+ DfsBlockCacheTable bitmapIndexCacheTable = mock(
+ DfsBlockCacheTable.class);
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable.fromCacheTables(
+ defaultBlockCacheTable,
+ Map.of(PackExt.BITMAP_INDEX, bitmapIndexCacheTable));
+
+ assertTrue(tables.hasBlock0(streamKey));
+ }
+
+ @Test
+ public void getOrLoad_packExtMapsToCacheTable_callsBitmapIndexCacheTable()
+ throws Exception {
+ BlockBasedFile blockBasedFile = new BlockBasedFile(null,
+ mock(DfsPackDescription.class), PackExt.BITMAP_INDEX) {
+ // empty
+ };
+ DfsBlock dfsBlock = mock(DfsBlock.class);
+ DfsBlockCacheTable defaultBlockCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(defaultBlockCacheTable.getOrLoad(any(BlockBasedFile.class),
+ anyLong(), any(DfsReader.class),
+ any(DfsBlockCache.ReadableChannelSupplier.class)))
+ .thenReturn(mock(DfsBlock.class));
+ DfsBlockCacheTable bitmapIndexCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(bitmapIndexCacheTable.getOrLoad(any(BlockBasedFile.class),
+ anyLong(), any(DfsReader.class),
+ any(DfsBlockCache.ReadableChannelSupplier.class)))
+ .thenReturn(dfsBlock);
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable.fromCacheTables(
+ defaultBlockCacheTable,
+ Map.of(PackExt.BITMAP_INDEX, bitmapIndexCacheTable));
+
+ assertThat(
+ tables.getOrLoad(blockBasedFile, 0, mock(DfsReader.class),
+ mock(DfsBlockCache.ReadableChannelSupplier.class)),
+ sameInstance(dfsBlock));
+ }
+
+ @Test
+ public void getOrLoad_packExtDoesNotMapToCacheTable_callsDefaultCache()
+ throws Exception {
+ BlockBasedFile blockBasedFile = new BlockBasedFile(null,
+ mock(DfsPackDescription.class), PackExt.PACK) {
+ // empty
+ };
+ DfsBlock dfsBlock = mock(DfsBlock.class);
+ DfsBlockCacheTable defaultBlockCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(defaultBlockCacheTable.getOrLoad(any(BlockBasedFile.class),
+ anyLong(), any(DfsReader.class),
+ any(DfsBlockCache.ReadableChannelSupplier.class)))
+ .thenReturn(dfsBlock);
+ DfsBlockCacheTable bitmapIndexCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(bitmapIndexCacheTable.getOrLoad(any(BlockBasedFile.class),
+ anyLong(), any(DfsReader.class),
+ any(DfsBlockCache.ReadableChannelSupplier.class)))
+ .thenReturn(mock(DfsBlock.class));
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable.fromCacheTables(
+ defaultBlockCacheTable,
+ Map.of(PackExt.BITMAP_INDEX, bitmapIndexCacheTable));
+
+ assertThat(
+ tables.getOrLoad(blockBasedFile, 0, mock(DfsReader.class),
+ mock(DfsBlockCache.ReadableChannelSupplier.class)),
+ sameInstance(dfsBlock));
+ }
+
+ @Test
+ public void getOrLoadRef_packExtMapsToCacheTable_callsBitmapIndexCacheTable()
+ throws Exception {
+ Ref<Integer> ref = mock(Ref.class);
+ DfsStreamKey dfsStreamKey = new TestKey(PackExt.BITMAP_INDEX);
+ DfsBlockCacheTable defaultBlockCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(defaultBlockCacheTable.getOrLoadRef(any(DfsStreamKey.class),
+ anyLong(), any(RefLoader.class))).thenReturn(mock(Ref.class));
+ DfsBlockCacheTable bitmapIndexCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(bitmapIndexCacheTable.getOrLoadRef(any(DfsStreamKey.class),
+ anyLong(), any(RefLoader.class))).thenReturn(ref);
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable.fromCacheTables(
+ defaultBlockCacheTable,
+ Map.of(PackExt.BITMAP_INDEX, bitmapIndexCacheTable));
+
+ assertThat(tables.getOrLoadRef(dfsStreamKey, 0, mock(RefLoader.class)),
+ sameInstance(ref));
+ }
+
+ @Test
+ public void getOrLoadRef_packExtDoesNotMapToCacheTable_callsDefaultCache()
+ throws Exception {
+ Ref<Integer> ref = mock(Ref.class);
+ DfsStreamKey dfsStreamKey = new TestKey(PackExt.PACK);
+ DfsBlockCacheTable defaultBlockCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(defaultBlockCacheTable.getOrLoadRef(any(DfsStreamKey.class),
+ anyLong(), any(RefLoader.class))).thenReturn(ref);
+ DfsBlockCacheTable bitmapIndexCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(bitmapIndexCacheTable.getOrLoadRef(any(DfsStreamKey.class),
+ anyLong(), any(RefLoader.class))).thenReturn(mock(Ref.class));
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable.fromCacheTables(
+ defaultBlockCacheTable,
+ Map.of(PackExt.BITMAP_INDEX, bitmapIndexCacheTable));
+
+ assertThat(tables.getOrLoadRef(dfsStreamKey, 0, mock(RefLoader.class)),
+ sameInstance(ref));
+ }
+
+ @Test
+ public void putDfsBlock_packExtMapsToCacheTable_callsBitmapIndexCacheTable() {
+ DfsStreamKey dfsStreamKey = new TestKey(PackExt.BITMAP_INDEX);
+ DfsBlock dfsBlock = new DfsBlock(dfsStreamKey, 0, new byte[0]);
+ DfsBlockCacheTable defaultBlockCacheTable = mock(
+ DfsBlockCacheTable.class);
+ DfsBlockCacheTable bitmapIndexCacheTable = mock(
+ DfsBlockCacheTable.class);
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable.fromCacheTables(
+ defaultBlockCacheTable,
+ Map.of(PackExt.BITMAP_INDEX, bitmapIndexCacheTable));
+
+ tables.put(dfsBlock);
+ Mockito.verify(bitmapIndexCacheTable, times(1)).put(dfsBlock);
+ }
+
+ @Test
+ public void putDfsBlock_packExtDoesNotMapToCacheTable_callsDefaultCache() {
+ DfsStreamKey dfsStreamKey = new TestKey(PackExt.PACK);
+ DfsBlock dfsBlock = new DfsBlock(dfsStreamKey, 0, new byte[0]);
+ DfsBlockCacheTable defaultBlockCacheTable = mock(
+ DfsBlockCacheTable.class);
+ DfsBlockCacheTable bitmapIndexCacheTable = mock(
+ DfsBlockCacheTable.class);
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable.fromCacheTables(
+ defaultBlockCacheTable,
+ Map.of(PackExt.BITMAP_INDEX, bitmapIndexCacheTable));
+
+ tables.put(dfsBlock);
+ Mockito.verify(defaultBlockCacheTable, times(1)).put(dfsBlock);
+ }
+
+ @Test
+ public void putDfsStreamKey_packExtMapsToCacheTable_callsBitmapIndexCacheTable() {
+ DfsStreamKey dfsStreamKey = new TestKey(PackExt.BITMAP_INDEX);
+ Ref<Integer> ref = mock(Ref.class);
+ DfsBlockCacheTable defaultBlockCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(defaultBlockCacheTable.put(any(DfsStreamKey.class), anyLong(),
+ anyLong(), anyInt())).thenReturn(mock(Ref.class));
+ DfsBlockCacheTable bitmapIndexCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(bitmapIndexCacheTable.put(any(DfsStreamKey.class), anyLong(),
+ anyLong(), anyInt())).thenReturn(ref);
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable.fromCacheTables(
+ defaultBlockCacheTable,
+ Map.of(PackExt.BITMAP_INDEX, bitmapIndexCacheTable));
+
+ assertThat(tables.put(dfsStreamKey, 0, 0, 0), sameInstance(ref));
+ }
+
+ @Test
+ public void putDfsStreamKey_packExtDoesNotMapToCacheTable_callsDefaultCache() {
+ DfsStreamKey dfsStreamKey = new TestKey(PackExt.PACK);
+ Ref<Integer> ref = mock(Ref.class);
+ DfsBlockCacheTable defaultBlockCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(defaultBlockCacheTable.put(any(DfsStreamKey.class), anyLong(),
+ anyLong(), anyInt())).thenReturn(ref);
+ DfsBlockCacheTable bitmapIndexCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(bitmapIndexCacheTable.put(any(DfsStreamKey.class), anyLong(),
+ anyLong(), anyInt())).thenReturn(mock(Ref.class));
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable.fromCacheTables(
+ defaultBlockCacheTable,
+ Map.of(PackExt.BITMAP_INDEX, bitmapIndexCacheTable));
+
+ assertThat(tables.put(dfsStreamKey, 0, 0, 0), sameInstance(ref));
+ }
+
+ @Test
+ public void putRef_packExtMapsToCacheTable_callsBitmapIndexCacheTable() {
+ DfsStreamKey dfsStreamKey = new TestKey(PackExt.BITMAP_INDEX);
+ Ref<Integer> ref = mock(Ref.class);
+ DfsBlockCacheTable defaultBlockCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(defaultBlockCacheTable.putRef(any(DfsStreamKey.class), anyLong(),
+ anyInt())).thenReturn(mock(Ref.class));
+ DfsBlockCacheTable bitmapIndexCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(bitmapIndexCacheTable.putRef(any(DfsStreamKey.class), anyLong(),
+ anyInt())).thenReturn(ref);
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable.fromCacheTables(
+ defaultBlockCacheTable,
+ Map.of(PackExt.BITMAP_INDEX, bitmapIndexCacheTable));
+
+ assertThat(tables.putRef(dfsStreamKey, 0, 0), sameInstance(ref));
+ }
+
+ @Test
+ public void putRef_packExtDoesNotMapToCacheTable_callsDefaultCache() {
+ DfsStreamKey dfsStreamKey = new TestKey(PackExt.PACK);
+ Ref<Integer> ref = mock(Ref.class);
+ DfsBlockCacheTable defaultBlockCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(defaultBlockCacheTable.putRef(any(DfsStreamKey.class), anyLong(),
+ anyInt())).thenReturn(ref);
+ DfsBlockCacheTable bitmapIndexCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(bitmapIndexCacheTable.putRef(any(DfsStreamKey.class), anyLong(),
+ anyInt())).thenReturn(mock(Ref.class));
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable.fromCacheTables(
+ defaultBlockCacheTable,
+ Map.of(PackExt.BITMAP_INDEX, bitmapIndexCacheTable));
+
+ assertThat(tables.putRef(dfsStreamKey, 0, 0), sameInstance(ref));
+ }
+
+ @Test
+ public void contains_packExtMapsToCacheTable_callsBitmapIndexCacheTable() {
+ DfsStreamKey streamKey = new TestKey(PackExt.BITMAP_INDEX);
+ DfsBlockCacheTable defaultBlockCacheTable = mock(
+ DfsBlockCacheTable.class);
+ DfsBlockCacheTable bitmapIndexCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(bitmapIndexCacheTable.contains(any(DfsStreamKey.class), anyLong()))
+ .thenReturn(true);
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable.fromCacheTables(
+ defaultBlockCacheTable,
+ Map.of(PackExt.BITMAP_INDEX, bitmapIndexCacheTable));
+
+ assertTrue(tables.contains(streamKey, 0));
+ }
+
+ @Test
+ public void contains_packExtDoesNotMapToCacheTable_callsDefaultCache() {
+ DfsStreamKey streamKey = new TestKey(PackExt.PACK);
+ DfsBlockCacheTable defaultBlockCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(defaultBlockCacheTable.contains(any(DfsStreamKey.class),
+ anyLong())).thenReturn(true);
+ DfsBlockCacheTable bitmapIndexCacheTable = mock(
+ DfsBlockCacheTable.class);
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable.fromCacheTables(
+ defaultBlockCacheTable,
+ Map.of(PackExt.BITMAP_INDEX, bitmapIndexCacheTable));
+
+ assertTrue(tables.contains(streamKey, 0));
+ }
+
+ @Test
+ public void get_packExtMapsToCacheTable_callsBitmapIndexCacheTable() {
+ DfsStreamKey dfsStreamKey = new TestKey(PackExt.BITMAP_INDEX);
+ Ref<Integer> ref = mock(Ref.class);
+ DfsBlockCacheTable defaultBlockCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(defaultBlockCacheTable.get(any(DfsStreamKey.class), anyLong()))
+ .thenReturn(mock(Ref.class));
+ DfsBlockCacheTable bitmapIndexCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(bitmapIndexCacheTable.get(any(DfsStreamKey.class), anyLong()))
+ .thenReturn(ref);
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable.fromCacheTables(
+ defaultBlockCacheTable,
+ Map.of(PackExt.BITMAP_INDEX, bitmapIndexCacheTable));
+
+ assertThat(tables.get(dfsStreamKey, 0), sameInstance(ref));
+ }
+
+ @Test
+ public void get_packExtDoesNotMapToCacheTable_callsDefaultCache() {
+ DfsStreamKey dfsStreamKey = new TestKey(PackExt.PACK);
+ Ref<Integer> ref = mock(Ref.class);
+ DfsBlockCacheTable defaultBlockCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(defaultBlockCacheTable.get(any(DfsStreamKey.class), anyLong()))
+ .thenReturn(ref);
+ DfsBlockCacheTable bitmapIndexCacheTable = mock(
+ DfsBlockCacheTable.class);
+ when(bitmapIndexCacheTable.get(any(DfsStreamKey.class), anyLong()))
+ .thenReturn(mock(Ref.class));
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable.fromCacheTables(
+ defaultBlockCacheTable,
+ Map.of(PackExt.BITMAP_INDEX, bitmapIndexCacheTable));
+
+ assertThat(tables.get(dfsStreamKey, 0), sameInstance(ref));
+ }
+
+ @Test
+ public void getName() {
+ DfsBlockCacheStats packStats = new DfsBlockCacheStats();
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable.fromCacheTables(
+ cacheTableWithStats(/* name= */ "defaultName", packStats),
+ Map.of(PackExt.PACK, cacheTableWithStats(/* name= */ "packName",
+ packStats)));
+
+ assertThat(tables.getName(), equalTo("defaultName,packName"));
+ }
+
+ @Test
+ public void getAllBlockCacheStats() {
+ String defaultTableName = "default table";
+ DfsBlockCacheStats defaultStats = new DfsBlockCacheStats(
+ defaultTableName);
+ incrementCounter(4,
+ () -> defaultStats.incrementHit(new TestKey(PackExt.REFTABLE)));
+
+ String packTableName = "pack table";
+ DfsBlockCacheStats packStats = new DfsBlockCacheStats(packTableName);
+ incrementCounter(5,
+ () -> packStats.incrementHit(new TestKey(PackExt.PACK)));
+
+ String bitmapTableName = "bitmap table";
+ DfsBlockCacheStats bitmapStats = new DfsBlockCacheStats(
+ bitmapTableName);
+ incrementCounter(6, () -> bitmapStats
+ .incrementHit(new TestKey(PackExt.BITMAP_INDEX)));
+
+ DfsBlockCacheTable defaultTable = cacheTableWithStats(defaultStats);
+ DfsBlockCacheTable packTable = cacheTableWithStats(packStats);
+ DfsBlockCacheTable bitmapTable = cacheTableWithStats(bitmapStats);
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable
+ .fromCacheTables(defaultTable, Map.of(PackExt.PACK, packTable,
+ PackExt.BITMAP_INDEX, bitmapTable));
+
+ List<BlockCacheStats> statsList = tables.getBlockCacheStats();
+ assertThat(statsList, hasSize(3));
+
+ long[] defaultTableHitCounts = createEmptyStatsArray();
+ defaultTableHitCounts[PackExt.REFTABLE.getPosition()] = 4;
+ assertArrayEquals(
+ getCacheStatsByName(statsList, defaultTableName).getHitCount(),
+ defaultTableHitCounts);
+
+ long[] packTableHitCounts = createEmptyStatsArray();
+ packTableHitCounts[PackExt.PACK.getPosition()] = 5;
+ assertArrayEquals(
+ getCacheStatsByName(statsList, packTableName).getHitCount(),
+ packTableHitCounts);
+
+ long[] bitmapHitCounts = createEmptyStatsArray();
+ bitmapHitCounts[PackExt.BITMAP_INDEX.getPosition()] = 6;
+ assertArrayEquals(
+ getCacheStatsByName(statsList, bitmapTableName).getHitCount(),
+ bitmapHitCounts);
+ }
+
+ @Test
+ public void getBlockCacheStats_getCurrentSize_consolidatesAllTableCurrentSizes() {
+ long[] currentSizes = createEmptyStatsArray();
+
+ DfsBlockCacheStats packStats = new DfsBlockCacheStats();
+ packStats.addToLiveBytes(new TestKey(PackExt.PACK), 5);
+ currentSizes[PackExt.PACK.getPosition()] = 5;
+
+ DfsBlockCacheStats bitmapStats = new DfsBlockCacheStats();
+ bitmapStats.addToLiveBytes(new TestKey(PackExt.BITMAP_INDEX), 6);
+ currentSizes[PackExt.BITMAP_INDEX.getPosition()] = 6;
+
+ DfsBlockCacheStats indexStats = new DfsBlockCacheStats();
+ indexStats.addToLiveBytes(new TestKey(PackExt.INDEX), 7);
+ currentSizes[PackExt.INDEX.getPosition()] = 7;
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable
+ .fromCacheTables(cacheTableWithStats(packStats),
+ Map.of(PackExt.BITMAP_INDEX,
+ cacheTableWithStats(bitmapStats), PackExt.INDEX,
+ cacheTableWithStats(indexStats)));
+
+ assertArrayEquals(AggregatedBlockCacheStats
+ .fromStatsList(tables.getBlockCacheStats()).getCurrentSize(),
+ currentSizes);
+ }
+
+ @Test
+ public void getBlockCacheStats_GetHitCount_consolidatesAllTableHitCounts() {
+ long[] hitCounts = createEmptyStatsArray();
+
+ DfsBlockCacheStats packStats = new DfsBlockCacheStats();
+ incrementCounter(5,
+ () -> packStats.incrementHit(new TestKey(PackExt.PACK)));
+ hitCounts[PackExt.PACK.getPosition()] = 5;
+
+ DfsBlockCacheStats bitmapStats = new DfsBlockCacheStats();
+ incrementCounter(6, () -> bitmapStats
+ .incrementHit(new TestKey(PackExt.BITMAP_INDEX)));
+ hitCounts[PackExt.BITMAP_INDEX.getPosition()] = 6;
+
+ DfsBlockCacheStats indexStats = new DfsBlockCacheStats();
+ incrementCounter(7,
+ () -> indexStats.incrementHit(new TestKey(PackExt.INDEX)));
+ hitCounts[PackExt.INDEX.getPosition()] = 7;
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable
+ .fromCacheTables(cacheTableWithStats(packStats),
+ Map.of(PackExt.BITMAP_INDEX,
+ cacheTableWithStats(bitmapStats), PackExt.INDEX,
+ cacheTableWithStats(indexStats)));
+
+ assertArrayEquals(AggregatedBlockCacheStats
+ .fromStatsList(tables.getBlockCacheStats()).getHitCount(),
+ hitCounts);
+ }
+
+ @Test
+ public void getBlockCacheStats_getMissCount_consolidatesAllTableMissCounts() {
+ long[] missCounts = createEmptyStatsArray();
+
+ DfsBlockCacheStats packStats = new DfsBlockCacheStats();
+ incrementCounter(5,
+ () -> packStats.incrementMiss(new TestKey(PackExt.PACK)));
+ missCounts[PackExt.PACK.getPosition()] = 5;
+
+ DfsBlockCacheStats bitmapStats = new DfsBlockCacheStats();
+ incrementCounter(6, () -> bitmapStats
+ .incrementMiss(new TestKey(PackExt.BITMAP_INDEX)));
+ missCounts[PackExt.BITMAP_INDEX.getPosition()] = 6;
+
+ DfsBlockCacheStats indexStats = new DfsBlockCacheStats();
+ incrementCounter(7,
+ () -> indexStats.incrementMiss(new TestKey(PackExt.INDEX)));
+ missCounts[PackExt.INDEX.getPosition()] = 7;
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable
+ .fromCacheTables(cacheTableWithStats(packStats),
+ Map.of(PackExt.BITMAP_INDEX,
+ cacheTableWithStats(bitmapStats), PackExt.INDEX,
+ cacheTableWithStats(indexStats)));
+
+ assertArrayEquals(AggregatedBlockCacheStats
+ .fromStatsList(tables.getBlockCacheStats()).getMissCount(),
+ missCounts);
+ }
+
+ @Test
+ public void getBlockCacheStats_getTotalRequestCount_consolidatesAllTableTotalRequestCounts() {
+ long[] totalRequestCounts = createEmptyStatsArray();
+
+ DfsBlockCacheStats packStats = new DfsBlockCacheStats();
+ incrementCounter(5, () -> {
+ packStats.incrementHit(new TestKey(PackExt.PACK));
+ packStats.incrementMiss(new TestKey(PackExt.PACK));
+ });
+ totalRequestCounts[PackExt.PACK.getPosition()] = 10;
+
+ DfsBlockCacheStats bitmapStats = new DfsBlockCacheStats();
+ incrementCounter(6, () -> {
+ bitmapStats.incrementHit(new TestKey(PackExt.BITMAP_INDEX));
+ bitmapStats.incrementMiss(new TestKey(PackExt.BITMAP_INDEX));
+ });
+ totalRequestCounts[PackExt.BITMAP_INDEX.getPosition()] = 12;
+
+ DfsBlockCacheStats indexStats = new DfsBlockCacheStats();
+ incrementCounter(7, () -> {
+ indexStats.incrementHit(new TestKey(PackExt.INDEX));
+ indexStats.incrementMiss(new TestKey(PackExt.INDEX));
+ });
+ totalRequestCounts[PackExt.INDEX.getPosition()] = 14;
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable
+ .fromCacheTables(cacheTableWithStats(packStats),
+ Map.of(PackExt.BITMAP_INDEX,
+ cacheTableWithStats(bitmapStats), PackExt.INDEX,
+ cacheTableWithStats(indexStats)));
+
+ assertArrayEquals(AggregatedBlockCacheStats
+ .fromStatsList(tables.getBlockCacheStats())
+ .getTotalRequestCount(), totalRequestCounts);
+ }
+
+ @Test
+ public void getBlockCacheStats_getHitRatio_consolidatesAllTableHitRatios() {
+ long[] hitRatios = createEmptyStatsArray();
+
+ DfsBlockCacheStats packStats = new DfsBlockCacheStats();
+ incrementCounter(5,
+ () -> packStats.incrementHit(new TestKey(PackExt.PACK)));
+ hitRatios[PackExt.PACK.getPosition()] = 100;
+
+ DfsBlockCacheStats bitmapStats = new DfsBlockCacheStats();
+ incrementCounter(6, () -> {
+ bitmapStats.incrementHit(new TestKey(PackExt.BITMAP_INDEX));
+ bitmapStats.incrementMiss(new TestKey(PackExt.BITMAP_INDEX));
+ });
+ hitRatios[PackExt.BITMAP_INDEX.getPosition()] = 50;
+
+ DfsBlockCacheStats indexStats = new DfsBlockCacheStats();
+ incrementCounter(7,
+ () -> indexStats.incrementMiss(new TestKey(PackExt.INDEX)));
+ hitRatios[PackExt.INDEX.getPosition()] = 0;
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable
+ .fromCacheTables(cacheTableWithStats(packStats),
+ Map.of(PackExt.BITMAP_INDEX,
+ cacheTableWithStats(bitmapStats), PackExt.INDEX,
+ cacheTableWithStats(indexStats)));
+
+ assertArrayEquals(AggregatedBlockCacheStats
+ .fromStatsList(tables.getBlockCacheStats()).getHitRatio(),
+ hitRatios);
+ }
+
+ @Test
+ public void getBlockCacheStats_getEvictions_consolidatesAllTableEvictions() {
+ long[] evictions = createEmptyStatsArray();
+
+ DfsBlockCacheStats packStats = new DfsBlockCacheStats();
+ incrementCounter(5,
+ () -> packStats.incrementEvict(new TestKey(PackExt.PACK)));
+ evictions[PackExt.PACK.getPosition()] = 5;
+
+ DfsBlockCacheStats bitmapStats = new DfsBlockCacheStats();
+ incrementCounter(6, () -> bitmapStats
+ .incrementEvict(new TestKey(PackExt.BITMAP_INDEX)));
+ evictions[PackExt.BITMAP_INDEX.getPosition()] = 6;
+
+ DfsBlockCacheStats indexStats = new DfsBlockCacheStats();
+ incrementCounter(7,
+ () -> indexStats.incrementEvict(new TestKey(PackExt.INDEX)));
+ evictions[PackExt.INDEX.getPosition()] = 7;
+
+ PackExtBlockCacheTable tables = PackExtBlockCacheTable
+ .fromCacheTables(cacheTableWithStats(packStats),
+ Map.of(PackExt.BITMAP_INDEX,
+ cacheTableWithStats(bitmapStats), PackExt.INDEX,
+ cacheTableWithStats(indexStats)));
+
+ assertArrayEquals(AggregatedBlockCacheStats
+ .fromStatsList(tables.getBlockCacheStats()).getEvictions(),
+ evictions);
+ }
+
+ private BlockCacheStats getCacheStatsByName(
+ List<BlockCacheStats> blockCacheStats, String name) {
+ for (BlockCacheStats entry : blockCacheStats) {
+ if (entry.getName().equals(name)) {
+ return entry;
+ }
+ }
+ return null;
+ }
+
+ private static void incrementCounter(int amount, Runnable fn) {
+ for (int i = 0; i < amount; i++) {
+ fn.run();
+ }
+ }
+
+ private static long[] createEmptyStatsArray() {
+ return new long[PackExt.values().length];
+ }
+
+ private static DfsBlockCacheTable cacheTableWithStats(
+ BlockCacheStats dfsBlockCacheStats) {
+ return cacheTableWithStats(CACHE_NAME, dfsBlockCacheStats);
+ }
+
+ private static DfsBlockCacheTable cacheTableWithStats(String name,
+ BlockCacheStats dfsBlockCacheStats) {
+ DfsBlockCacheTable cacheTable = mock(DfsBlockCacheTable.class);
+ when(cacheTable.getName()).thenReturn(name);
+ when(cacheTable.getBlockCacheStats())
+ .thenReturn(List.of(dfsBlockCacheStats));
+ return cacheTable;
+ }
+
+ private static class TestKey extends DfsStreamKey {
+ TestKey(PackExt packExt) {
+ super(0, packExt);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return false;
+ }
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/AbbreviationTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/AbbreviationTest.java
index bd36337..41a33df 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/AbbreviationTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/AbbreviationTest.java
@@ -29,6 +29,7 @@
import org.eclipse.jgit.errors.AmbiguousObjectException;
import org.eclipse.jgit.internal.storage.pack.PackExt;
+import org.eclipse.jgit.internal.storage.pack.PackIndexWriter;
import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.AbbreviatedObjectId;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/BasePackWriterTest.java
similarity index 99%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/BasePackWriterTest.java
index 24a81b6..92d7465 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/BasePackWriterTest.java
@@ -66,7 +66,7 @@
import org.junit.Test;
import org.mockito.Mockito;
-public class PackWriterTest extends SampleDataRepositoryTestCase {
+public class BasePackWriterTest extends SampleDataRepositoryTestCase {
private static final List<RevObject> EMPTY_LIST_REVS = Collections
.<RevObject> emptyList();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/BatchRefUpdateTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/BatchRefUpdateTest.java
index daf4382..a0afc3e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/BatchRefUpdateTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/BatchRefUpdateTest.java
@@ -171,7 +171,7 @@ public void packedRefsFileIsSorted() throws IOException {
assertEquals(c2.getResult(), ReceiveCommand.Result.OK);
}
- File packed = new File(diskRepo.getDirectory(), "packed-refs");
+ File packed = new File(diskRepo.getCommonDirectory(), "packed-refs");
String packedStr = new String(Files.readAllBytes(packed.toPath()),
UTF_8);
@@ -1263,7 +1263,7 @@ private Map<String, ReflogEntry> getLastReflogs(String... names)
}
private ReflogEntry getLastReflog(String name) throws IOException {
- ReflogReader r = diskRepo.getReflogReader(name);
+ ReflogReader r = diskRepo.getRefDatabase().getReflogReader(name);
if (r == null) {
return null;
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileReftableTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileReftableTest.java
index 32342e3..5756b41 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileReftableTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileReftableTest.java
@@ -23,6 +23,7 @@
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
import java.io.File;
import java.io.FileOutputStream;
@@ -33,8 +34,15 @@
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
-
import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.NullProgressMonitor;
@@ -51,6 +59,10 @@
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
import org.eclipse.jgit.transport.ReceiveCommand;
+import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.FS.ExecutionResult;
+import org.eclipse.jgit.util.RawParseUtils;
+import org.eclipse.jgit.util.TemporaryBuffer;
import org.junit.Test;
public class FileReftableTest extends SampleDataRepositoryTestCase {
@@ -66,6 +78,30 @@ public void setUp() throws Exception {
@SuppressWarnings("boxing")
@Test
+ public void testReloadIfNecessary() throws Exception {
+ ObjectId id = db.resolve("master");
+ try (FileRepository repo1 = new FileRepository(db.getDirectory());
+ FileRepository repo2 = new FileRepository(db.getDirectory())) {
+ ((FileReftableDatabase) repo1.getRefDatabase())
+ .setAutoRefresh(true);
+ ((FileReftableDatabase) repo2.getRefDatabase())
+ .setAutoRefresh(true);
+ FileRepository repos[] = { repo1, repo2 };
+ for (int i = 0; i < 10; i++) {
+ for (int j = 0; j < 2; j++) {
+ FileRepository repo = repos[j];
+ RefUpdate u = repo.getRefDatabase().newUpdate(
+ String.format("branch%d", i * 10 + j), false);
+ u.setNewObjectId(id);
+ RefUpdate.Result r = u.update();
+ assertEquals(Result.NEW, r);
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("boxing")
+ @Test
public void testRacyReload() throws Exception {
ObjectId id = db.resolve("master");
int retry = 0;
@@ -87,13 +123,61 @@ public void testRacyReload() throws Exception {
u.setNewObjectId(id);
r = u.update();
- assertEquals(r, Result.NEW);
+ assertEquals(Result.NEW, r);
}
}
}
// only the first one succeeds
- assertEquals(retry, 19);
+ assertEquals(19, retry);
+ }
+ }
+
+ @Test
+ public void testConcurrentRacyReload() throws Exception {
+ ObjectId id = db.resolve("master");
+ final CyclicBarrier barrier = new CyclicBarrier(2);
+
+ class UpdateRef implements Callable<RefUpdate.Result> {
+
+ private RefUpdate u;
+
+ UpdateRef(FileRepository repo, String branchName)
+ throws IOException {
+ u = repo.getRefDatabase().newUpdate(branchName,
+ false);
+ u.setNewObjectId(id);
+ }
+
+ @Override
+ public RefUpdate.Result call() throws Exception {
+ barrier.await(); // wait for the other thread to prepare
+ return u.update();
+ }
+ }
+
+ ExecutorService pool = Executors.newFixedThreadPool(2);
+ try (FileRepository repo1 = new FileRepository(db.getDirectory());
+ FileRepository repo2 = new FileRepository(db.getDirectory())) {
+ ((FileReftableDatabase) repo1.getRefDatabase())
+ .setAutoRefresh(true);
+ ((FileReftableDatabase) repo2.getRefDatabase())
+ .setAutoRefresh(true);
+ for (int i = 0; i < 10; i++) {
+ String branchName = String.format("branch%d",
+ Integer.valueOf(i));
+ Future<RefUpdate.Result> ru1 = pool
+ .submit(new UpdateRef(repo1, branchName));
+ Future<RefUpdate.Result> ru2 = pool
+ .submit(new UpdateRef(repo2, branchName));
+ assertTrue((ru1.get() == Result.NEW
+ && ru2.get() == Result.LOCK_FAILURE)
+ || (ru1.get() == Result.LOCK_FAILURE
+ && ru2.get() == Result.NEW));
+ }
+ } finally {
+ pool.shutdown();
+ pool.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
}
}
@@ -105,13 +189,13 @@ public void testCompactFully() throws Exception {
RefUpdate u = db.updateRef("refs/heads/master");
u.setForceUpdate(true);
u.setNewObjectId((i%2) == 0 ? c1 : c2);
- assertEquals(u.update(), FORCED);
+ assertEquals(FORCED, u.update());
}
File tableDir = new File(db.getDirectory(), Constants.REFTABLE);
assertTrue(tableDir.listFiles().length > 2);
((FileReftableDatabase)db.getRefDatabase()).compactFully();
- assertEquals(tableDir.listFiles().length,2);
+ assertEquals(2, tableDir.listFiles().length);
}
@Test
@@ -171,9 +255,10 @@ public void testConvertToRefdirReflog() throws Exception {
v.update();
db.convertToPackedRefs(true, false);
- List<ReflogEntry> logs = db.getReflogReader("refs/heads/master").getReverseEntries(2);
- assertEquals(logs.get(0).getComment(), "banana");
- assertEquals(logs.get(1).getComment(), "apple");
+ List<ReflogEntry> logs = db.getRefDatabase()
+ .getReflogReader("refs/heads/master").getReverseEntries(2);
+ assertEquals("banana", logs.get(0).getComment());
+ assertEquals("apple", logs.get(1).getComment());
}
@Test
@@ -185,8 +270,9 @@ public void testBatchrefUpdate() throws Exception {
ReceiveCommand rc1 = new ReceiveCommand(ObjectId.zeroId(), cur, "refs/heads/batch1");
ReceiveCommand rc2 = new ReceiveCommand(ObjectId.zeroId(), prev, "refs/heads/batch2");
String msg = "message";
+ RefDatabase refDb = db.getRefDatabase();
try (RevWalk rw = new RevWalk(db)) {
- db.getRefDatabase().newBatchUpdate()
+ refDb.newBatchUpdate()
.addCommand(rc1, rc2)
.setAtomic(true)
.setRefLogIdent(person)
@@ -194,15 +280,17 @@ public void testBatchrefUpdate() throws Exception {
.execute(rw, NullProgressMonitor.INSTANCE);
}
- assertEquals(rc1.getResult(), ReceiveCommand.Result.OK);
- assertEquals(rc2.getResult(), ReceiveCommand.Result.OK);
+ assertEquals(ReceiveCommand.Result.OK, rc1.getResult());
+ assertEquals(ReceiveCommand.Result.OK, rc2.getResult());
- ReflogEntry e = db.getReflogReader("refs/heads/batch1").getLastEntry();
+ ReflogEntry e = refDb.getReflogReader("refs/heads/batch1")
+ .getLastEntry();
assertEquals(msg, e.getComment());
assertEquals(person, e.getWho());
assertEquals(cur, e.getNewId());
- e = db.getReflogReader("refs/heads/batch2").getLastEntry();
+ e = refDb.getReflogReader("refs/heads/batch2")
+ .getLastEntry();
assertEquals(msg, e.getComment());
assertEquals(person, e.getWho());
assertEquals(prev, e.getNewId());
@@ -267,7 +355,7 @@ public void testDelete() throws Exception {
RefUpdate up = db.getRefDatabase().newUpdate("refs/heads/a", false);
up.setForceUpdate(true);
RefUpdate.Result res = up.delete();
- assertEquals(res, FORCED);
+ assertEquals(FORCED, res);
assertNull(db.exactRef("refs/heads/a"));
}
@@ -309,7 +397,7 @@ public void testUpdateRefDetached() throws Exception {
// the branch HEAD referred to is left untouched
assertEquals(pid, db.resolve("refs/heads/master"));
- ReflogReader reflogReader = db.getReflogReader("HEAD");
+ ReflogReader reflogReader = db.getRefDatabase().getReflogReader("HEAD");
ReflogEntry e = reflogReader.getReverseEntries().get(0);
assertEquals(ppid, e.getNewId());
assertEquals("GIT_COMMITTER_EMAIL", e.getWho().getEmailAddress());
@@ -330,12 +418,13 @@ public void testWriteReflog() throws Exception {
updateRef.setForceUpdate(true);
RefUpdate.Result update = updateRef.update();
assertEquals(FORCED, update); // internal
- ReflogReader r = db.getReflogReader("refs/heads/master");
+ ReflogReader r = db.getRefDatabase()
+ .getReflogReader("refs/heads/master");
ReflogEntry e = r.getLastEntry();
- assertEquals(e.getNewId(), pid);
- assertEquals(e.getComment(), "REFLOG!: FORCED");
- assertEquals(e.getWho(), person);
+ assertEquals(pid, e.getNewId());
+ assertEquals("REFLOG!: FORCED", e.getComment());
+ assertEquals(person, e.getWho());
}
@Test
@@ -352,10 +441,11 @@ public void testLooseDelete() throws IOException {
ref = db.updateRef(newRef);
ref.setNewObjectId(db.resolve(Constants.HEAD));
- assertEquals(ref.delete(), RefUpdate.Result.NO_CHANGE);
+ assertEquals(RefUpdate.Result.NO_CHANGE, ref.delete());
// Differs from RefupdateTest. Deleting a loose ref leaves reflog trail.
- ReflogReader reader = db.getReflogReader("refs/heads/abc");
+ ReflogReader reader = db.getRefDatabase()
+ .getReflogReader("refs/heads/abc");
assertEquals(ObjectId.zeroId(), reader.getReverseEntry(1).getOldId());
assertEquals(nonZero, reader.getReverseEntry(1).getNewId());
assertEquals(nonZero, reader.getReverseEntry(0).getOldId());
@@ -382,8 +472,9 @@ public void testNoCacheObjectIdSubclass() throws IOException {
assertNotSame(newid, r.getObjectId());
assertSame(ObjectId.class, r.getObjectId().getClass());
assertEquals(newid, r.getObjectId());
- List<ReflogEntry> reverseEntries1 = db.getReflogReader("refs/heads/abc")
- .getReverseEntries();
+ RefDatabase refDb = db.getRefDatabase();
+ List<ReflogEntry> reverseEntries1 = refDb
+ .getReflogReader("refs/heads/abc").getReverseEntries();
ReflogEntry entry1 = reverseEntries1.get(0);
assertEquals(1, reverseEntries1.size());
assertEquals(ObjectId.zeroId(), entry1.getOldId());
@@ -392,7 +483,7 @@ public void testNoCacheObjectIdSubclass() throws IOException {
assertEquals(new PersonIdent(db).toString(),
entry1.getWho().toString());
assertEquals("", entry1.getComment());
- List<ReflogEntry> reverseEntries2 = db.getReflogReader("HEAD")
+ List<ReflogEntry> reverseEntries2 = refDb.getReflogReader("HEAD")
.getReverseEntries();
assertEquals(0, reverseEntries2.size());
}
@@ -431,7 +522,7 @@ public void writeUnbornHead() throws Exception {
Ref head = db.exactRef("HEAD");
assertTrue(head.isSymbolic());
- assertEquals(head.getTarget().getName(), "refs/heads/unborn");
+ assertEquals("refs/heads/unborn", head.getTarget().getName());
}
/**
@@ -455,7 +546,7 @@ public void testUpdateRefDetachedUnbornHead() throws Exception {
// the branch HEAD referred to is left untouched
assertNull(db.resolve("refs/heads/unborn"));
- ReflogReader reflogReader = db.getReflogReader("HEAD");
+ ReflogReader reflogReader = db.getRefDatabase().getReflogReader("HEAD");
ReflogEntry e = reflogReader.getReverseEntries().get(0);
assertEquals(ObjectId.zeroId(), e.getOldId());
assertEquals(ppid, e.getNewId());
@@ -499,7 +590,7 @@ public void testRenameCurrentBranch() throws IOException {
names.add("refs/heads/new/name");
for (String nm : names) {
- ReflogReader rd = db.getReflogReader(nm);
+ ReflogReader rd = db.getRefDatabase().getReflogReader(nm);
assertNotNull(rd);
ReflogEntry last = rd.getLastEntry();
ObjectId id = last.getNewId();
@@ -573,10 +664,10 @@ public void compactFully() throws Exception {
assertTrue(res == Result.NEW || res == FORCED);
}
- assertEquals(refDb.exactRef(refName).getObjectId(), bId);
+ assertEquals(bId, refDb.exactRef(refName).getObjectId());
assertTrue(randomStr.equals(refDb.getReflogReader(refName).getReverseEntry(1).getComment()));
refDb.compactFully();
- assertEquals(refDb.exactRef(refName).getObjectId(), bId);
+ assertEquals(bId, refDb.exactRef(refName).getObjectId());
assertTrue(randomStr.equals(refDb.getReflogReader(refName).getReverseEntry(1).getComment()));
}
@@ -644,6 +735,54 @@ public void testGetRefsWithPrefixExcludingOverlappingPrefixes() throws IOExcepti
checkContainsRef(refs, db.exactRef("HEAD"));
}
+ @Test
+ public void testExternalUpdate_bug_102() throws Exception {
+ ((FileReftableDatabase) db.getRefDatabase()).setAutoRefresh(true);
+ assumeTrue(atLeastGitVersion(2, 45));
+ Git git = Git.wrap(db);
+ git.tag().setName("foo").call();
+ Ref ref = db.exactRef("refs/tags/foo");
+ assertNotNull(ref);
+ runGitCommand("tag", "--force", "foo", "e");
+ Ref e = db.exactRef("refs/heads/e");
+ Ref foo = db.exactRef("refs/tags/foo");
+ assertEquals(e.getObjectId(), foo.getObjectId());
+ }
+
+ private String toString(TemporaryBuffer b) throws IOException {
+ return RawParseUtils.decode(b.toByteArray());
+ }
+
+ private ExecutionResult runGitCommand(String... args)
+ throws IOException, InterruptedException {
+ FS fs = db.getFS();
+ ProcessBuilder pb = fs.runInShell("git", args);
+ pb.directory(db.getWorkTree());
+ System.err.println("PATH=" + pb.environment().get("PATH"));
+ ExecutionResult result = fs.execute(pb, null);
+ assertEquals(0, result.getRc());
+ String err = toString(result.getStderr());
+ if (!err.isEmpty()) {
+ System.err.println(err);
+ }
+ String out = toString(result.getStdout());
+ if (!out.isEmpty()) {
+ System.out.println(out);
+ }
+ return result;
+ }
+
+ private boolean atLeastGitVersion(int minMajor, int minMinor)
+ throws IOException, InterruptedException {
+ String version = toString(runGitCommand("version").getStdout())
+ .split(" ")[2];
+ System.out.println(version);
+ String[] digits = version.split("\\.");
+ int major = Integer.parseInt(digits[0]);
+ int minor = Integer.parseInt(digits[1]);
+ return (major >= minMajor) && (minor >= minMinor);
+ }
+
private RefUpdate updateRef(String name) throws IOException {
final RefUpdate ref = db.updateRef(name);
ref.setNewObjectId(db.resolve(Constants.HEAD));
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java
index 6cad8b6..434f7e4 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java
@@ -16,9 +16,9 @@
import java.io.File;
import java.io.IOException;
+import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Date;
import java.util.List;
import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
@@ -206,7 +206,7 @@ public void testDonePruneTooYoungPacks() throws Exception {
// The old packfile is too young to be deleted. We should end up with
// two pack files
- gc.setExpire(new Date(oldPackfile.lastModified() - 1));
+ gc.setExpire(Instant.ofEpochMilli(oldPackfile.lastModified() - 1));
gc.gc().get();
stats = gc.getStatistics();
assertEquals(0, stats.numberOfLooseObjects);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcNumberOfPackFilesSinceBitmapStatisticsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcNumberOfPackFilesSinceBitmapStatisticsTest.java
new file mode 100644
index 0000000..cd1264e
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcNumberOfPackFilesSinceBitmapStatisticsTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2024 Jacek Centkowski <geminica.programs@gmail.com> and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.eclipse.jgit.internal.storage.file;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.stream.StreamSupport;
+
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.junit.Test;
+
+public class GcNumberOfPackFilesSinceBitmapStatisticsTest extends GcTestCase {
+ @Test
+ public void testShouldReportZeroObjectsForInitializedRepo()
+ throws IOException {
+ assertEquals(0L, gc.getStatistics().numberOfPackFilesSinceBitmap);
+ }
+
+ @Test
+ public void testShouldReportAllPackFilesWhenNoGcWasPerformed()
+ throws Exception {
+ tr.packAndPrune();
+ long result = gc.getStatistics().numberOfPackFilesSinceBitmap;
+
+ assertEquals(repo.getObjectDatabase().getPacks().size(), result);
+ }
+
+ @Test
+ public void testShouldReportNoObjectsDirectlyAfterGc() throws Exception {
+ // given
+ addCommit(null);
+ gc.gc().get();
+ assertEquals(1L, repositoryBitmapFiles());
+ assertEquals(0L, gc.getStatistics().numberOfPackFilesSinceBitmap);
+ }
+
+ @Test
+ public void testShouldReportNewObjectsSinceGcWhenRepositoryProgresses()
+ throws Exception {
+ // commit & gc
+ RevCommit parent = addCommit(null);
+ gc.gc().get();
+ assertEquals(1L, repositoryBitmapFiles());
+
+ // progress & pack
+ addCommit(parent);
+ tr.packAndPrune();
+
+ assertEquals(1L, gc.getStatistics().numberOfPackFilesSinceBitmap);
+ }
+
+ @Test
+ public void testShouldReportNewObjectsFromTheLatestBitmapWhenRepositoryProgresses()
+ throws Exception {
+ // commit & gc
+ RevCommit parent = addCommit(null);
+ gc.gc().get();
+ assertEquals(1L, repositoryBitmapFiles());
+
+ // progress & gc
+ parent = addCommit(parent);
+ gc.gc().get();
+ assertEquals(2L, repositoryBitmapFiles());
+
+ // progress & pack
+ addCommit(parent);
+ tr.packAndPrune();
+
+ assertEquals(1L, gc.getStatistics().numberOfPackFilesSinceBitmap);
+ }
+
+ private RevCommit addCommit(RevCommit parent) throws Exception {
+ PersonIdent ident = new PersonIdent("repo-metrics", "repo@metrics.com");
+ TestRepository<FileRepository>.CommitBuilder builder = tr.commit()
+ .author(ident);
+ if (parent != null) {
+ builder.parent(parent);
+ }
+ RevCommit commit = builder.create();
+ tr.update("master", commit);
+ parent = commit;
+ return parent;
+ }
+
+ private long repositoryBitmapFiles() throws IOException {
+ return StreamSupport
+ .stream(Files
+ .newDirectoryStream(repo.getObjectDatabase()
+ .getPackDirectory().toPath(), "pack-*.bitmap")
+ .spliterator(), false)
+ .count();
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java
index 8baa3cc..f84be21 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java
@@ -19,7 +19,6 @@
import static org.junit.Assert.assertSame;
import java.io.File;
-import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.BrokenBarrierException;
@@ -31,6 +30,8 @@
import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.PackRefsCommand;
+import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
@@ -49,7 +50,7 @@ public void looseRefPacked() throws Exception {
RevBlob a = tr.blob("a");
tr.lightweightTag("t", a);
- gc.packRefs();
+ packRefs(false);
assertSame(repo.exactRef("refs/tags/t").getStorage(), Storage.PACKED);
}
@@ -58,9 +59,9 @@ public void emptyRefDirectoryDeleted() throws Exception {
String ref = "dir/ref";
tr.branch(ref).commit().create();
String name = repo.findRef(ref).getName();
- Path dir = repo.getDirectory().toPath().resolve(name).getParent();
+ Path dir = repo.getCommonDirectory().toPath().resolve(name).getParent();
assertNotNull(dir);
- gc.packRefs();
+ packRefs(true);
assertFalse(Files.exists(dir));
}
@@ -75,9 +76,9 @@ public void concurrentOnlyOneWritesPackedRefs() throws Exception {
Callable<Integer> packRefs = () -> {
syncPoint.await();
try {
- gc.packRefs();
+ packRefs(false);
return 0;
- } catch (IOException e) {
+ } catch (GitAPIException e) {
return 1;
}
};
@@ -102,7 +103,7 @@ public void whileRefLockedRefNotPackedNoError()
"refs/tags/t1"));
try {
refLock.lock();
- gc.packRefs();
+ packRefs(false);
} finally {
refLock.unlock();
}
@@ -145,7 +146,7 @@ public boolean isForceUpdate() {
Future<Result> result2 = pool.submit(() -> {
refUpdateLockedRef.await();
- gc.packRefs();
+ packRefs(false);
packRefsDone.await();
return null;
});
@@ -173,19 +174,20 @@ public void dontPackHEAD_nonBare() throws Exception {
assertEquals(repo.exactRef("HEAD").getTarget().getName(),
"refs/heads/master");
assertNull(repo.exactRef("HEAD").getTarget().getObjectId());
- gc.packRefs();
+ PackRefsCommand packRefsCommand = git.packRefs().setAll(true);
+ packRefsCommand.call();
assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE);
assertEquals(repo.exactRef("HEAD").getTarget().getName(),
"refs/heads/master");
assertNull(repo.exactRef("HEAD").getTarget().getObjectId());
git.checkout().setName("refs/heads/side").call();
- gc.packRefs();
+ packRefsCommand.call();
assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE);
// check for detached HEAD
git.checkout().setName(first.getName()).call();
- gc.packRefs();
+ packRefsCommand.call();
assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE);
}
@@ -208,7 +210,7 @@ public void dontPackHEAD_bare() throws Exception {
assertEquals(repo.exactRef("HEAD").getTarget().getName(),
"refs/heads/master");
assertNull(repo.exactRef("HEAD").getTarget().getObjectId());
- gc.packRefs();
+ packRefs(true);
assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE);
assertEquals(repo.exactRef("HEAD").getTarget().getName(),
"refs/heads/master");
@@ -216,9 +218,14 @@ public void dontPackHEAD_bare() throws Exception {
// check for non-detached HEAD
repo.updateRef(Constants.HEAD).link("refs/heads/side");
- gc.packRefs();
+ packRefs(true);
assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE);
assertEquals(repo.exactRef("HEAD").getTarget().getObjectId(),
second.getId());
}
+
+ private void packRefs(boolean all) throws GitAPIException {
+ new PackRefsCommand(repo).setAll(all).call();
+ }
+
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPruneNonReferencedTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPruneNonReferencedTest.java
index ca0f684..84ec132 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPruneNonReferencedTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPruneNonReferencedTest.java
@@ -16,8 +16,8 @@
import static org.junit.Assert.assertTrue;
import java.io.File;
+import java.time.Instant;
import java.util.Collections;
-import java.util.Date;
import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
import org.eclipse.jgit.lib.ObjectId;
@@ -30,7 +30,7 @@ public class GcPruneNonReferencedTest extends GcTestCase {
@Test
public void nonReferencedNonExpiredObject_notPruned() throws Exception {
RevBlob a = tr.blob("a");
- gc.setExpire(new Date(lastModified(a)));
+ gc.setExpire(Instant.ofEpochMilli(lastModified(a)));
gc.prune(Collections.<ObjectId> emptySet());
assertTrue(repo.getObjectDatabase().has(a));
}
@@ -58,7 +58,7 @@ public void nonReferencedExpiredObjectTree_pruned() throws Exception {
@Test
public void nonReferencedObjects_onlyExpiredPruned() throws Exception {
RevBlob a = tr.blob("a");
- gc.setExpire(new Date(lastModified(a) + 1));
+ gc.setExpire(Instant.ofEpochMilli(lastModified(a) + 1));
fsTick();
RevBlob b = tr.blob("b");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcReflogTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcReflogTest.java
index e6c1ee5..29f180d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcReflogTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcReflogTest.java
@@ -30,7 +30,7 @@ public void testPruneNone() throws Exception {
BranchBuilder bb = tr.branch("refs/heads/master");
bb.commit().add("A", "A").add("B", "B").create();
bb.commit().add("A", "A2").add("B", "B2").create();
- new File(repo.getDirectory(), Constants.LOGS + "/refs/heads/master")
+ new File(repo.getCommonDirectory(), Constants.LOGS + "/refs/heads/master")
.delete();
stats = gc.getStatistics();
assertEquals(8, stats.numberOfLooseObjects);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcSinceBitmapStatisticsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcSinceBitmapStatisticsTest.java
new file mode 100644
index 0000000..af52e2c
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcSinceBitmapStatisticsTest.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2024 Jacek Centkowski <geminica.programs@gmail.com> and others.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.eclipse.jgit.internal.storage.file;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.Collection;
+import java.util.stream.StreamSupport;
+
+import org.eclipse.jgit.internal.storage.file.GC.RepoStatistics;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.storage.pack.PackConfig;
+import org.junit.Test;
+
+public class GcSinceBitmapStatisticsTest extends GcTestCase {
+ @Test
+ public void testShouldReportZeroPacksAndObjectsForInitializedRepo()
+ throws IOException {
+ RepoStatistics s = gc.getStatistics();
+ assertEquals(0L, s.numberOfPackFilesSinceBitmap);
+ assertEquals(0L, s.numberOfObjectsSinceBitmap);
+ }
+
+ @Test
+ public void testShouldReportAllPackFilesWhenNoGcWasPerformed()
+ throws Exception {
+ tr.packAndPrune();
+ long result = gc.getStatistics().numberOfPackFilesSinceBitmap;
+
+ assertEquals(repo.getObjectDatabase().getPacks().size(), result);
+ }
+
+ @Test
+ public void testShouldReportAllObjectsWhenNoGcWasPerformed()
+ throws Exception {
+ tr.packAndPrune();
+
+ assertEquals(
+ getNumberOfObjectsInPacks(repo.getObjectDatabase().getPacks()),
+ gc.getStatistics().numberOfObjectsSinceBitmap);
+ }
+
+ @Test
+ public void testShouldReportNoPacksFilesSinceBitmapWhenPackfilesAreOlderThanBitmapFile()
+ throws Exception {
+ addCommit(null);
+ configureGC(/* buildBitmap */ false).gc().get();
+ assertEquals(1L, gc.getStatistics().numberOfPackFiles);
+ assertEquals(0L, repositoryBitmapFiles());
+ assertEquals(1L, gc.getStatistics().numberOfPackFilesSinceBitmap);
+
+ addCommit(null);
+ configureGC(/* buildBitmap */ true).gc().get();
+
+ assertEquals(1L, repositoryBitmapFiles());
+ assertEquals(2L, gc.getStatistics().numberOfPackFiles);
+ assertEquals(0L, gc.getStatistics().numberOfPackFilesSinceBitmap);
+ }
+
+ @Test
+ public void testShouldReportNoObjectsDirectlyAfterGc() throws Exception {
+ // given
+ addCommit(null);
+ assertEquals(2L, gc.getStatistics().numberOfObjectsSinceBitmap);
+
+ gc.gc().get();
+ assertEquals(0L, gc.getStatistics().numberOfObjectsSinceBitmap);
+ }
+
+ @Test
+ public void testShouldReportNewPacksSinceGcWhenRepositoryProgresses()
+ throws Exception {
+ // commit & gc
+ RevCommit parent = addCommit(null);
+ gc.gc().get();
+ assertEquals(1L, repositoryBitmapFiles());
+
+ // progress & pack
+ addCommit(parent);
+ assertEquals(1L, gc.getStatistics().numberOfPackFiles);
+ assertEquals(0L, gc.getStatistics().numberOfPackFilesSinceBitmap);
+
+ tr.packAndPrune();
+ assertEquals(2L, gc.getStatistics().numberOfPackFiles);
+ assertEquals(1L, gc.getStatistics().numberOfPackFilesSinceBitmap);
+ }
+
+ @Test
+ public void testShouldReportNewObjectsSinceGcWhenRepositoryProgresses()
+ throws Exception {
+ // commit & gc
+ RevCommit parent = addCommit(null);
+ gc.gc().get();
+ assertEquals(0L, gc.getStatistics().numberOfLooseObjects);
+ assertEquals(0L, gc.getStatistics().numberOfObjectsSinceBitmap);
+
+ // progress & pack
+ addCommit(parent);
+ assertEquals(1L, gc.getStatistics().numberOfLooseObjects);
+ assertEquals(1L, gc.getStatistics().numberOfObjectsSinceBitmap);
+
+ tr.packAndPrune();
+ assertEquals(0L, gc.getStatistics().numberOfLooseObjects);
+ // Number of objects contained in the newly created PackFile
+ assertEquals(3L, gc.getStatistics().numberOfObjectsSinceBitmap);
+ }
+
+ @Test
+ public void testShouldReportNewPacksFromTheLatestBitmapWhenRepositoryProgresses()
+ throws Exception {
+ // commit & gc
+ RevCommit parent = addCommit(null);
+ gc.gc().get();
+ assertEquals(1L, repositoryBitmapFiles());
+
+ // progress & gc
+ parent = addCommit(parent);
+ gc.gc().get();
+ assertEquals(2L, repositoryBitmapFiles());
+
+ // progress & pack
+ addCommit(parent);
+ tr.packAndPrune();
+
+ assertEquals(1L, gc.getStatistics().numberOfPackFilesSinceBitmap);
+ }
+
+ @Test
+ public void testShouldReportNewObjectsFromTheLatestBitmapWhenRepositoryProgresses()
+ throws Exception {
+ // commit & gc
+ RevCommit parent = addCommit(null);
+ gc.gc().get();
+
+ // progress & gc
+ parent = addCommit(parent);
+ gc.gc().get();
+ assertEquals(0L, gc.getStatistics().numberOfObjectsSinceBitmap);
+
+ // progress & pack
+ addCommit(parent);
+ assertEquals(1L, gc.getStatistics().numberOfObjectsSinceBitmap);
+
+ tr.packAndPrune();
+ assertEquals(4L, gc.getStatistics().numberOfObjectsSinceBitmap);
+ }
+
+ private RevCommit addCommit(RevCommit parent) throws Exception {
+ return tr.branch("master").commit()
+ .author(new PersonIdent("repo-metrics", "repo@metrics.com"))
+ .parent(parent).create();
+ }
+
+ private long repositoryBitmapFiles() throws IOException {
+ return StreamSupport
+ .stream(Files
+ .newDirectoryStream(repo.getObjectDatabase()
+ .getPackDirectory().toPath(), "pack-*.bitmap")
+ .spliterator(), false)
+ .count();
+ }
+
+ private long getNumberOfObjectsInPacks(Collection<Pack> packs) {
+ return packs.stream().mapToLong(pack -> {
+ try {
+ return pack.getObjectCount();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }).sum();
+ }
+
+ private GC configureGC(boolean buildBitmap) {
+ PackConfig pc = new PackConfig(repo.getObjectDatabase().getConfig());
+ pc.setBuildBitmaps(buildBitmap);
+ gc.setPackConfig(pc);
+ return gc;
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ObjectDirectoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ObjectDirectoryTest.java
index 746a0a1..33cbc86 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ObjectDirectoryTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ObjectDirectoryTest.java
@@ -49,7 +49,10 @@
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import java.io.File;
@@ -66,6 +69,7 @@
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
@@ -207,33 +211,35 @@ public void testOpenLooseObjectSuppressStaleFileHandleException()
.fromString("873fb8d667d05436d728c52b1d7a09528e6eb59b");
WindowCursor curs = new WindowCursor(db.getObjectDatabase());
- LooseObjects mock = mock(LooseObjects.class);
+ Config config = new Config();
+ config.setString("core", null, "trustLooseObjectStat", "ALWAYS");
+ LooseObjects spy = Mockito.spy(new LooseObjects(config, trash));
UnpackedObjectCache unpackedObjectCacheMock = mock(
UnpackedObjectCache.class);
- Mockito.when(mock.getObjectLoader(any(), any(), any()))
- .thenThrow(new IOException("Stale File Handle"));
- Mockito.when(mock.open(curs, id)).thenCallRealMethod();
- Mockito.when(mock.unpackedObjectCache())
- .thenReturn(unpackedObjectCacheMock);
+ doThrow(new IOException("Stale File Handle")).when(spy)
+ .getObjectLoader(any(), any(), any());
+ doReturn(unpackedObjectCacheMock).when(spy).unpackedObjectCache();
- assertNull(mock.open(curs, id));
+ assertNull(spy.open(curs, id));
verify(unpackedObjectCacheMock).remove(id);
}
- @Test
+ @Test(expected = IOException.class)
public void testOpenLooseObjectPropagatesIOExceptions() throws Exception {
ObjectId id = ObjectId
.fromString("873fb8d667d05436d728c52b1d7a09528e6eb59b");
WindowCursor curs = new WindowCursor(db.getObjectDatabase());
- LooseObjects mock = mock(LooseObjects.class);
+ Config config = new Config();
+ config.setString("core", null, "trustLooseObjectStat", "NEVER");
+ LooseObjects spy = spy(new LooseObjects(config,
+ db.getObjectDatabase().getDirectory()));
- Mockito.when(mock.getObjectLoader(any(), any(), any()))
- .thenThrow(new IOException("some IO failure"));
- Mockito.when(mock.open(curs, id)).thenCallRealMethod();
+ doThrow(new IOException("some IO failure")).when(spy)
+ .getObjectLoader(any(), any(), any());
- assertThrows(IOException.class, () -> mock.open(curs, id));
+ spy.open(curs, id);
}
@Test
@@ -243,17 +249,18 @@ public void testWindowCursorGetCommitGraph() throws Exception {
db.getConfig().setBoolean(ConfigConstants.CONFIG_GC_SECTION, null,
ConfigConstants.CONFIG_KEY_WRITE_COMMIT_GRAPH, true);
- WindowCursor curs = new WindowCursor(db.getObjectDatabase());
- assertTrue(curs.getCommitGraph().isEmpty());
- commitFile("file.txt", "content", "master");
- GC gc = new GC(db);
- gc.gc().get();
- assertTrue(curs.getCommitGraph().isPresent());
+ try (WindowCursor curs = new WindowCursor(db.getObjectDatabase())) {
+ assertTrue(curs.getCommitGraph().isEmpty());
+ commitFile("file.txt", "content", "master");
+ GC gc = new GC(db);
+ gc.gc().get();
+ assertTrue(curs.getCommitGraph().isPresent());
- db.getConfig().setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
- ConfigConstants.CONFIG_COMMIT_GRAPH, false);
+ db.getConfig().setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+ ConfigConstants.CONFIG_COMMIT_GRAPH, false);
- assertTrue(curs.getCommitGraph().isEmpty());
+ assertTrue(curs.getCommitGraph().isEmpty());
+ }
}
@Test
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackIndexTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackIndexTestCase.java
index 24bdc4a..1f934ac 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackIndexTestCase.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackIndexTestCase.java
@@ -13,6 +13,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.File;
@@ -25,6 +26,7 @@
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.file.PackIndex.MutableEntry;
import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.MutableObjectId;
import org.eclipse.jgit.lib.ObjectId;
import org.junit.Test;
@@ -99,6 +101,39 @@ public void testIteratorMethodsContract() {
}
}
+ @Test
+ public void testIteratorMutableEntryCompareTo() {
+ Iterator<PackIndex.MutableEntry> iterA = smallIdx.iterator();
+ Iterator<PackIndex.MutableEntry> iterB = smallIdx.iterator();
+
+ MutableEntry aEntry = iterA.next();
+ iterB.next();
+ MutableEntry bEntry = iterB.next();
+ // b is one ahead
+ assertTrue(aEntry.compareBySha1To(bEntry) < 0);
+ assertTrue(bEntry.compareBySha1To(aEntry) > 0);
+
+ // advance a, now should be equal
+ assertEquals(0, iterA.next().compareBySha1To(bEntry));
+ }
+
+ @Test
+ public void testIteratorMutableEntryCopyTo() {
+ Iterator<PackIndex.MutableEntry> it = smallIdx.iterator();
+
+ MutableObjectId firstOidCopy = new MutableObjectId();
+ MutableEntry next = it.next();
+ next.copyOidTo(firstOidCopy);
+ ObjectId firstImmutable = next.toObjectId();
+
+ MutableEntry second = it.next();
+
+ // The copy has the right value after "next"
+ assertTrue(firstImmutable.equals(firstOidCopy));
+ assertFalse("iterator has moved",
+ second.toObjectId().equals(firstImmutable));
+ }
+
/**
* Test results of iterator comparing to content of well-known (prepared)
* small index.
@@ -106,22 +141,22 @@ public void testIteratorMethodsContract() {
@Test
public void testIteratorReturnedValues1() {
Iterator<PackIndex.MutableEntry> iter = smallIdx.iterator();
- assertEquals("4b825dc642cb6eb9a060e54bf8d69288fbee4904", iter.next()
- .name());
- assertEquals("540a36d136cf413e4b064c2b0e0a4db60f77feab", iter.next()
- .name());
- assertEquals("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259", iter.next()
- .name());
- assertEquals("6ff87c4664981e4397625791c8ea3bbb5f2279a3", iter.next()
- .name());
- assertEquals("82c6b885ff600be425b4ea96dee75dca255b69e7", iter.next()
- .name());
- assertEquals("902d5476fa249b7abc9d84c611577a81381f0327", iter.next()
- .name());
- assertEquals("aabf2ffaec9b497f0950352b3e582d73035c2035", iter.next()
- .name());
- assertEquals("c59759f143fb1fe21c197981df75a7ee00290799", iter.next()
- .name());
+ assertEquals("4b825dc642cb6eb9a060e54bf8d69288fbee4904",
+ iter.next().name());
+ assertEquals("540a36d136cf413e4b064c2b0e0a4db60f77feab",
+ iter.next().name());
+ assertEquals("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259",
+ iter.next().name());
+ assertEquals("6ff87c4664981e4397625791c8ea3bbb5f2279a3",
+ iter.next().name());
+ assertEquals("82c6b885ff600be425b4ea96dee75dca255b69e7",
+ iter.next().name());
+ assertEquals("902d5476fa249b7abc9d84c611577a81381f0327",
+ iter.next().name());
+ assertEquals("aabf2ffaec9b497f0950352b3e582d73035c2035",
+ iter.next().name());
+ assertEquals("c59759f143fb1fe21c197981df75a7ee00290799",
+ iter.next().name());
assertFalse(iter.hasNext());
}
@@ -198,16 +233,16 @@ public void testCompareEntriesOffsetsWithGetOffsets() {
@Test
public void testIteratorReturnedValues2() {
Iterator<PackIndex.MutableEntry> iter = denseIdx.iterator();
- while (!iter.next().name().equals(
- "0a3d7772488b6b106fb62813c4d6d627918d9181")) {
+ while (!iter.next().name()
+ .equals("0a3d7772488b6b106fb62813c4d6d627918d9181")) {
// just iterating
}
- assertEquals("1004d0d7ac26fbf63050a234c9b88a46075719d3", iter.next()
- .name()); // same level-1
- assertEquals("10da5895682013006950e7da534b705252b03be6", iter.next()
- .name()); // same level-1
- assertEquals("1203b03dc816ccbb67773f28b3c19318654b0bc8", iter.next()
- .name());
+ assertEquals("1004d0d7ac26fbf63050a234c9b88a46075719d3",
+ iter.next().name()); // same level-1
+ assertEquals("10da5895682013006950e7da534b705252b03be6",
+ iter.next().name()); // same level-1
+ assertEquals("1203b03dc816ccbb67773f28b3c19318654b0bc8",
+ iter.next().name());
}
@Test
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java
index 2bafde6..baa0182 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java
@@ -90,25 +90,26 @@ public void refDirectorySetup() throws Exception {
@Test
public void testCreate() throws IOException {
// setUp above created the directory. We just have to test it.
- File d = diskRepo.getDirectory();
+ File gitDir = diskRepo.getDirectory();
+ File commonDir = diskRepo.getCommonDirectory();
assertSame(diskRepo, refdir.getRepository());
- assertTrue(new File(d, "refs").isDirectory());
- assertTrue(new File(d, "logs").isDirectory());
- assertTrue(new File(d, "logs/refs").isDirectory());
- assertFalse(new File(d, "packed-refs").exists());
+ assertTrue(new File(commonDir, "refs").isDirectory());
+ assertTrue(new File(commonDir, "logs").isDirectory());
+ assertTrue(new File(commonDir, "logs/refs").isDirectory());
+ assertFalse(new File(commonDir, "packed-refs").exists());
- assertTrue(new File(d, "refs/heads").isDirectory());
- assertTrue(new File(d, "refs/tags").isDirectory());
- assertEquals(2, new File(d, "refs").list().length);
- assertEquals(0, new File(d, "refs/heads").list().length);
- assertEquals(0, new File(d, "refs/tags").list().length);
+ assertTrue(new File(commonDir, "refs/heads").isDirectory());
+ assertTrue(new File(commonDir, "refs/tags").isDirectory());
+ assertEquals(2, new File(commonDir, "refs").list().length);
+ assertEquals(0, new File(commonDir, "refs/heads").list().length);
+ assertEquals(0, new File(commonDir, "refs/tags").list().length);
- assertTrue(new File(d, "logs/refs/heads").isDirectory());
- assertFalse(new File(d, "logs/HEAD").exists());
- assertEquals(0, new File(d, "logs/refs/heads").list().length);
+ assertTrue(new File(commonDir, "logs/refs/heads").isDirectory());
+ assertFalse(new File(gitDir, "logs/HEAD").exists());
+ assertEquals(0, new File(commonDir, "logs/refs/heads").list().length);
- assertEquals("ref: refs/heads/master\n", read(new File(d, HEAD)));
+ assertEquals("ref: refs/heads/master\n", read(new File(gitDir, HEAD)));
}
@Test(expected = UnsupportedOperationException.class)
@@ -1382,7 +1383,7 @@ private void writeLooseRef(String name, String content) throws IOException {
}
private void deleteLooseRef(String name) {
- File path = new File(diskRepo.getDirectory(), name);
+ File path = new File(diskRepo.getCommonDirectory(), name);
assertTrue("deleted " + name, path.delete());
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java
index cb977bd..acc36d7 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java
@@ -40,6 +40,7 @@
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.lib.RefRename;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.RefUpdate.Result;
@@ -111,16 +112,17 @@ public void testNoCacheObjectIdSubclass() throws IOException {
assertNotSame(newid, r.getObjectId());
assertSame(ObjectId.class, r.getObjectId().getClass());
assertEquals(newid, r.getObjectId());
- List<ReflogEntry> reverseEntries1 = db
+ List<ReflogEntry> reverseEntries1 = db.getRefDatabase()
.getReflogReader("refs/heads/abc").getReverseEntries();
ReflogEntry entry1 = reverseEntries1.get(0);
assertEquals(1, reverseEntries1.size());
assertEquals(ObjectId.zeroId(), entry1.getOldId());
assertEquals(r.getObjectId(), entry1.getNewId());
- assertEquals(new PersonIdent(db).toString(), entry1.getWho().toString());
+ assertEquals(new PersonIdent(db).toString(),
+ entry1.getWho().toString());
assertEquals("", entry1.getComment());
- List<ReflogEntry> reverseEntries2 = db.getReflogReader("HEAD")
- .getReverseEntries();
+ List<ReflogEntry> reverseEntries2 = db.getRefDatabase()
+ .getReflogReader("HEAD").getReverseEntries();
assertEquals(0, reverseEntries2.size());
}
@@ -136,8 +138,11 @@ public void testNewNamespaceConflictWithLoosePrefixNameExists()
final RefUpdate ru2 = updateRef(newRef2);
Result update2 = ru2.update();
assertEquals(Result.LOCK_FAILURE, update2);
- assertEquals(1, db.getReflogReader("refs/heads/z").getReverseEntries().size());
- assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size());
+ RefDatabase refDb = db.getRefDatabase();
+ assertEquals(1, refDb.getReflogReader("refs/heads/z")
+ .getReverseEntries().size());
+ assertEquals(0,
+ refDb.getReflogReader("HEAD").getReverseEntries().size());
}
@Test
@@ -147,8 +152,10 @@ public void testNewNamespaceConflictWithPackedPrefixNameExists()
final RefUpdate ru = updateRef(newRef);
Result update = ru.update();
assertEquals(Result.LOCK_FAILURE, update);
- assertNull(db.getReflogReader("refs/heads/master/x"));
- assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size());
+ RefDatabase refDb = db.getRefDatabase();
+ assertNull(refDb.getReflogReader("refs/heads/master/x"));
+ assertEquals(0,
+ refDb.getReflogReader("HEAD").getReverseEntries().size());
}
@Test
@@ -163,9 +170,12 @@ public void testNewNamespaceConflictWithLoosePrefixOfExisting()
final RefUpdate ru2 = updateRef(newRef2);
Result update2 = ru2.update();
assertEquals(Result.LOCK_FAILURE, update2);
- assertEquals(1, db.getReflogReader("refs/heads/z/a").getReverseEntries().size());
- assertNull(db.getReflogReader("refs/heads/z"));
- assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size());
+ RefDatabase refDb = db.getRefDatabase();
+ assertEquals(1, refDb.getReflogReader("refs/heads/z/a")
+ .getReverseEntries().size());
+ assertNull(refDb.getReflogReader("refs/heads/z"));
+ assertEquals(0,
+ refDb.getReflogReader("HEAD").getReverseEntries().size());
}
@Test
@@ -175,8 +185,10 @@ public void testNewNamespaceConflictWithPackedPrefixOfExisting()
final RefUpdate ru = updateRef(newRef);
Result update = ru.update();
assertEquals(Result.LOCK_FAILURE, update);
- assertNull(db.getReflogReader("refs/heads/prefix"));
- assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size());
+ RefDatabase refDb = db.getRefDatabase();
+ assertNull(refDb.getReflogReader("refs/heads/prefix"));
+ assertEquals(0,
+ refDb.getReflogReader("HEAD").getReverseEntries().size());
}
/**
@@ -197,8 +209,11 @@ public void testDeleteHEADreferencedRef() throws IOException {
Result delete = updateRef2.delete();
assertEquals(Result.REJECTED_CURRENT_BRANCH, delete);
assertEquals(pid, db.resolve("refs/heads/master"));
- assertEquals(1,db.getReflogReader("refs/heads/master").getReverseEntries().size());
- assertEquals(0,db.getReflogReader("HEAD").getReverseEntries().size());
+ RefDatabase refDb = db.getRefDatabase();
+ assertEquals(1, refDb.getReflogReader("refs/heads/master")
+ .getReverseEntries().size());
+ assertEquals(0,
+ refDb.getReflogReader("HEAD").getReverseEntries().size());
}
@Test
@@ -209,7 +224,8 @@ public void testWriteReflog() throws IOException {
updateRef.setForceUpdate(true);
Result update = updateRef.update();
assertEquals(Result.FORCED, update);
- assertEquals(1,db.getReflogReader("refs/heads/master").getReverseEntries().size());
+ assertEquals(1, db.getRefDatabase().getReflogReader("refs/heads/master")
+ .getReverseEntries().size());
}
@Test
@@ -219,15 +235,18 @@ public void testLooseDelete() throws IOException {
ref.update(); // create loose ref
ref = updateRef(newRef); // refresh
delete(ref, Result.NO_CHANGE);
- assertNull(db.getReflogReader("refs/heads/abc"));
+ assertNull(db.getRefDatabase().getReflogReader("refs/heads/abc"));
}
@Test
public void testDeleteHead() throws IOException {
final RefUpdate ref = updateRef(Constants.HEAD);
delete(ref, Result.REJECTED_CURRENT_BRANCH, true, false);
- assertEquals(0, db.getReflogReader("refs/heads/master").getReverseEntries().size());
- assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size());
+ RefDatabase refDb = db.getRefDatabase();
+ assertEquals(0, refDb.getReflogReader("refs/heads/master")
+ .getReverseEntries().size());
+ assertEquals(0,
+ refDb.getReflogReader("HEAD").getReverseEntries().size());
}
@Test
@@ -423,7 +442,7 @@ public void testUpdateRefDetached() throws Exception {
// the branch HEAD referred to is left untouched
assertEquals(pid, db.resolve("refs/heads/master"));
- ReflogReader reflogReader = db.getReflogReader("HEAD");
+ ReflogReader reflogReader = db.getRefDatabase().getReflogReader("HEAD");
ReflogEntry e = reflogReader.getReverseEntries().get(0);
assertEquals(pid, e.getOldId());
assertEquals(ppid, e.getNewId());
@@ -453,7 +472,7 @@ public void testUpdateRefDetachedUnbornHead() throws Exception {
// the branch HEAD referred to is left untouched
assertNull(db.resolve("refs/heads/unborn"));
- ReflogReader reflogReader = db.getReflogReader("HEAD");
+ ReflogReader reflogReader = db.getRefDatabase().getReflogReader("HEAD");
ReflogEntry e = reflogReader.getReverseEntries().get(0);
assertEquals(ObjectId.zeroId(), e.getOldId());
assertEquals(ppid, e.getNewId());
@@ -691,9 +710,12 @@ public void testRenameBranchNoPreviousLog() throws IOException {
assertEquals(Result.RENAMED, result);
assertEquals(rb, db.resolve("refs/heads/new/name"));
assertNull(db.resolve("refs/heads/b"));
- assertEquals(1, db.getReflogReader("new/name").getReverseEntries().size());
- assertEquals("Branch: renamed b to new/name", db.getReflogReader("new/name")
- .getLastEntry().getComment());
+ RefDatabase refDb = db.getRefDatabase();
+ assertEquals(1, refDb.getReflogReader("refs/heads/new/name")
+ .getReverseEntries().size());
+ assertEquals("Branch: renamed b to new/name",
+ refDb.getReflogReader("refs/heads/new/name").getLastEntry()
+ .getComment());
assertFalse(new File(db.getDirectory(), "logs/refs/heads/b").exists());
assertEquals(oldHead, db.resolve(Constants.HEAD)); // unchanged
}
@@ -713,11 +735,15 @@ public void testRenameBranchHasPreviousLog() throws IOException {
assertEquals(Result.RENAMED, result);
assertEquals(rb, db.resolve("refs/heads/new/name"));
assertNull(db.resolve("refs/heads/b"));
- assertEquals(2, db.getReflogReader("new/name").getReverseEntries().size());
- assertEquals("Branch: renamed b to new/name", db.getReflogReader("new/name")
- .getLastEntry().getComment());
- assertEquals("Just a message", db.getReflogReader("new/name")
- .getReverseEntries().get(1).getComment());
+ RefDatabase refDb = db.getRefDatabase();
+ assertEquals(2, refDb.getReflogReader("refs/heads/new/name")
+ .getReverseEntries().size());
+ assertEquals("Branch: renamed b to new/name",
+ refDb.getReflogReader("refs/heads/new/name").getLastEntry()
+ .getComment());
+ assertEquals("Just a message",
+ refDb.getReflogReader("refs/heads/new/name").getReverseEntries()
+ .get(1).getComment());
assertFalse(new File(db.getDirectory(), "logs/refs/heads/b").exists());
assertEquals(oldHead, db.resolve(Constants.HEAD)); // unchanged
}
@@ -737,13 +763,20 @@ public void testRenameCurrentBranch() throws IOException {
assertEquals(Result.RENAMED, result);
assertEquals(rb, db.resolve("refs/heads/new/name"));
assertNull(db.resolve("refs/heads/b"));
- assertEquals("Branch: renamed b to new/name", db.getReflogReader(
- "new/name").getLastEntry().getComment());
+ RefDatabase refDb = db.getRefDatabase();
+ assertEquals("Branch: renamed b to new/name",
+ refDb.getReflogReader("refs/heads/new/name").getLastEntry()
+ .getComment());
assertFalse(new File(db.getDirectory(), "logs/refs/heads/b").exists());
assertEquals(rb, db.resolve(Constants.HEAD));
- assertEquals(2, db.getReflogReader("new/name").getReverseEntries().size());
- assertEquals("Branch: renamed b to new/name", db.getReflogReader("new/name").getReverseEntries().get(0).getComment());
- assertEquals("Just a message", db.getReflogReader("new/name").getReverseEntries().get(1).getComment());
+ assertEquals(2, refDb.getReflogReader("refs/heads/new/name")
+ .getReverseEntries().size());
+ assertEquals("Branch: renamed b to new/name",
+ refDb.getReflogReader("refs/heads/new/name").getReverseEntries()
+ .get(0).getComment());
+ assertEquals("Just a message",
+ refDb.getReflogReader("refs/heads/new/name").getReverseEntries()
+ .get(1).getComment());
}
@Test
@@ -766,11 +799,17 @@ public void testRenameBranchAlsoInPack() throws IOException {
assertEquals(Result.RENAMED, result);
assertEquals(rb2, db.resolve("refs/heads/new/name"));
assertNull(db.resolve("refs/heads/b"));
- assertEquals("Branch: renamed b to new/name", db.getReflogReader(
- "new/name").getLastEntry().getComment());
- assertEquals(3, db.getReflogReader("refs/heads/new/name").getReverseEntries().size());
- assertEquals("Branch: renamed b to new/name", db.getReflogReader("refs/heads/new/name").getReverseEntries().get(0).getComment());
- assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size());
+ RefDatabase refDb = db.getRefDatabase();
+ assertEquals("Branch: renamed b to new/name",
+ refDb.getReflogReader("refs/heads/new/name").getLastEntry()
+ .getComment());
+ assertEquals(3, refDb.getReflogReader("refs/heads/new/name")
+ .getReverseEntries().size());
+ assertEquals("Branch: renamed b to new/name",
+ refDb.getReflogReader("refs/heads/new/name").getReverseEntries()
+ .get(0).getComment());
+ assertEquals(0,
+ refDb.getReflogReader("HEAD").getReverseEntries().size());
// make sure b's log file is gone too.
assertFalse(new File(db.getDirectory(), "logs/refs/heads/b").exists());
@@ -789,9 +828,10 @@ public void tryRenameWhenLocked(String toLock, String fromName,
ObjectId oldfromId = db.resolve(fromName);
ObjectId oldHeadId = db.resolve(Constants.HEAD);
writeReflog(db, oldfromId, "Just a message", fromName);
- List<ReflogEntry> oldFromLog = db
+ RefDatabase refDb = db.getRefDatabase();
+ List<ReflogEntry> oldFromLog = refDb
.getReflogReader(fromName).getReverseEntries();
- List<ReflogEntry> oldHeadLog = oldHeadId != null ? db
+ List<ReflogEntry> oldHeadLog = oldHeadId != null ? refDb
.getReflogReader(Constants.HEAD).getReverseEntries() : null;
assertTrue("internal check, we have a log", new File(db.getDirectory(),
@@ -818,10 +858,10 @@ public void tryRenameWhenLocked(String toLock, String fromName,
assertEquals(oldHeadId, db.resolve(Constants.HEAD));
assertEquals(oldfromId, db.resolve(fromName));
assertNull(db.resolve(toName));
- assertEquals(oldFromLog.toString(), db.getReflogReader(fromName)
+ assertEquals(oldFromLog.toString(), refDb.getReflogReader(fromName)
.getReverseEntries().toString());
if (oldHeadId != null && oldHeadLog != null)
- assertEquals(oldHeadLog.toString(), db.getReflogReader(
+ assertEquals(oldHeadLog.toString(), refDb.getReflogReader(
Constants.HEAD).getReverseEntries().toString());
} finally {
lockFile.unlock();
@@ -942,15 +982,18 @@ public void testRenameRefNameColission1avoided() throws IOException {
assertEquals(Result.RENAMED, result);
assertNull(db.resolve("refs/heads/a"));
assertEquals(rb, db.resolve("refs/heads/a/b"));
- assertEquals(3, db.getReflogReader("a/b").getReverseEntries().size());
- assertEquals("Branch: renamed a to a/b", db.getReflogReader("a/b")
- .getReverseEntries().get(0).getComment());
- assertEquals("Just a message", db.getReflogReader("a/b")
+ RefDatabase refDb = db.getRefDatabase();
+ assertEquals(3, refDb.getReflogReader("refs/heads/a/b")
+ .getReverseEntries().size());
+ assertEquals("Branch: renamed a to a/b",
+ refDb.getReflogReader("refs/heads/a/b").getReverseEntries()
+ .get(0).getComment());
+ assertEquals("Just a message", refDb.getReflogReader("refs/heads/a/b")
.getReverseEntries().get(1).getComment());
- assertEquals("Setup", db.getReflogReader("a/b").getReverseEntries()
- .get(2).getComment());
+ assertEquals("Setup", refDb.getReflogReader("refs/heads/a/b")
+ .getReverseEntries().get(2).getComment());
// same thing was logged to HEAD
- assertEquals("Branch: renamed a to a/b", db.getReflogReader("HEAD")
+ assertEquals("Branch: renamed a to a/b", refDb.getReflogReader("HEAD")
.getReverseEntries().get(0).getComment());
}
@@ -978,15 +1021,20 @@ public void testRenameRefNameColission2avoided() throws IOException {
assertNull(db.resolve("refs/heads/prefix/a"));
assertEquals(rb, db.resolve("refs/heads/prefix"));
- assertEquals(3, db.getReflogReader("prefix").getReverseEntries().size());
- assertEquals("Branch: renamed prefix/a to prefix", db.getReflogReader(
- "prefix").getReverseEntries().get(0).getComment());
- assertEquals("Just a message", db.getReflogReader("prefix")
- .getReverseEntries().get(1).getComment());
- assertEquals("Setup", db.getReflogReader("prefix").getReverseEntries()
- .get(2).getComment());
- assertEquals("Branch: renamed prefix/a to prefix", db.getReflogReader(
- "HEAD").getReverseEntries().get(0).getComment());
+ RefDatabase refDb = db.getRefDatabase();
+ assertEquals(3, refDb.getReflogReader("refs/heads/prefix")
+ .getReverseEntries().size());
+ assertEquals("Branch: renamed prefix/a to prefix",
+ refDb.getReflogReader("refs/heads/prefix").getReverseEntries()
+ .get(0).getComment());
+ assertEquals("Just a message",
+ refDb.getReflogReader("refs/heads/prefix").getReverseEntries()
+ .get(1).getComment());
+ assertEquals("Setup", refDb.getReflogReader("refs/heads/prefix")
+ .getReverseEntries().get(2).getComment());
+ assertEquals("Branch: renamed prefix/a to prefix",
+ refDb.getReflogReader("HEAD").getReverseEntries().get(0)
+ .getComment());
}
@Test
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogReaderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogReaderTest.java
index dc0e749..16645cb 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogReaderTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogReaderTest.java
@@ -27,6 +27,7 @@
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.lib.ReflogEntry;
import org.eclipse.jgit.lib.ReflogReader;
import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
@@ -154,18 +155,22 @@ public void testReadRightLog() throws Exception {
setupReflog("logs/refs/heads/a", aLine);
setupReflog("logs/refs/heads/master", masterLine);
setupReflog("logs/HEAD", headLine);
- assertEquals("branch: change to master", db.getReflogReader("master")
- .getLastEntry().getComment());
- assertEquals("branch: change to a", db.getReflogReader("a")
- .getLastEntry().getComment());
- assertEquals("branch: change to HEAD", db.getReflogReader("HEAD")
- .getLastEntry().getComment());
+ RefDatabase refDb = db.getRefDatabase();
+ assertEquals("branch: change to master",
+ refDb.getReflogReader("refs/heads/master").getLastEntry()
+ .getComment());
+ assertEquals("branch: change to a",
+ refDb.getReflogReader("refs/heads/a").getLastEntry()
+ .getComment());
+ assertEquals("branch: change to HEAD",
+ refDb.getReflogReader("HEAD").getLastEntry().getComment());
}
@Test
public void testReadLineWithMissingComment() throws Exception {
setupReflog("logs/refs/heads/master", oneLineWithoutComment);
- final ReflogReader reader = db.getReflogReader("master");
+ final ReflogReader reader = db.getRefDatabase()
+ .getReflogReader("refs/heads/master");
ReflogEntry e = reader.getLastEntry();
assertEquals(ObjectId
.fromString("da85355dfc525c9f6f3927b876f379f46ccf826e"), e
@@ -183,15 +188,18 @@ public void testReadLineWithMissingComment() throws Exception {
@Test
public void testNoLog() throws Exception {
- assertEquals(0, db.getReflogReader("master").getReverseEntries().size());
- assertNull(db.getReflogReader("master").getLastEntry());
+ RefDatabase refDb = db.getRefDatabase();
+ assertEquals(0,
+ refDb.getReflogReader("refs/heads/master").getReverseEntries()
+ .size());
+ assertNull(refDb.getReflogReader("refs/heads/master").getLastEntry());
}
@Test
public void testCheckout() throws Exception {
setupReflog("logs/HEAD", switchBranch);
- List<ReflogEntry> entries = db.getReflogReader(Constants.HEAD)
- .getReverseEntries();
+ List<ReflogEntry> entries = db.getRefDatabase()
+ .getReflogReader(Constants.HEAD).getReverseEntries();
assertEquals(1, entries.size());
ReflogEntry entry = entries.get(0);
CheckoutEntry checkout = entry.parseCheckout();
@@ -238,7 +246,7 @@ public void testSpecificEntryNumber() throws Exception {
private void setupReflog(String logName, byte[] data)
throws FileNotFoundException, IOException {
- File logfile = new File(db.getDirectory(), logName);
+ File logfile = new File(db.getCommonDirectory(), logName);
if (!logfile.getParentFile().mkdirs()
&& !logfile.getParentFile().isDirectory()) {
throw new IOException(
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogWriterTest.java
index 8d0e99d..a836333 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogWriterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ReflogWriterTest.java
@@ -16,6 +16,8 @@
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.time.Instant;
+import java.time.ZoneOffset;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
@@ -32,7 +34,7 @@ public void shouldFilterLineFeedFromMessage() throws Exception {
ReflogWriter writer =
new ReflogWriter((RefDirectory) db.getRefDatabase());
PersonIdent ident = new PersonIdent("John Doe", "john@doe.com",
- 1243028200000L, 120);
+ Instant.ofEpochMilli(1243028200000L), ZoneOffset.ofHours(2));
ObjectId oldId = ObjectId
.fromString("da85355dfc525c9f6f3927b876f379f46ccf826e");
ObjectId newId = ObjectId
@@ -48,7 +50,7 @@ public void shouldFilterLineFeedFromMessage() throws Exception {
private void readReflog(byte[] buffer)
throws FileNotFoundException, IOException {
- File logfile = new File(db.getDirectory(), "logs/refs/heads/master");
+ File logfile = new File(db.getCommonDirectory(), "logs/refs/heads/master");
if (!logfile.getParentFile().mkdirs()
&& !logfile.getParentFile().isDirectory()) {
throw new IOException(
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java
index 49e8a7b..e067beb 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java
@@ -28,6 +28,7 @@
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.time.Instant;
+import java.time.ZoneOffset;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
@@ -374,8 +375,10 @@ public void test008_FailOnWrongVersion() throws IOException {
public void test009_CreateCommitOldFormat() throws IOException {
final ObjectId treeId = insertTree(new TreeFormatter());
final CommitBuilder c = new CommitBuilder();
- c.setAuthor(new PersonIdent(author, 1154236443000L, -4 * 60));
- c.setCommitter(new PersonIdent(committer, 1154236443000L, -4 * 60));
+ c.setAuthor(new PersonIdent(author,
+ Instant.ofEpochMilli(1154236443000L), ZoneOffset.ofHours(-4)));
+ c.setCommitter(new PersonIdent(committer,
+ Instant.ofEpochMilli(1154236443000L), ZoneOffset.ofHours(-4)));
c.setMessage("A Commit\n");
c.setTreeId(treeId);
assertEquals(treeId, c.getTreeId());
@@ -411,7 +414,8 @@ public void test020_createBlobTag() throws IOException {
final TagBuilder t = new TagBuilder();
t.setObjectId(emptyId, Constants.OBJ_BLOB);
t.setTag("test020");
- t.setTagger(new PersonIdent(author, 1154236443000L, -4 * 60));
+ t.setTagger(new PersonIdent(author,
+ Instant.ofEpochMilli(1154236443000L), ZoneOffset.ofHours(-4)));
t.setMessage("test020 tagged\n");
ObjectId actid = insertTag(t);
assertEquals("6759556b09fbb4fd8ae5e315134481cc25d46954", actid.name());
@@ -419,8 +423,9 @@ public void test020_createBlobTag() throws IOException {
RevTag mapTag = parseTag(actid);
assertEquals(Constants.OBJ_BLOB, mapTag.getObject().getType());
assertEquals("test020 tagged\n", mapTag.getFullMessage());
- assertEquals(new PersonIdent(author, 1154236443000L, -4 * 60), mapTag
- .getTaggerIdent());
+ assertEquals(new PersonIdent(author,
+ Instant.ofEpochMilli(1154236443000L), ZoneOffset.ofHours(-4)),
+ mapTag.getTaggerIdent());
assertEquals("e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", mapTag
.getObject().getId().name());
}
@@ -434,7 +439,8 @@ public void test021_createTreeTag() throws IOException {
final TagBuilder t = new TagBuilder();
t.setObjectId(almostEmptyTreeId, Constants.OBJ_TREE);
t.setTag("test021");
- t.setTagger(new PersonIdent(author, 1154236443000L, -4 * 60));
+ t.setTagger(new PersonIdent(author,
+ Instant.ofEpochMilli(1154236443000L), ZoneOffset.ofHours(-4)));
t.setMessage("test021 tagged\n");
ObjectId actid = insertTag(t);
assertEquals("b0517bc8dbe2096b419d42424cd7030733f4abe5", actid.name());
@@ -442,8 +448,9 @@ public void test021_createTreeTag() throws IOException {
RevTag mapTag = parseTag(actid);
assertEquals(Constants.OBJ_TREE, mapTag.getObject().getType());
assertEquals("test021 tagged\n", mapTag.getFullMessage());
- assertEquals(new PersonIdent(author, 1154236443000L, -4 * 60), mapTag
- .getTaggerIdent());
+ assertEquals(new PersonIdent(author,
+ Instant.ofEpochMilli(1154236443000L), ZoneOffset.ofHours(-4)),
+ mapTag.getTaggerIdent());
assertEquals("417c01c8795a35b8e835113a85a5c0c1c77f67fb", mapTag
.getObject().getId().name());
}
@@ -455,17 +462,18 @@ public void test022_createCommitTag() throws IOException {
almostEmptyTree.append("empty", FileMode.REGULAR_FILE, emptyId);
final ObjectId almostEmptyTreeId = insertTree(almostEmptyTree);
final CommitBuilder almostEmptyCommit = new CommitBuilder();
- almostEmptyCommit.setAuthor(new PersonIdent(author, 1154236443000L,
- -2 * 60)); // not exactly the same
- almostEmptyCommit.setCommitter(new PersonIdent(author, 1154236443000L,
- -2 * 60));
+ almostEmptyCommit.setAuthor(new PersonIdent(author,
+ Instant.ofEpochMilli(1154236443000L), ZoneOffset.ofHours(-2)));
+ almostEmptyCommit.setCommitter(new PersonIdent(author,
+ Instant.ofEpochMilli(1154236443000L), ZoneOffset.ofHours(-2)));
almostEmptyCommit.setMessage("test022\n");
almostEmptyCommit.setTreeId(almostEmptyTreeId);
ObjectId almostEmptyCommitId = insertCommit(almostEmptyCommit);
final TagBuilder t = new TagBuilder();
t.setObjectId(almostEmptyCommitId, Constants.OBJ_COMMIT);
t.setTag("test022");
- t.setTagger(new PersonIdent(author, 1154236443000L, -4 * 60));
+ t.setTagger(new PersonIdent(author,
+ Instant.ofEpochMilli(1154236443000L), ZoneOffset.ofHours(-4)));
t.setMessage("test022 tagged\n");
ObjectId actid = insertTag(t);
assertEquals("0ce2ebdb36076ef0b38adbe077a07d43b43e3807", actid.name());
@@ -473,8 +481,9 @@ public void test022_createCommitTag() throws IOException {
RevTag mapTag = parseTag(actid);
assertEquals(Constants.OBJ_COMMIT, mapTag.getObject().getType());
assertEquals("test022 tagged\n", mapTag.getFullMessage());
- assertEquals(new PersonIdent(author, 1154236443000L, -4 * 60), mapTag
- .getTaggerIdent());
+ assertEquals(new PersonIdent(author,
+ Instant.ofEpochMilli(1154236443000L), ZoneOffset.ofHours(-4)),
+ mapTag.getTaggerIdent());
assertEquals("b5d3b45a96b340441f5abb9080411705c51cc86c", mapTag
.getObject().getId().name());
}
@@ -488,9 +497,9 @@ public void test023_createCommitNonAnullii() throws IOException {
CommitBuilder commit = new CommitBuilder();
commit.setTreeId(almostEmptyTreeId);
commit.setAuthor(new PersonIdent("Joe H\u00e4cker", "joe@example.com",
- 4294967295000L, 60));
+ Instant.ofEpochMilli(4294967295000L), ZoneOffset.ofHours(1)));
commit.setCommitter(new PersonIdent("Joe Hacker", "joe2@example.com",
- 4294967295000L, 60));
+ Instant.ofEpochMilli(4294967295000L), ZoneOffset.ofHours(1)));
commit.setEncoding(UTF_8);
commit.setMessage("\u00dcbergeeks");
ObjectId cid = insertCommit(commit);
@@ -509,9 +518,9 @@ public void test024_createCommitNonAscii() throws IOException {
CommitBuilder commit = new CommitBuilder();
commit.setTreeId(almostEmptyTreeId);
commit.setAuthor(new PersonIdent("Joe H\u00e4cker", "joe@example.com",
- 4294967295000L, 60));
+ Instant.ofEpochMilli(4294967295000L), ZoneOffset.ofHours(1)));
commit.setCommitter(new PersonIdent("Joe Hacker", "joe2@example.com",
- 4294967295000L, 60));
+ Instant.ofEpochMilli(4294967295000L), ZoneOffset.ofHours(1)));
commit.setEncoding(ISO_8859_1);
commit.setMessage("\u00dcbergeeks");
ObjectId cid = insertCommit(commit);
@@ -544,8 +553,10 @@ public void test026_CreateCommitMultipleparents() throws IOException {
.fromString("00b1f73724f493096d1ffa0b0f1f1482dbb8c936"), treeId);
final CommitBuilder c1 = new CommitBuilder();
- c1.setAuthor(new PersonIdent(author, 1154236443000L, -4 * 60));
- c1.setCommitter(new PersonIdent(committer, 1154236443000L, -4 * 60));
+ c1.setAuthor(new PersonIdent(author,
+ Instant.ofEpochMilli(1154236443000L), ZoneOffset.ofHours(-4)));
+ c1.setCommitter(new PersonIdent(committer,
+ Instant.ofEpochMilli(1154236443000L), ZoneOffset.ofHours(-4)));
c1.setMessage("A Commit\n");
c1.setTreeId(treeId);
assertEquals(treeId, c1.getTreeId());
@@ -555,8 +566,10 @@ public void test026_CreateCommitMultipleparents() throws IOException {
assertEquals(cmtid1, actid1);
final CommitBuilder c2 = new CommitBuilder();
- c2.setAuthor(new PersonIdent(author, 1154236443000L, -4 * 60));
- c2.setCommitter(new PersonIdent(committer, 1154236443000L, -4 * 60));
+ c2.setAuthor(new PersonIdent(author,
+ Instant.ofEpochMilli(1154236443000L), ZoneOffset.ofHours(-4)));
+ c2.setCommitter(new PersonIdent(committer,
+ Instant.ofEpochMilli(1154236443000L), ZoneOffset.ofHours(-4)));
c2.setMessage("A Commit 2\n");
c2.setTreeId(treeId);
assertEquals(treeId, c2.getTreeId());
@@ -577,8 +590,10 @@ public void test026_CreateCommitMultipleparents() throws IOException {
assertEquals(actid1, rm2.getParent(0));
final CommitBuilder c3 = new CommitBuilder();
- c3.setAuthor(new PersonIdent(author, 1154236443000L, -4 * 60));
- c3.setCommitter(new PersonIdent(committer, 1154236443000L, -4 * 60));
+ c3.setAuthor(new PersonIdent(author,
+ Instant.ofEpochMilli(1154236443000L), ZoneOffset.ofHours(-4)));
+ c3.setCommitter(new PersonIdent(committer,
+ Instant.ofEpochMilli(1154236443000L), ZoneOffset.ofHours(-4)));
c3.setMessage("A Commit 3\n");
c3.setTreeId(treeId);
assertEquals(treeId, c3.getTreeId());
@@ -600,8 +615,10 @@ public void test026_CreateCommitMultipleparents() throws IOException {
assertEquals(actid2, rm3.getParent(1));
final CommitBuilder c4 = new CommitBuilder();
- c4.setAuthor(new PersonIdent(author, 1154236443000L, -4 * 60));
- c4.setCommitter(new PersonIdent(committer, 1154236443000L, -4 * 60));
+ c4.setAuthor(new PersonIdent(author,
+ Instant.ofEpochMilli(1154236443000L), ZoneOffset.ofHours(-4)));
+ c4.setCommitter(new PersonIdent(committer,
+ Instant.ofEpochMilli(1154236443000L), ZoneOffset.ofHours(-4)));
c4.setMessage("A Commit 4\n");
c4.setTreeId(treeId);
assertEquals(treeId, c3.getTreeId());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/MultiPackIndexWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/MultiPackIndexWriterTest.java
new file mode 100644
index 0000000..82f3eb1
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/MultiPackIndexWriterTest.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2025, Google Inc.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.storage.midx;
+
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.CHUNK_LOOKUP_WIDTH;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.MIDX_CHUNKID_LARGEOFFSETS;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.MIDX_CHUNKID_OBJECTOFFSETS;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.MIDX_CHUNKID_OIDFANOUT;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.MIDX_CHUNKID_OIDLOOKUP;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.MIDX_CHUNKID_PACKNAMES;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.MIDX_CHUNKID_REVINDEX;
+import static org.junit.Assert.assertEquals;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jgit.internal.storage.file.PackIndex;
+import org.eclipse.jgit.junit.FakeIndexFactory;
+import org.eclipse.jgit.junit.FakeIndexFactory.IndexObject;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.util.NB;
+import org.junit.Test;
+
+public class MultiPackIndexWriterTest {
+
+ @Test
+ public void write_allSmallOffsets() throws IOException {
+ PackIndex index1 = indexOf(
+ object("0000000000000000000000000000000000000001", 500),
+ object("0000000000000000000000000000000000000003", 1500),
+ object("0000000000000000000000000000000000000005", 3000));
+ PackIndex index2 = indexOf(
+ object("0000000000000000000000000000000000000002", 500),
+ object("0000000000000000000000000000000000000004", 1500),
+ object("0000000000000000000000000000000000000006", 3000));
+
+ Map<String, PackIndex> data = Map.of("packname1", index1, "packname2",
+ index2);
+
+ MultiPackIndexWriter writer = new MultiPackIndexWriter();
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ writer.write(NullProgressMonitor.INSTANCE, out, data);
+ // header (12 bytes)
+ // + chunkHeader (6 * 12 bytes)
+ // + fanout table (256 * 4 bytes)
+ // + OIDs (6 * 20 bytes)
+ // + (pack, offset) pairs (6 * 8)
+ // + RIDX (6 * 4 bytes)
+ // + packfile names (2 * 10)
+ // + checksum (20)
+ assertEquals(1340, out.size());
+ List<Integer> chunkIds = readChunkIds(out);
+ assertEquals(5, chunkIds.size());
+ assertEquals(0, chunkIds.indexOf(MIDX_CHUNKID_OIDFANOUT));
+ assertEquals(1, chunkIds.indexOf(MIDX_CHUNKID_OIDLOOKUP));
+ assertEquals(2, chunkIds.indexOf(MIDX_CHUNKID_OBJECTOFFSETS));
+ assertEquals(3, chunkIds.indexOf(MIDX_CHUNKID_REVINDEX));
+ assertEquals(4, chunkIds.indexOf(MIDX_CHUNKID_PACKNAMES));
+ }
+
+ @Test
+ public void write_smallOffset_limit() throws IOException {
+ PackIndex index1 = indexOf(
+ object("0000000000000000000000000000000000000001", 500),
+ object("0000000000000000000000000000000000000003", 1500),
+ object("0000000000000000000000000000000000000005", (1L << 32) -1));
+ PackIndex index2 = indexOf(
+ object("0000000000000000000000000000000000000002", 500),
+ object("0000000000000000000000000000000000000004", 1500),
+ object("0000000000000000000000000000000000000006", 3000));
+ Map<String, PackIndex> data =
+ Map.of("packname1", index1, "packname2", index2);
+
+ MultiPackIndexWriter writer = new MultiPackIndexWriter();
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ writer.write(NullProgressMonitor.INSTANCE, out, data);
+ // header (12 bytes)
+ // + chunkHeader (6 * 12 bytes)
+ // + fanout table (256 * 4 bytes)
+ // + OIDs (6 * 20 bytes)
+ // + (pack, offset) pairs (6 * 8)
+ // + RIDX (6 * 4 bytes)
+ // + packfile names (2 * 10)
+ // + checksum (20)
+ assertEquals(1340, out.size());
+ List<Integer> chunkIds = readChunkIds(out);
+ assertEquals(5, chunkIds.size());
+ assertEquals(0, chunkIds.indexOf(MIDX_CHUNKID_OIDFANOUT));
+ assertEquals(1, chunkIds.indexOf(MIDX_CHUNKID_OIDLOOKUP));
+ assertEquals(2, chunkIds.indexOf(MIDX_CHUNKID_OBJECTOFFSETS));
+ assertEquals(3, chunkIds.indexOf(MIDX_CHUNKID_REVINDEX));
+ assertEquals(4, chunkIds.indexOf(MIDX_CHUNKID_PACKNAMES));
+ }
+
+ @Test
+ public void write_largeOffset() throws IOException {
+ PackIndex index1 = indexOf(
+ object("0000000000000000000000000000000000000001", 500),
+ object("0000000000000000000000000000000000000003", 1500),
+ object("0000000000000000000000000000000000000005", 1L << 32));
+ PackIndex index2 = indexOf(
+ object("0000000000000000000000000000000000000002", 500),
+ object("0000000000000000000000000000000000000004", 1500),
+ object("0000000000000000000000000000000000000006", 3000));
+ Map<String, PackIndex> data =
+ Map.of("packname1", index1, "packname2", index2);
+
+ MultiPackIndexWriter writer = new MultiPackIndexWriter();
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ writer.write(NullProgressMonitor.INSTANCE, out, data);
+ // header (12 bytes)
+ // + chunkHeader (7 * 12 bytes)
+ // + fanout table (256 * 4 bytes)
+ // + OIDs (6 * 20 bytes)
+ // + (pack, offset) pairs (6 * 8)
+ // + (large-offset) (1 * 8)
+ // + RIDX (6 * 4 bytes)
+ // + packfile names (2 * 10)
+ // + checksum (20)
+ assertEquals(1360, out.size());
+ List<Integer> chunkIds = readChunkIds(out);
+ assertEquals(6, chunkIds.size());
+ assertEquals(0, chunkIds.indexOf(MIDX_CHUNKID_OIDFANOUT));
+ assertEquals(1, chunkIds.indexOf(MIDX_CHUNKID_OIDLOOKUP));
+ assertEquals(2, chunkIds.indexOf(MIDX_CHUNKID_OBJECTOFFSETS));
+ assertEquals(3, chunkIds.indexOf(MIDX_CHUNKID_LARGEOFFSETS));
+ assertEquals(4, chunkIds.indexOf(MIDX_CHUNKID_REVINDEX));
+ assertEquals(5, chunkIds.indexOf(MIDX_CHUNKID_PACKNAMES));
+ }
+
+ private List<Integer> readChunkIds(ByteArrayOutputStream out) {
+ List<Integer> chunkIds = new ArrayList<>();
+ byte[] raw = out.toByteArray();
+ int numChunks = raw[6];
+ int position = 12;
+ for (int i = 0; i < numChunks; i++) {
+ chunkIds.add(NB.decodeInt32(raw, position));
+ position += CHUNK_LOOKUP_WIDTH;
+ }
+ return chunkIds;
+ }
+
+ private static PackIndex indexOf(IndexObject... objs) {
+ return FakeIndexFactory.indexOf(Arrays.asList(objs));
+ }
+
+ private static IndexObject object(String name, long offset) {
+ return new IndexObject(name, offset);
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/PackIndexMergerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/PackIndexMergerTest.java
new file mode 100644
index 0000000..1d8bde0
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/PackIndexMergerTest.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2025, Google Inc.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.storage.midx;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.eclipse.jgit.internal.storage.file.PackIndex;
+import org.eclipse.jgit.junit.FakeIndexFactory;
+import org.eclipse.jgit.junit.FakeIndexFactory.IndexObject;
+import org.junit.Test;
+
+public class PackIndexMergerTest {
+
+ @Test
+ public void rawIterator_noDuplicates() {
+ PackIndex idxOne = indexOf(
+ oidOffset("0000000000000000000000000000000000000001", 500),
+ oidOffset("0000000000000000000000000000000000000005", 12),
+ oidOffset("0000000000000000000000000000000000000010", 1500));
+ PackIndex idxTwo = indexOf(
+ oidOffset("0000000000000000000000000000000000000002", 501),
+ oidOffset("0000000000000000000000000000000000000003", 13),
+ oidOffset("0000000000000000000000000000000000000015", 1501));
+ PackIndex idxThree = indexOf(
+ oidOffset("0000000000000000000000000000000000000004", 502),
+ oidOffset("0000000000000000000000000000000000000007", 14),
+ oidOffset("0000000000000000000000000000000000000012", 1502));
+ PackIndexMerger merger = new PackIndexMerger(
+ Map.of("p1", idxOne, "p2", idxTwo, "p3", idxThree));
+ assertEquals(9, merger.getUniqueObjectCount());
+ assertEquals(3, merger.getPackCount());
+ assertFalse(merger.needsLargeOffsetsChunk());
+ Iterator<PackIndexMerger.MidxMutableEntry> it = merger.rawIterator();
+ assertNextEntry(it, "0000000000000000000000000000000000000001", 0, 500);
+ assertNextEntry(it, "0000000000000000000000000000000000000002", 1, 501);
+ assertNextEntry(it, "0000000000000000000000000000000000000003", 1, 13);
+ assertNextEntry(it, "0000000000000000000000000000000000000004", 2, 502);
+ assertNextEntry(it, "0000000000000000000000000000000000000005", 0, 12);
+ assertNextEntry(it, "0000000000000000000000000000000000000007", 2, 14);
+ assertNextEntry(it, "0000000000000000000000000000000000000010", 0,
+ 1500);
+ assertNextEntry(it, "0000000000000000000000000000000000000012", 2,
+ 1502);
+ assertNextEntry(it, "0000000000000000000000000000000000000015", 1,
+ 1501);
+ assertFalse(it.hasNext());
+ }
+
+ @Test
+ public void rawIterator_allDuplicates() {
+ PackIndex idxOne = indexOf(
+ oidOffset("0000000000000000000000000000000000000001", 500),
+ oidOffset("0000000000000000000000000000000000000005", 12),
+ oidOffset("0000000000000000000000000000000000000010", 1500));
+ PackIndexMerger merger = new PackIndexMerger(
+ Map.of("p1", idxOne, "p2", idxOne, "p3", idxOne));
+ assertEquals(3, merger.getUniqueObjectCount());
+ assertEquals(3, merger.getPackCount());
+ assertFalse(merger.needsLargeOffsetsChunk());
+ Iterator<PackIndexMerger.MidxMutableEntry> it = merger.rawIterator();
+ assertNextEntry(it, "0000000000000000000000000000000000000001", 0, 500);
+ assertNextEntry(it, "0000000000000000000000000000000000000001", 1, 500);
+ assertNextEntry(it, "0000000000000000000000000000000000000001", 2, 500);
+ assertNextEntry(it, "0000000000000000000000000000000000000005", 0, 12);
+ assertNextEntry(it, "0000000000000000000000000000000000000005", 1, 12);
+ assertNextEntry(it, "0000000000000000000000000000000000000005", 2, 12);
+ assertNextEntry(it, "0000000000000000000000000000000000000010", 0,
+ 1500);
+ assertNextEntry(it, "0000000000000000000000000000000000000010", 1,
+ 1500);
+ assertNextEntry(it, "0000000000000000000000000000000000000010", 2,
+ 1500);
+ assertFalse(it.hasNext());
+ }
+
+ @Test
+ public void bySha1Iterator_noDuplicates() {
+ PackIndex idxOne = indexOf(
+ oidOffset("0000000000000000000000000000000000000001", 500),
+ oidOffset("0000000000000000000000000000000000000005", 12),
+ oidOffset("0000000000000000000000000000000000000010", 1500));
+ PackIndex idxTwo = indexOf(
+ oidOffset("0000000000000000000000000000000000000002", 501),
+ oidOffset("0000000000000000000000000000000000000003", 13),
+ oidOffset("0000000000000000000000000000000000000015", 1501));
+ PackIndex idxThree = indexOf(
+ oidOffset("0000000000000000000000000000000000000004", 502),
+ oidOffset("0000000000000000000000000000000000000007", 14),
+ oidOffset("0000000000000000000000000000000000000012", 1502));
+ PackIndexMerger merger = new PackIndexMerger(
+ Map.of("p1", idxOne, "p2", idxTwo, "p3", idxThree));
+ assertEquals(9, merger.getUniqueObjectCount());
+ assertEquals(3, merger.getPackCount());
+ assertFalse(merger.needsLargeOffsetsChunk());
+ Iterator<PackIndexMerger.MidxMutableEntry> it = merger.bySha1Iterator();
+ assertNextEntry(it, "0000000000000000000000000000000000000001", 0, 500);
+ assertNextEntry(it, "0000000000000000000000000000000000000002", 1, 501);
+ assertNextEntry(it, "0000000000000000000000000000000000000003", 1, 13);
+ assertNextEntry(it, "0000000000000000000000000000000000000004", 2, 502);
+ assertNextEntry(it, "0000000000000000000000000000000000000005", 0, 12);
+ assertNextEntry(it, "0000000000000000000000000000000000000007", 2, 14);
+ assertNextEntry(it, "0000000000000000000000000000000000000010", 0,
+ 1500);
+ assertNextEntry(it, "0000000000000000000000000000000000000012", 2,
+ 1502);
+ assertNextEntry(it, "0000000000000000000000000000000000000015", 1,
+ 1501);
+ assertFalse(it.hasNext());
+ }
+
+ @Test
+ public void bySha1Iterator_allDuplicates() {
+ PackIndex idxOne = indexOf(
+ oidOffset("0000000000000000000000000000000000000001", 500),
+ oidOffset("0000000000000000000000000000000000000005", 12),
+ oidOffset("0000000000000000000000000000000000000010", 1500));
+ PackIndexMerger merger = new PackIndexMerger(
+ Map.of("p1", idxOne, "p2", idxOne, "p3", idxOne));
+ assertEquals(3, merger.getUniqueObjectCount());
+ assertEquals(3, merger.getPackCount());
+ assertFalse(merger.needsLargeOffsetsChunk());
+ Iterator<PackIndexMerger.MidxMutableEntry> it = merger.bySha1Iterator();
+ assertNextEntry(it, "0000000000000000000000000000000000000001", 0, 500);
+ assertNextEntry(it, "0000000000000000000000000000000000000005", 0, 12);
+ assertNextEntry(it, "0000000000000000000000000000000000000010", 0,
+ 1500);
+ assertFalse(it.hasNext());
+ }
+
+ @Test
+ public void bySha1Iterator_differentIndexSizes() {
+ PackIndex idxOne = indexOf(
+ oidOffset("0000000000000000000000000000000000000010", 1500));
+ PackIndex idxTwo = indexOf(
+ oidOffset("0000000000000000000000000000000000000002", 500),
+ oidOffset("0000000000000000000000000000000000000003", 12));
+ PackIndex idxThree = indexOf(
+ oidOffset("0000000000000000000000000000000000000004", 500),
+ oidOffset("0000000000000000000000000000000000000007", 12),
+ oidOffset("0000000000000000000000000000000000000012", 1500));
+ PackIndexMerger merger = new PackIndexMerger(
+ Map.of("p1", idxOne, "p2", idxTwo, "p3", idxThree));
+ assertEquals(6, merger.getUniqueObjectCount());
+ assertEquals(3, merger.getPackCount());
+ assertFalse(merger.needsLargeOffsetsChunk());
+ Iterator<PackIndexMerger.MidxMutableEntry> it = merger.bySha1Iterator();
+ assertNextEntry(it, "0000000000000000000000000000000000000002", 1, 500);
+ assertNextEntry(it, "0000000000000000000000000000000000000003", 1, 12);
+ assertNextEntry(it, "0000000000000000000000000000000000000004", 2, 500);
+ assertNextEntry(it, "0000000000000000000000000000000000000007", 2, 12);
+ assertNextEntry(it, "0000000000000000000000000000000000000010", 0,
+ 1500);
+ assertNextEntry(it, "0000000000000000000000000000000000000012", 2,
+ 1500);
+ assertFalse(it.hasNext());
+ }
+
+ @Test
+ public void merger_noIndexes() {
+ PackIndexMerger merger = new PackIndexMerger(Map.of());
+ assertEquals(0, merger.getUniqueObjectCount());
+ assertFalse(merger.needsLargeOffsetsChunk());
+ assertTrue(merger.getPackNames().isEmpty());
+ assertEquals(0, merger.getPackCount());
+ assertFalse(merger.bySha1Iterator().hasNext());
+ }
+
+ @Test
+ public void merger_emptyIndexes() {
+ PackIndexMerger merger = new PackIndexMerger(
+ Map.of("p1", indexOf(), "p2", indexOf()));
+ assertEquals(0, merger.getUniqueObjectCount());
+ assertFalse(merger.needsLargeOffsetsChunk());
+ assertEquals(2, merger.getPackNames().size());
+ assertEquals(2, merger.getPackCount());
+ assertFalse(merger.bySha1Iterator().hasNext());
+ }
+
+ @Test
+ public void bySha1Iterator_largeOffsets_needsChunk() {
+ PackIndex idx1 = indexOf(
+ oidOffset("0000000000000000000000000000000000000002", 1L << 32),
+ oidOffset("0000000000000000000000000000000000000004", 12));
+ PackIndex idx2 = indexOf(oidOffset(
+ "0000000000000000000000000000000000000003", (1L << 31) + 10));
+ PackIndexMerger merger = new PackIndexMerger(
+ Map.of("p1", idx1, "p2", idx2));
+ assertTrue(merger.needsLargeOffsetsChunk());
+ assertEquals(2, merger.getOffsetsOver31BitsCount());
+ assertEquals(3, merger.getUniqueObjectCount());
+ }
+
+ @Test
+ public void bySha1Iterator_largeOffsets_noChunk() {
+ // If no value is over 2^32-1, then we don't need large offset
+ PackIndex idx1 = indexOf(
+ oidOffset("0000000000000000000000000000000000000002",
+ (1L << 31) + 15),
+ oidOffset("0000000000000000000000000000000000000004", 12));
+ PackIndex idx2 = indexOf(oidOffset(
+ "0000000000000000000000000000000000000003", (1L << 31) + 10));
+ PackIndexMerger merger = new PackIndexMerger(
+ Map.of("p1", idx1, "p2", idx2));
+ assertFalse(merger.needsLargeOffsetsChunk());
+ assertEquals(2, merger.getOffsetsOver31BitsCount());
+ assertEquals(3, merger.getUniqueObjectCount());
+ }
+
+ private static void assertNextEntry(
+ Iterator<PackIndexMerger.MidxMutableEntry> it, String oid,
+ int packId, long offset) {
+ assertTrue(it.hasNext());
+ PackIndexMerger.MidxMutableEntry e = it.next();
+ assertEquals(oid, e.getObjectId().name());
+ assertEquals(packId, e.getPackId());
+ assertEquals(offset, e.getOffset());
+ }
+
+ private static IndexObject oidOffset(String oid, long offset) {
+ return new IndexObject(oid, offset);
+ }
+
+ private static PackIndex indexOf(IndexObject... objs) {
+ return FakeIndexFactory.indexOf(Arrays.asList(objs));
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/PackIndexPeekIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/PackIndexPeekIteratorTest.java
new file mode 100644
index 0000000..917288a
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/PackIndexPeekIteratorTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2025, Google Inc.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.storage.midx;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import java.util.Arrays;
+
+import org.eclipse.jgit.internal.storage.file.PackIndex;
+import org.eclipse.jgit.junit.FakeIndexFactory;
+import org.junit.Test;
+
+public class PackIndexPeekIteratorTest {
+ @Test
+ public void next() {
+ PackIndex index1 = indexOf(
+ object("0000000000000000000000000000000000000001", 500),
+ object("0000000000000000000000000000000000000003", 1500),
+ object("0000000000000000000000000000000000000005", 3000));
+ PackIndexMerger.PackIndexPeekIterator it = new PackIndexMerger.PackIndexPeekIterator(0, index1);
+ assertEquals("0000000000000000000000000000000000000001", it.next().name());
+ assertEquals("0000000000000000000000000000000000000003", it.next().name());
+ assertEquals("0000000000000000000000000000000000000005", it.next().name());
+ assertNull(it.next());
+ }
+
+ @Test
+ public void peek_doesNotAdvance() {
+ PackIndex index1 = indexOf(
+ object("0000000000000000000000000000000000000001", 500),
+ object("0000000000000000000000000000000000000003", 1500),
+ object("0000000000000000000000000000000000000005", 3000));
+ PackIndexMerger.PackIndexPeekIterator it = new PackIndexMerger.PackIndexPeekIterator(0, index1);
+ it.next();
+ assertEquals("0000000000000000000000000000000000000001", it.peek().name());
+ assertEquals("0000000000000000000000000000000000000001", it.peek().name());
+ it.next();
+ assertEquals("0000000000000000000000000000000000000003", it.peek().name());
+ assertEquals("0000000000000000000000000000000000000003", it.peek().name());
+ it.next();
+ assertEquals("0000000000000000000000000000000000000005", it.peek().name());
+ assertEquals("0000000000000000000000000000000000000005", it.peek().name());
+ it.next();
+ assertNull(it.peek());
+ assertNull(it.peek());
+ }
+
+ @Test
+ public void empty() {
+ PackIndex index1 = indexOf();
+ PackIndexMerger.PackIndexPeekIterator it = new PackIndexMerger.PackIndexPeekIterator(0, index1);
+ assertNull(it.next());
+ assertNull(it.peek());
+ }
+
+ private static PackIndex indexOf(FakeIndexFactory.IndexObject... objs) {
+ return FakeIndexFactory.indexOf(Arrays.asList(objs));
+ }
+
+ private static FakeIndexFactory.IndexObject object(String name, long offset) {
+ return new FakeIndexFactory.IndexObject(name, offset);
+ }
+}
\ No newline at end of file
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/ReftableTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/ReftableTest.java
index ea0d92a..a54002b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/ReftableTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/ReftableTest.java
@@ -29,6 +29,8 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.time.Instant;
+import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -175,7 +177,8 @@ public void hasObjMapRefsSmallTable() throws IOException {
@Test
public void hasObjLogs() throws IOException {
- PersonIdent who = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
+ PersonIdent who = new PersonIdent("Log", "Ger",
+ Instant.ofEpochMilli(1500079709), ZoneOffset.ofHours(-8));
String msg = "test";
ReftableConfig cfg = new ReftableConfig();
cfg.setIndexObjects(false);
@@ -617,7 +620,8 @@ public void invalidReflogWriteOrderUpdateIndex() throws IOException {
.setMinUpdateIndex(1)
.setMaxUpdateIndex(2)
.begin();
- PersonIdent who = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
+ PersonIdent who = new PersonIdent("Log", "Ger",
+ Instant.ofEpochMilli(1500079709), ZoneOffset.ofHours(-8));
String msg = "test";
writer.writeLog(MASTER, 1, who, ObjectId.zeroId(), id(1), msg);
@@ -633,7 +637,8 @@ public void invalidReflogWriteOrderName() throws IOException {
.setMinUpdateIndex(1)
.setMaxUpdateIndex(1)
.begin();
- PersonIdent who = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
+ PersonIdent who = new PersonIdent("Log", "Ger",
+ Instant.ofEpochMilli(1500079709), ZoneOffset.ofHours(-8));
String msg = "test";
writer.writeLog(NEXT, 1, who, ObjectId.zeroId(), id(1), msg);
@@ -647,7 +652,8 @@ public void invalidReflogWriteOrderName() throws IOException {
public void withReflog() throws IOException {
Ref master = ref(MASTER, 1);
Ref next = ref(NEXT, 2);
- PersonIdent who = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
+ PersonIdent who = new PersonIdent("Log", "Ger",
+ Instant.ofEpochMilli(1500079709), ZoneOffset.ofHours(-8));
String msg = "test";
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
@@ -712,11 +718,14 @@ public void reflogReader() throws IOException {
writer.writeRef(master);
writer.writeRef(next);
- PersonIdent who1 = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
+ PersonIdent who1 = new PersonIdent("Log", "Ger",
+ Instant.ofEpochMilli(1500079709), ZoneOffset.ofHours(-8));
writer.writeLog(MASTER, 3, who1, ObjectId.zeroId(), id(1), "1");
- PersonIdent who2 = new PersonIdent("Log", "Ger", 1500079710, -8 * 60);
+ PersonIdent who2 = new PersonIdent("Log", "Ger",
+ Instant.ofEpochMilli(1500079709), ZoneOffset.ofHours(-8));
writer.writeLog(MASTER, 2, who2, id(1), id(2), "2");
- PersonIdent who3 = new PersonIdent("Log", "Ger", 1500079711, -8 * 60);
+ PersonIdent who3 = new PersonIdent("Log", "Ger",
+ Instant.ofEpochMilli(1500079709), ZoneOffset.ofHours(-8));
writer.writeLog(MASTER, 1, who3, id(2), id(3), "3");
writer.finish();
@@ -753,7 +762,8 @@ public void allRefs() throws IOException {
.setMaxUpdateIndex(1)
.setConfig(cfg)
.begin();
- PersonIdent who = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
+ PersonIdent who = new PersonIdent("Log", "Ger",
+ Instant.ofEpochMilli(1500079709), ZoneOffset.ofHours(-8));
// Fill out the 1st ref block.
List<String> names = new ArrayList<>();
@@ -782,7 +792,8 @@ public void allRefs() throws IOException {
@Test
public void reflogSeek() throws IOException {
- PersonIdent who = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
+ PersonIdent who = new PersonIdent("Log", "Ger",
+ Instant.ofEpochSecond(1500079709), ZoneOffset.ofHours(-8));
String msg = "test";
String msgNext = "test next";
@@ -827,7 +838,8 @@ public void reflogSeek() throws IOException {
@Test
public void reflogSeekPrefix() throws IOException {
- PersonIdent who = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
+ PersonIdent who = new PersonIdent("Log", "Ger",
+ Instant.ofEpochMilli(1500079709), ZoneOffset.ofHours(-8));
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
ReftableWriter writer = new ReftableWriter(buffer)
@@ -850,7 +862,8 @@ public void reflogSeekPrefix() throws IOException {
@Test
public void onlyReflog() throws IOException {
- PersonIdent who = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
+ PersonIdent who = new PersonIdent("Log", "Ger",
+ Instant.ofEpochMilli(1500079709), ZoneOffset.ofHours(-8));
String msg = "test";
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
@@ -916,7 +929,8 @@ public void logScan() throws IOException {
writer.writeRef(ref);
}
- PersonIdent who = new PersonIdent("Log", "Ger", 1500079709, -8 * 60);
+ PersonIdent who = new PersonIdent("Log", "Ger",
+ Instant.ofEpochMilli(1500079709), ZoneOffset.ofHours(-8));
for (Ref ref : refs) {
writer.writeLog(ref.getName(), 1, who,
ObjectId.zeroId(), ref.getObjectId(),
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/junit/TestRepositoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/junit/TestRepositoryTest.java
index 450b753..1581d49 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/junit/TestRepositoryTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/junit/TestRepositoryTest.java
@@ -11,6 +11,7 @@
package org.eclipse.jgit.junit;
import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.time.Instant.EPOCH;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
@@ -18,7 +19,6 @@
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
-import java.util.Date;
import java.util.regex.Pattern;
import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
@@ -199,8 +199,8 @@ public void amendRef() throws Exception {
assertEquals(orig.getAuthorIdent(), amended.getAuthorIdent());
// Committer name/email is the same, but time was incremented.
- assertEquals(new PersonIdent(orig.getCommitterIdent(), new Date(0)),
- new PersonIdent(amended.getCommitterIdent(), new Date(0)));
+ assertEquals(new PersonIdent(orig.getCommitterIdent(), EPOCH),
+ new PersonIdent(amended.getCommitterIdent(), EPOCH));
assertTrue(orig.getCommitTime() < amended.getCommitTime());
assertEquals("foo contents", blobAsString(amended, "foo"));
@@ -275,9 +275,9 @@ public void cherryPick() throws Exception {
RevCommit toPick = tr.commit()
.parent(tr.commit().create()) // Can't cherry-pick root.
.author(new PersonIdent("Cherrypick Author", "cpa@example.com",
- tr.getDate(), tr.getTimeZone()))
+ tr.getInstant(), tr.getTimeZoneId()))
.author(new PersonIdent("Cherrypick Committer", "cpc@example.com",
- tr.getDate(), tr.getTimeZone()))
+ tr.getInstant(), tr.getTimeZoneId()))
.message("message to cherry-pick")
.add("bar", "bar contents\n")
.create();
@@ -294,8 +294,8 @@ public void cherryPick() throws Exception {
assertEquals(toPick.getAuthorIdent(), result.getAuthorIdent());
// Committer name/email matches default, and time was incremented.
- assertEquals(new PersonIdent(head.getCommitterIdent(), new Date(0)),
- new PersonIdent(result.getCommitterIdent(), new Date(0)));
+ assertEquals(new PersonIdent(head.getCommitterIdent(), EPOCH),
+ new PersonIdent(result.getCommitterIdent(), EPOCH));
assertTrue(toPick.getCommitTime() < result.getCommitTime());
assertEquals("message to cherry-pick", result.getFullMessage());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java
index 31940a1..06fee8e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java
@@ -1636,6 +1636,47 @@ public void testCoreCommitGraphConfig() {
assertFalse(config.get(CoreConfig.KEY).enableCommitGraph());
}
+ @Test
+ public void testGetNoDefaultBoolean() {
+ Config config = new Config();
+ assertNull(config.getBoolean("foo", "bar"));
+ assertNull(config.getBoolean("foo", "bar", "baz"));
+ }
+
+ @Test
+ public void testGetNoDefaultEnum() {
+ Config config = new Config();
+ assertNull(config.getEnum(new TestEnum[] { TestEnum.ONE_TWO }, "foo",
+ "bar", "baz"));
+ }
+
+ @Test
+ public void testGetNoDefaultInt() {
+ Config config = new Config();
+ assertNull(config.getInt("foo", "bar"));
+ assertNull(config.getInt("foo", "bar", "baz"));
+ }
+ @Test
+ public void testGetNoDefaultIntInRange() {
+ Config config = new Config();
+ assertNull(config.getIntInRange("foo", "bar", 1, 5));
+ assertNull(config.getIntInRange("foo", "bar", "baz", 1, 5));
+ }
+
+ @Test
+ public void testGetNoDefaultLong() {
+ Config config = new Config();
+ assertNull(config.getLong("foo", "bar"));
+ assertNull(config.getLong("foo", "bar", "baz"));
+ }
+
+ @Test
+ public void testGetNoDefaultTimeUnit() {
+ Config config = new Config();
+ assertNull(config.getTimeUnit("foo", "bar", "baz",
+ TimeUnit.SECONDS));
+ }
+
private static void assertValueRoundTrip(String value)
throws ConfigInvalidException {
assertValueRoundTrip(value, value);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/GpgConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/GpgConfigTest.java
index 32f6766..5c2b190 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/GpgConfigTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/GpgConfigTest.java
@@ -96,6 +96,16 @@ public void testGetKeyFormat_x509() throws Exception {
}
@Test
+ public void testGetKeyFormat_ssh() throws Exception {
+ Config c = parse("" //
+ + "[gpg]\n" //
+ + " format = ssh\n" //
+ );
+
+ assertEquals(GpgConfig.GpgFormat.SSH, new GpgConfig(c).getKeyFormat());
+ }
+
+ @Test
public void testGetSigningKey() throws Exception {
Config c = parse("" //
+ "[user]\n" //
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java
index 2b7b6ca..cd98606 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java
@@ -2,7 +2,7 @@
* Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
* Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
* Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
- * Copyright (C) 2013, Robin Stocker <robin@nibor.org> and others
+ * Copyright (C) 2013, 2025 Robin Stocker <robin@nibor.org> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -539,7 +539,7 @@ public void testAssumeUnchanged() throws Exception {
assertTrue(diff.getAssumeUnchanged().contains("file3"));
assertTrue(diff.getModified().contains("file"));
- git.add().addFilepattern(".").call();
+ git.add().addFilepattern(".").setAll(false).call();
iterator = new FileTreeIterator(db);
diff = new IndexDiff(db, Constants.HEAD, iterator);
@@ -551,6 +551,18 @@ public void testAssumeUnchanged() throws Exception {
assertTrue(diff.getAssumeUnchanged().contains("file3"));
assertTrue(diff.getChanged().contains("file"));
assertEquals(Collections.EMPTY_SET, diff.getUntrackedFolders());
+
+ git.add().addFilepattern(".").call();
+
+ iterator = new FileTreeIterator(db);
+ diff = new IndexDiff(db, Constants.HEAD, iterator);
+ diff.diff();
+ assertEquals(1, diff.getAssumeUnchanged().size());
+ assertEquals(0, diff.getModified().size());
+ assertEquals(1, diff.getChanged().size());
+ assertTrue(diff.getAssumeUnchanged().contains("file2"));
+ assertTrue(diff.getChanged().contains("file"));
+ assertEquals(Collections.EMPTY_SET, diff.getUntrackedFolders());
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdTest.java
index 21032c3..d6f0b03 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdTest.java
@@ -16,6 +16,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import java.nio.ByteBuffer;
import java.util.Locale;
import org.eclipse.jgit.errors.InvalidObjectIdException;
@@ -153,4 +154,16 @@ public void testSetByte() {
assertEquals(ObjectId.fromRaw(exp).name(), id.name());
}
}
+
+ @Test
+ public void test_toFromByteBuffer_raw() {
+ ObjectId oid = ObjectId
+ .fromString("ff00eedd003713bb1bb26b808ec9312548e73946");
+ ByteBuffer anObject = ByteBuffer.allocate(Constants.OBJECT_ID_LENGTH);
+ oid.copyRawTo(anObject);
+ anObject.flip();
+
+ ObjectId actual = ObjectId.fromRaw(anObject);
+ assertEquals(oid.name(), actual.name());
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PersonIdentTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PersonIdentTest.java
index 97da175..943a68b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PersonIdentTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/PersonIdentTest.java
@@ -55,7 +55,8 @@ public void testNewIdentInstant() {
p.getWhenAsInstant());
assertEquals("A U Thor <author@example.com> 1142878501 -0500",
p.toExternalString());
- assertEquals(ZoneId.of("GMT-05:00"), p.getZoneId());
+ assertEquals(ZoneId.of("GMT-05:00").getRules().getOffset(
+ Instant.ofEpochMilli(1142878501000L)), p.getZoneOffset());
}
@Test
@@ -69,7 +70,8 @@ public void testNewIdentInstant2() {
p.getWhenAsInstant());
assertEquals("A U Thor <author@example.com> 1142878501 +0530",
p.toExternalString());
- assertEquals(ZoneId.of("GMT+05:30"), p.getZoneId());
+ assertEquals(ZoneId.of("GMT+05:30").getRules().getOffset(
+ Instant.ofEpochMilli(1142878501000L)), p.getZoneOffset());
}
@SuppressWarnings("unused")
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefDatabaseConflictingNamesTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefDatabaseConflictingNamesTest.java
index b02f245..85f9612 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefDatabaseConflictingNamesTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefDatabaseConflictingNamesTest.java
@@ -71,6 +71,11 @@ public List<Ref> getAdditionalRefs() throws IOException {
}
@Override
+ public ReflogReader getReflogReader(Ref ref) throws IOException {
+ return null;
+ }
+
+ @Override
public void create() throws IOException {
// Not needed
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogConfigTest.java
index 854180e..a93937e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogConfigTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogConfigTest.java
@@ -16,6 +16,9 @@
import static org.junit.Assert.assertTrue;
import java.io.IOException;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.ZoneOffset;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.storage.file.FileBasedConfig;
@@ -24,22 +27,23 @@
public class ReflogConfigTest extends RepositoryTestCase {
@Test
public void testlogAllRefUpdates() throws Exception {
- long commitTime = 1154236443000L;
- int tz = -4 * 60;
+ Instant commitTime = Instant.ofEpochSecond(1154236443L);
+ ZoneOffset tz = ZoneOffset.ofHours(-4);
// check that there are no entries in the reflog and turn off writing
// reflogs
- assertTrue(db.getReflogReader(Constants.HEAD).getReverseEntries()
+ RefDatabase refDb = db.getRefDatabase();
+ assertTrue(refDb.getReflogReader(Constants.HEAD).getReverseEntries()
.isEmpty());
- final FileBasedConfig cfg = db.getConfig();
+ FileBasedConfig cfg = db.getConfig();
cfg.setBoolean("core", null, "logallrefupdates", false);
cfg.save();
// do one commit and check that reflog size is 0: no reflogs should be
// written
commit("A Commit\n", commitTime, tz);
- commitTime += 60 * 1000;
- assertTrue("Reflog for HEAD still contain no entry", db
+ commitTime = commitTime.plus(Duration.ofMinutes(1));
+ assertTrue("Reflog for HEAD still contain no entry", refDb
.getReflogReader(Constants.HEAD).getReverseEntries().isEmpty());
// set the logAllRefUpdates parameter to true and check it
@@ -52,10 +56,10 @@ public void testlogAllRefUpdates() throws Exception {
// do one commit and check that reflog size is increased to 1
commit("A Commit\n", commitTime, tz);
- commitTime += 60 * 1000;
- assertTrue(
- "Reflog for HEAD should contain one entry",
- db.getReflogReader(Constants.HEAD).getReverseEntries().size() == 1);
+ commitTime = commitTime.plus(Duration.ofMinutes(1));
+ assertTrue("Reflog for HEAD should contain one entry",
+ refDb.getReflogReader(Constants.HEAD).getReverseEntries()
+ .size() == 1);
// set the logAllRefUpdates parameter to false and check it
cfg.setBoolean("core", null, "logallrefupdates", false);
@@ -67,10 +71,10 @@ public void testlogAllRefUpdates() throws Exception {
// do one commit and check that reflog size is 2
commit("A Commit\n", commitTime, tz);
- commitTime += 60 * 1000;
- assertTrue(
- "Reflog for HEAD should contain two entries",
- db.getReflogReader(Constants.HEAD).getReverseEntries().size() == 2);
+ commitTime = commitTime.plus(Duration.ofMinutes(1));
+ assertTrue("Reflog for HEAD should contain two entries",
+ refDb.getReflogReader(Constants.HEAD).getReverseEntries()
+ .size() == 2);
// set the logAllRefUpdates parameter to false and check it
cfg.setEnum("core", null, "logallrefupdates",
@@ -84,13 +88,13 @@ public void testlogAllRefUpdates() throws Exception {
// do one commit and check that reflog size is 3
commit("A Commit\n", commitTime, tz);
assertTrue("Reflog for HEAD should contain three entries",
- db.getReflogReader(Constants.HEAD).getReverseEntries()
+ refDb.getReflogReader(Constants.HEAD).getReverseEntries()
.size() == 3);
}
- private void commit(String commitMsg, long commitTime, int tz)
+ private void commit(String commitMsg, Instant commitTime, ZoneOffset tz)
throws IOException {
- final CommitBuilder commit = new CommitBuilder();
+ CommitBuilder commit = new CommitBuilder();
commit.setAuthor(new PersonIdent(author, commitTime, tz));
commit.setCommitter(new PersonIdent(committer, commitTime, tz));
commit.setMessage(commitMsg);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/CherryPickTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/CherryPickTest.java
index ae811f8..8865ba9 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/CherryPickTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/CherryPickTest.java
@@ -15,6 +15,9 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import java.time.Instant;
+import java.time.ZoneOffset;
+
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheBuilder;
import org.eclipse.jgit.junit.RepositoryTestCase;
@@ -162,7 +165,8 @@ private static ObjectId commit(final ObjectInserter odi,
final ObjectId[] parentIds) throws Exception {
final CommitBuilder c = new CommitBuilder();
c.setTreeId(treeB.writeTree(odi));
- c.setAuthor(new PersonIdent("A U Thor", "a.u.thor", 1L, 0));
+ c.setAuthor(new PersonIdent("A U Thor", "a.u.thor",
+ Instant.ofEpochSecond(1), ZoneOffset.UTC));
c.setCommitter(c.getAuthor());
c.setParentIds(parentIds);
c.setMessage("Tree " + c.getTreeId().name());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/GitlinkMergeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/GitlinkMergeTest.java
index f410960..b1998f3 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/GitlinkMergeTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/GitlinkMergeTest.java
@@ -15,6 +15,8 @@
import static org.junit.Assert.assertTrue;
import java.io.IOException;
+import java.time.Instant;
+import java.time.ZoneOffset;
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.dircache.DirCache;
@@ -357,7 +359,8 @@ private static ObjectId commit(ObjectInserter odi, DirCache treeB,
ObjectId[] parentIds) throws Exception {
CommitBuilder c = new CommitBuilder();
c.setTreeId(treeB.writeTree(odi));
- c.setAuthor(new PersonIdent("A U Thor", "a.u.thor", 1L, 0));
+ c.setAuthor(new PersonIdent("A U Thor", "a.u.thor",
+ Instant.ofEpochSecond(1), ZoneOffset.UTC));
c.setCommitter(c.getAuthor());
c.setParentIds(parentIds);
c.setMessage("Tree " + c.getTreeId().name());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeAlgorithmUnionTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeAlgorithmUnionTest.java
new file mode 100644
index 0000000..3a8af7a
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeAlgorithmUnionTest.java
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2024 Qualcomm Innovation Center, Inc.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.eclipse.jgit.merge;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.junit.Assert.assertEquals;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.eclipse.jgit.diff.RawText;
+import org.eclipse.jgit.diff.RawTextComparator;
+import org.eclipse.jgit.lib.Constants;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.experimental.theories.DataPoints;
+import org.junit.experimental.theories.Theories;
+import org.junit.runner.RunWith;
+
+@RunWith(Theories.class)
+public class MergeAlgorithmUnionTest {
+ MergeFormatter fmt = new MergeFormatter();
+
+ private final boolean newlineAtEnd;
+
+ @DataPoints
+ public static boolean[] newlineAtEndDataPoints = { false, true };
+
+ public MergeAlgorithmUnionTest(boolean newlineAtEnd) {
+ this.newlineAtEnd = newlineAtEnd;
+ }
+
+ /**
+ * Check for a conflict where the second text was changed similar to the
+ * first one, but the second texts modification covers one more line.
+ *
+ * @throws java.io.IOException
+ */
+ @Test
+ public void testTwoConflictingModifications() throws IOException {
+ assertEquals(t("abZZdefghij"),
+ merge("abcdefghij", "abZdefghij", "aZZdefghij"));
+ }
+
+ /**
+ * Test a case where we have three consecutive chunks. The first text
+ * modifies all three chunks. The second text modifies the first and the
+ * last chunk. This should be reported as one conflicting region.
+ *
+ * @throws java.io.IOException
+ */
+ @Test
+ public void testOneAgainstTwoConflictingModifications() throws IOException {
+ assertEquals(t("aZZcZefghij"),
+ merge("abcdefghij", "aZZZefghij", "aZcZefghij"));
+ }
+
+ /**
+ * Test a merge where only the second text contains modifications. Expect as
+ * merge result the second text.
+ *
+ * @throws java.io.IOException
+ */
+ @Test
+ public void testNoAgainstOneModification() throws IOException {
+ assertEquals(t("aZcZefghij"),
+ merge("abcdefghij", "abcdefghij", "aZcZefghij"));
+ }
+
+ /**
+ * Both texts contain modifications but not on the same chunks. Expect a
+ * non-conflict merge result.
+ *
+ * @throws java.io.IOException
+ */
+ @Test
+ public void testTwoNonConflictingModifications() throws IOException {
+ assertEquals(t("YbZdefghij"),
+ merge("abcdefghij", "abZdefghij", "Ybcdefghij"));
+ }
+
+ /**
+ * Merge two complicated modifications. The merge algorithm has to extend
+ * and combine conflicting regions to get to the expected merge result.
+ *
+ * @throws java.io.IOException
+ */
+ @Test
+ public void testTwoComplicatedModifications() throws IOException {
+ assertEquals(t("aZZZZfZhZjbYdYYYYiY"),
+ merge("abcdefghij", "aZZZZfZhZj", "abYdYYYYiY"));
+ }
+
+ /**
+ * Merge two modifications with a shared delete at the end. The underlying
+ * diff algorithm has to provide consistent edit results to get the expected
+ * merge result.
+ *
+ * @throws java.io.IOException
+ */
+ @Test
+ public void testTwoModificationsWithSharedDelete() throws IOException {
+ assertEquals(t("Cb}n}"), merge("ab}n}n}", "ab}n}", "Cb}n}"));
+ }
+
+ /**
+ * Merge modifications with a shared insert in the middle. The underlying
+ * diff algorithm has to provide consistent edit results to get the expected
+ * merge result.
+ *
+ * @throws java.io.IOException
+ */
+ @Test
+ public void testModificationsWithMiddleInsert() throws IOException {
+ assertEquals(t("aBcd123123uvwxPq"),
+ merge("abcd123uvwxpq", "aBcd123123uvwxPq", "abcd123123uvwxpq"));
+ }
+
+ /**
+ * Merge modifications with a shared delete in the middle. The underlying
+ * diff algorithm has to provide consistent edit results to get the expected
+ * merge result.
+ *
+ * @throws java.io.IOException
+ */
+ @Test
+ public void testModificationsWithMiddleDelete() throws IOException {
+ assertEquals(t("Abz}z123Q"),
+ merge("abz}z}z123q", "Abz}z123Q", "abz}z123q"));
+ }
+
+ @Test
+ public void testInsertionAfterDeletion() throws IOException {
+ assertEquals(t("abcd"), merge("abd", "ad", "abcd"));
+ }
+
+ @Test
+ public void testInsertionBeforeDeletion() throws IOException {
+ assertEquals(t("acbd"), merge("abd", "ad", "acbd"));
+ }
+
+ /**
+ * Test a conflicting region at the very start of the text.
+ *
+ * @throws java.io.IOException
+ */
+ @Test
+ public void testConflictAtStart() throws IOException {
+ assertEquals(t("ZYbcdefghij"),
+ merge("abcdefghij", "Zbcdefghij", "Ybcdefghij"));
+ }
+
+ /**
+ * Test a conflicting region at the very end of the text.
+ *
+ * @throws java.io.IOException
+ */
+ @Test
+ public void testConflictAtEnd() throws IOException {
+ assertEquals(t("abcdefghiZY"),
+ merge("abcdefghij", "abcdefghiZ", "abcdefghiY"));
+ }
+
+ /**
+ * Check for a conflict where the second text was changed similar to the
+ * first one, but the second texts modification covers one more line.
+ *
+ * @throws java.io.IOException
+ */
+ @Test
+ public void testSameModification() throws IOException {
+ assertEquals(t("abZdefghij"),
+ merge("abcdefghij", "abZdefghij", "abZdefghij"));
+ }
+
+ /**
+ * Check that a deleted vs. a modified line shows up as conflict (see Bug
+ * 328551)
+ *
+ * @throws java.io.IOException
+ */
+ @Test
+ public void testDeleteVsModify() throws IOException {
+ assertEquals(t("abZdefghij"),
+ merge("abcdefghij", "abdefghij", "abZdefghij"));
+ }
+
+ @Test
+ public void testInsertVsModify() throws IOException {
+ assertEquals(t("abZXY"), merge("ab", "abZ", "aXY"));
+ }
+
+ @Test
+ public void testAdjacentModifications() throws IOException {
+ assertEquals(t("aZcbYd"), merge("abcd", "aZcd", "abYd"));
+ }
+
+ @Test
+ public void testSeparateModifications() throws IOException {
+ assertEquals(t("aZcYe"), merge("abcde", "aZcde", "abcYe"));
+ }
+
+ @Test
+ public void testBlankLines() throws IOException {
+ assertEquals(t("aZc\nYe"), merge("abc\nde", "aZc\nde", "abc\nYe"));
+ }
+
+ /**
+ * Test merging two contents which do one similar modification and one
+ * insertion is only done by one side, in the middle. Between modification
+ * and insertion is a block which is common between the two contents and the
+ * common base
+ *
+ * @throws java.io.IOException
+ */
+ @Test
+ public void testTwoSimilarModsAndOneInsert() throws IOException {
+ assertEquals(t("aBcDde"), merge("abcde", "aBcde", "aBcDde"));
+
+ assertEquals(t("IAAAJCAB"), merge("iACAB", "IACAB", "IAAAJCAB"));
+
+ assertEquals(t("HIAAAJCAB"), merge("HiACAB", "HIACAB", "HIAAAJCAB"));
+
+ assertEquals(t("AGADEFHIAAAJCAB"),
+ merge("AGADEFHiACAB", "AGADEFHIACAB", "AGADEFHIAAAJCAB"));
+ }
+
+ /**
+ * Test merging two contents which do one similar modification and one
+ * insertion is only done by one side, at the end. Between modification and
+ * insertion is a block which is common between the two contents and the
+ * common base
+ *
+ * @throws java.io.IOException
+ */
+ @Test
+ public void testTwoSimilarModsAndOneInsertAtEnd() throws IOException {
+ Assume.assumeTrue(newlineAtEnd);
+ assertEquals(t("IAAJ"), merge("iA", "IA", "IAAJ"));
+
+ assertEquals(t("IAJ"), merge("iA", "IA", "IAJ"));
+
+ assertEquals(t("IAAAJ"), merge("iA", "IA", "IAAAJ"));
+ }
+
+ @Test
+ public void testTwoSimilarModsAndOneInsertAtEndNoNewlineAtEnd()
+ throws IOException {
+ Assume.assumeFalse(newlineAtEnd);
+ assertEquals(t("IAAAJ"), merge("iA", "IA", "IAAJ"));
+
+ assertEquals(t("IAAJ"), merge("iA", "IA", "IAJ"));
+
+ assertEquals(t("IAAAAJ"), merge("iA", "IA", "IAAAJ"));
+ }
+
+ // Test situations where (at least) one input value is the empty text
+
+ @Test
+ public void testEmptyTextModifiedAgainstDeletion() throws IOException {
+ // NOTE: git.git merge-file appends a '\n' to the end of the file even
+ // when the input files do not have a newline at the end. That appears
+ // to be a bug in git.git.
+ assertEquals(t("AB"), merge("A", "AB", ""));
+ assertEquals(t("AB"), merge("A", "", "AB"));
+ }
+
+ @Test
+ public void testEmptyTextUnmodifiedAgainstDeletion() throws IOException {
+ assertEquals(t(""), merge("AB", "AB", ""));
+
+ assertEquals(t(""), merge("AB", "", "AB"));
+ }
+
+ @Test
+ public void testEmptyTextDeletionAgainstDeletion() throws IOException {
+ assertEquals(t(""), merge("AB", "", ""));
+ }
+
+ private String merge(String commonBase, String ours, String theirs)
+ throws IOException {
+ MergeAlgorithm ma = new MergeAlgorithm();
+ ma.setContentMergeStrategy(ContentMergeStrategy.UNION);
+ MergeResult<RawText> r = ma.merge(RawTextComparator.DEFAULT,
+ T(commonBase), T(ours), T(theirs));
+ ByteArrayOutputStream bo = new ByteArrayOutputStream(50);
+ fmt.formatMerge(bo, r, "B", "O", "T", UTF_8);
+ return bo.toString(UTF_8);
+ }
+
+ public String t(String text) {
+ StringBuilder r = new StringBuilder();
+ for (int i = 0; i < text.length(); i++) {
+ char c = text.charAt(i);
+ switch (c) {
+ case '<':
+ r.append("<<<<<<< O\n");
+ break;
+ case '=':
+ r.append("=======\n");
+ break;
+ case '|':
+ r.append("||||||| B\n");
+ break;
+ case '>':
+ r.append(">>>>>>> T\n");
+ break;
+ default:
+ r.append(c);
+ if (newlineAtEnd || i < text.length() - 1)
+ r.append('\n');
+ }
+ }
+ return r.toString();
+ }
+
+ public RawText T(String text) {
+ return new RawText(Constants.encode(t(text)));
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java
index 3a036ac..c6a6321 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java
@@ -1792,7 +1792,77 @@ public void checkModeMergeConflictInVirtualAncestor(MergeStrategy strategy) thro
// children
mergeResult = git.merge().include(commitC3S).call();
assertEquals(mergeResult.getMergeStatus(), MergeStatus.MERGED);
+ }
+ /**
+ * Merging two commits when binary files have equal content, but conflicting content in the
+ * virtual ancestor.
+ *
+ * <p>
+ * This test has the same set up as
+ * {@code checkFileDirMergeConflictInVirtualAncestor_NoConflictInChildren}, only
+ * with the content conflict in A1 and A2.
+ */
+ @Theory
+ public void checkBinaryMergeConflictInVirtualAncestor(MergeStrategy strategy) throws Exception {
+ if (!strategy.equals(MergeStrategy.RECURSIVE)) {
+ return;
+ }
+
+ Git git = Git.wrap(db);
+
+ // master
+ writeTrashFile("c", "initial file");
+ git.add().addFilepattern("c").call();
+ RevCommit commitI = git.commit().setMessage("Initial commit").call();
+
+ writeTrashFile("a", "\0\1\1\1\1\0"); // content in Ancestor 1
+ git.add().addFilepattern("a").call();
+ RevCommit commitA1 = git.commit().setMessage("Ancestor 1").call();
+
+ writeTrashFile("a", "\0\1\2\3\4\5\0"); // content in Child 1 (commited on master)
+ git.add().addFilepattern("a").call();
+ // commit C1M
+ git.commit().setMessage("Child 1 on master").call();
+
+ git.checkout().setCreateBranch(true).setStartPoint(commitI).setName("branch-to-merge").call();
+ writeTrashFile("a", "\0\2\2\2\2\0"); // content in Ancestor 1
+ git.add().addFilepattern("a").call();
+ RevCommit commitA2 = git.commit().setMessage("Ancestor 2").call();
+
+ // second branch
+ git.checkout().setCreateBranch(true).setStartPoint(commitA1).setName("second-branch").call();
+ writeTrashFile("a", "\0\5\4\3\2\1\0"); // content in Child 2 (commited on second-branch)
+ git.add().addFilepattern("a").call();
+ // commit C2S
+ git.commit().setMessage("Child 2 on second-branch").call();
+
+ // Merge branch-to-merge into second-branch
+ MergeResult mergeResult = git.merge().include(commitA2).setStrategy(strategy).call();
+ assertEquals(mergeResult.getNewHead(), null);
+ assertEquals(mergeResult.getMergeStatus(), MergeStatus.CONFLICTING);
+ // Resolve the conflict manually
+ writeTrashFile("a", "\0\3\3\3\3\0"); // merge conflict resolution
+ git.add().addFilepattern("a").call();
+ RevCommit commitC3S = git.commit().setMessage("Child 3 on second bug - resolve merge conflict").call();
+
+ // Merge branch-to-merge into master
+ git.checkout().setName("master").call();
+ mergeResult = git.merge().include(commitA2).setStrategy(strategy).call();
+ assertEquals(mergeResult.getNewHead(), null);
+ assertEquals(mergeResult.getMergeStatus(), MergeStatus.CONFLICTING);
+
+ // Resolve the conflict manually - set the same value as in resolution above
+ writeTrashFile("a", "\0\3\3\3\3\0"); // merge conflict resolution
+ git.add().addFilepattern("a").call();
+ // commit C4M
+ git.commit().setMessage("Child 4 on master - resolve merge conflict").call();
+
+ // Merge C4M (second-branch) into master (C3S)
+ // Conflict in virtual base should be here, but there are no conflicts in
+ // children
+ mergeResult = git.merge().include(commitC3S).call();
+ assertEquals(mergeResult.getMergeStatus(), MergeStatus.MERGED);
}
/**
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SimpleMergeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SimpleMergeTest.java
index 798aebe..0016adf 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SimpleMergeTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SimpleMergeTest.java
@@ -16,6 +16,8 @@
import static org.junit.Assert.assertTrue;
import java.io.IOException;
+import java.time.Instant;
+import java.time.ZoneOffset;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheBuilder;
@@ -375,7 +377,8 @@ private static ObjectId commit(ObjectInserter odi, DirCache treeB,
ObjectId[] parentIds) throws Exception {
CommitBuilder c = new CommitBuilder();
c.setTreeId(treeB.writeTree(odi));
- c.setAuthor(new PersonIdent("A U Thor", "a.u.thor", 1L, 0));
+ c.setAuthor(new PersonIdent("A U Thor", "a.u.thor",
+ Instant.ofEpochMilli(1L), ZoneOffset.UTC));
c.setCommitter(c.getAuthor());
c.setParentIds(parentIds);
c.setMessage("Tree " + c.getTreeId().name());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchApplierTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchApplierTest.java
index 2aac15b..5507f85 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchApplierTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/patch/PatchApplierTest.java
@@ -48,8 +48,7 @@
import org.junit.runners.Suite;
@RunWith(Suite.class)
-@Suite.SuiteClasses({
- PatchApplierTest.WithWorktree. class, //
+@Suite.SuiteClasses({ PatchApplierTest.WithWorktree.class, //
PatchApplierTest.InCore.class, //
})
public class PatchApplierTest {
@@ -128,6 +127,20 @@ protected Result applyPatch() throws IOException {
}
}
+ protected Result applyPatchAllowConflicts() throws IOException {
+ InputStream patchStream = getTestResource(name + ".patch");
+ Patch patch = new Patch();
+ patch.parse(patchStream);
+ if (inCore) {
+ try (ObjectInserter oi = db.newObjectInserter()) {
+ return new PatchApplier(db, baseTip, oi).allowConflicts()
+ .applyPatch(patch);
+ }
+ }
+ return new PatchApplier(db).allowConflicts()
+ .applyPatch(patch);
+ }
+
protected static InputStream getTestResource(String patchFile) {
return PatchApplierTest.class.getClassLoader()
.getResourceAsStream("org/eclipse/jgit/diff/" + patchFile);
@@ -169,6 +182,13 @@ void verifyChange(Result result, String aName, boolean exists)
verifyContent(result, aName, exists);
}
+ void verifyChange(Result result, String aName, boolean exists,
+ int numConflicts) throws Exception {
+ assertEquals(numConflicts, result.getErrors().size());
+ assertEquals(1, result.getPaths().size());
+ verifyContent(result, aName, exists);
+ }
+
protected byte[] readBlob(ObjectId treeish, String path)
throws Exception {
try (TestRepository<?> tr = new TestRepository<>(db);
@@ -346,6 +366,44 @@ public void testCopyWithHunks() throws Exception {
}
@Test
+ public void testConflictMarkers() throws Exception {
+ init("allowconflict", true, true);
+
+ Result result = applyPatchAllowConflicts();
+
+ assertEquals(result.getErrors().size(), 1);
+ PatchApplier.Result.Error error = result.getErrors().get(0);
+ assertEquals("cannot apply hunk", error.msg);
+ assertEquals("allowconflict", error.oldFileName);
+ assertTrue(error.isGitConflict());
+ verifyChange(result, "allowconflict", true, 1);
+ }
+
+ @Test
+ public void testConflictMarkersOutOfBounds() throws Exception {
+ init("ConflictOutOfBounds", true, true);
+
+ Result result = applyPatchAllowConflicts();
+
+ assertEquals(result.getErrors().size(), 1);
+ PatchApplier.Result.Error error = result.getErrors().get(0);
+ assertEquals("cannot apply hunk", error.msg);
+ assertEquals("ConflictOutOfBounds", error.oldFileName);
+ assertTrue(error.isGitConflict());
+ verifyChange(result, "ConflictOutOfBounds", true, 1);
+ }
+
+ @Test
+ public void testConflictMarkersFileDeleted() throws Exception {
+ init("allowconflict_file_deleted", false, false);
+
+ Result result = applyPatchAllowConflicts();
+
+ assertEquals(1, result.getErrors().size());
+ assertEquals(0, result.getPaths().size());
+ }
+
+ @Test
public void testShiftUp() throws Exception {
init("ShiftUp");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevCommitParseTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevCommitParseTest.java
index 6872289..014ff92 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevCommitParseTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevCommitParseTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008-2009, Google Inc. and others
+ * Copyright (C) 2008, 2024 Google Inc. and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -23,7 +23,9 @@
import java.io.UnsupportedEncodingException;
import java.nio.charset.IllegalCharsetNameException;
import java.nio.charset.UnsupportedCharsetException;
-import java.util.TimeZone;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.CommitBuilder;
@@ -94,18 +96,17 @@ public void testParse_NoParents() throws Exception {
assertNotNull(cAuthor);
assertEquals(authorName, cAuthor.getName());
assertEquals(authorEmail, cAuthor.getEmailAddress());
- assertEquals((long) authorTime * 1000, cAuthor.getWhen().getTime());
- assertEquals(TimeZone.getTimeZone("GMT" + authorTimeZone),
- cAuthor.getTimeZone());
+ assertEquals(Instant.ofEpochSecond(authorTime),
+ cAuthor.getWhenAsInstant());
+ assertEquals(ZoneId.of(authorTimeZone), cAuthor.getZoneId());
final PersonIdent cCommitter = c.getCommitterIdent();
assertNotNull(cCommitter);
assertEquals(committerName, cCommitter.getName());
assertEquals(committerEmail, cCommitter.getEmailAddress());
- assertEquals((long) committerTime * 1000,
- cCommitter.getWhen().getTime());
- assertEquals(TimeZone.getTimeZone("GMT" + committerTimeZone),
- cCommitter.getTimeZone());
+ assertEquals(Instant.ofEpochSecond(committerTime),
+ cCommitter.getWhenAsInstant());
+ assertEquals(ZoneId.of(committerTimeZone), cCommitter.getZoneId());
}
private RevCommit create(String msg) throws Exception {
@@ -153,9 +154,13 @@ public void testParse_incompleteAuthorAndCommitter() throws Exception {
c.parseCanonical(rw, b.toString().getBytes(UTF_8));
}
assertEquals(
- new PersonIdent("", "a_u_thor@example.com", 1218123387000L, 7),
+ new PersonIdent("", "a_u_thor@example.com",
+ Instant.ofEpochMilli(1218123387000L),
+ ZoneOffset.ofHoursMinutes(0, 7)),
c.getAuthorIdent());
- assertEquals(new PersonIdent("", "", 1218123390000L, -5),
+ assertEquals(
+ new PersonIdent("", "", Instant.ofEpochMilli(1218123390000L),
+ ZoneOffset.ofHoursMinutes(0, -5)),
c.getCommitterIdent());
}
@@ -408,6 +413,7 @@ public void testParse_NoMessage() throws Exception {
final RevCommit c = create(msg);
assertEquals(msg, c.getFullMessage());
assertEquals(msg, c.getShortMessage());
+ assertEquals(msg, c.getFirstMessageLine());
}
@Test
@@ -415,6 +421,7 @@ public void testParse_OnlyLFMessage() throws Exception {
final RevCommit c = create("\n");
assertEquals("\n", c.getFullMessage());
assertEquals("", c.getShortMessage());
+ assertEquals("", c.getFirstMessageLine());
}
@Test
@@ -423,6 +430,7 @@ public void testParse_ShortLineOnlyNoLF() throws Exception {
final RevCommit c = create(shortMsg);
assertEquals(shortMsg, c.getFullMessage());
assertEquals(shortMsg, c.getShortMessage());
+ assertEquals(shortMsg, c.getFirstMessageLine());
}
@Test
@@ -432,6 +440,7 @@ public void testParse_ShortLineOnlyEndLF() throws Exception {
final RevCommit c = create(fullMsg);
assertEquals(fullMsg, c.getFullMessage());
assertEquals(shortMsg, c.getShortMessage());
+ assertEquals(shortMsg, c.getFirstMessageLine());
}
@Test
@@ -441,6 +450,7 @@ public void testParse_ShortLineOnlyEmbeddedLF() throws Exception {
final RevCommit c = create(fullMsg);
assertEquals(fullMsg, c.getFullMessage());
assertEquals(shortMsg, c.getShortMessage());
+ assertEquals("This is a", c.getFirstMessageLine());
}
@Test
@@ -450,6 +460,7 @@ public void testParse_ShortLineOnlyEmbeddedAndEndingLF() throws Exception {
final RevCommit c = create(fullMsg);
assertEquals(fullMsg, c.getFullMessage());
assertEquals(shortMsg, c.getShortMessage());
+ assertEquals("This is a", c.getFirstMessageLine());
}
@Test
@@ -461,6 +472,7 @@ public void testParse_GitStyleMessage() throws Exception {
final RevCommit c = create(fullMsg);
assertEquals(fullMsg, c.getFullMessage());
assertEquals(shortMsg, c.getShortMessage());
+ assertEquals(shortMsg, c.getFirstMessageLine());
}
@Test
@@ -480,6 +492,7 @@ public void testParse_PublicParseMethod()
assertEquals(author, p.getAuthorIdent());
assertEquals(committer, p.getCommitterIdent());
assertEquals("Test commit", p.getShortMessage());
+ assertEquals("Test commit", p.getFirstMessageLine());
assertEquals(src.getMessage(), p.getFullMessage());
}
@@ -494,6 +507,7 @@ public void testParse_GitStyleMessageWithCRLF() throws Exception {
final RevCommit c = create(fullMsg);
assertEquals(fullMsg, c.getFullMessage());
assertEquals(shortMsg, c.getShortMessage());
+ assertEquals("This fixes a", c.getFirstMessageLine());
}
private static ObjectId id(String str) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkFilterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkFilterTest.java
index 81ff4a2..7fece66 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkFilterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkFilterTest.java
@@ -14,6 +14,7 @@
import static org.junit.Assert.assertNull;
import java.io.IOException;
+import java.time.Instant;
import java.util.Date;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
@@ -217,14 +218,132 @@ public void testCommitTimeRevFilter() throws Exception {
final RevCommit b = commit(a);
tick(100);
- Date since = getDate();
+ Instant since = getInstant();
final RevCommit c1 = commit(b);
tick(100);
final RevCommit c2 = commit(b);
tick(100);
- Date until = getDate();
+ Instant until = getInstant();
+ final RevCommit d = commit(c1, c2);
+ tick(100);
+
+ final RevCommit e = commit(d);
+
+ {
+ RevFilter after = CommitTimeRevFilter.after(since);
+ assertNotNull(after);
+ rw.setRevFilter(after);
+ markStart(e);
+ assertCommit(e, rw.next());
+ assertCommit(d, rw.next());
+ assertCommit(c2, rw.next());
+ assertCommit(c1, rw.next());
+ assertNull(rw.next());
+ }
+
+ {
+ RevFilter before = CommitTimeRevFilter.before(until);
+ assertNotNull(before);
+ rw.reset();
+ rw.setRevFilter(before);
+ markStart(e);
+ assertCommit(c2, rw.next());
+ assertCommit(c1, rw.next());
+ assertCommit(b, rw.next());
+ assertCommit(a, rw.next());
+ assertNull(rw.next());
+ }
+
+ {
+ RevFilter between = CommitTimeRevFilter.between(since, until);
+ assertNotNull(between);
+ rw.reset();
+ rw.setRevFilter(between);
+ markStart(e);
+ assertCommit(c2, rw.next());
+ assertCommit(c1, rw.next());
+ assertNull(rw.next());
+ }
+ }
+
+ @Test
+ public void testCommitTimeRevFilter_date() throws Exception {
+ // Using deprecated Date api for the commit time rev filter.
+ // Delete this tests when method is removed.
+ final RevCommit a = commit();
+ tick(100);
+
+ final RevCommit b = commit(a);
+ tick(100);
+
+ Date since = Date.from(getInstant());
+ final RevCommit c1 = commit(b);
+ tick(100);
+
+ final RevCommit c2 = commit(b);
+ tick(100);
+
+ Date until = Date.from(getInstant());
+ final RevCommit d = commit(c1, c2);
+ tick(100);
+
+ final RevCommit e = commit(d);
+
+ {
+ RevFilter after = CommitTimeRevFilter.after(since);
+ assertNotNull(after);
+ rw.setRevFilter(after);
+ markStart(e);
+ assertCommit(e, rw.next());
+ assertCommit(d, rw.next());
+ assertCommit(c2, rw.next());
+ assertCommit(c1, rw.next());
+ assertNull(rw.next());
+ }
+
+ {
+ RevFilter before = CommitTimeRevFilter.before(until);
+ assertNotNull(before);
+ rw.reset();
+ rw.setRevFilter(before);
+ markStart(e);
+ assertCommit(c2, rw.next());
+ assertCommit(c1, rw.next());
+ assertCommit(b, rw.next());
+ assertCommit(a, rw.next());
+ assertNull(rw.next());
+ }
+
+ {
+ RevFilter between = CommitTimeRevFilter.between(since, until);
+ assertNotNull(between);
+ rw.reset();
+ rw.setRevFilter(between);
+ markStart(e);
+ assertCommit(c2, rw.next());
+ assertCommit(c1, rw.next());
+ assertNull(rw.next());
+ }
+ }
+
+ @Test
+ public void testCommitTimeRevFilter_long() throws Exception {
+ final RevCommit a = commit();
+ tick(100);
+
+ final RevCommit b = commit(a);
+ tick(100);
+
+ long since = getInstant().toEpochMilli();
+ final RevCommit c1 = commit(b);
+ tick(100);
+
+ final RevCommit c2 = commit(b);
+ tick(100);
+
+ long until = getInstant().toEpochMilli();
final RevCommit d = commit(c1, c2);
tick(100);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkTestCase.java
index ec0c0e7..8fa6a83 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkTestCase.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkTestCase.java
@@ -12,6 +12,7 @@
import static org.junit.Assert.assertSame;
+import java.time.Instant;
import java.util.Date;
import org.eclipse.jgit.dircache.DirCacheEntry;
@@ -38,8 +39,14 @@ protected RevWalk createRevWalk() {
return new RevWalk(db);
}
+ // Use getInstant() instead
+ @Deprecated
protected Date getDate() {
- return util.getDate();
+ return Date.from(util.getInstant());
+ }
+
+ protected Instant getInstant() {
+ return util.getInstant();
}
protected void tick(int secDelta) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkUtilsReachableTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkUtilsReachableTest.java
index 0a045c9..ffc7c96 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkUtilsReachableTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkUtilsReachableTest.java
@@ -14,6 +14,7 @@
import static org.junit.Assert.assertEquals;
import java.util.Collection;
+import java.util.HashSet;
import java.util.List;
import org.eclipse.jgit.api.Git;
@@ -121,7 +122,7 @@ private void assertContains(RevCommit commit, Collection<Ref> refsThatShouldCont
Collection<Ref> sortedRefs = RefComparator.sort(allRefs);
List<Ref> actual = RevWalkUtils.findBranchesReachableFrom(commit,
rw, sortedRefs);
- assertEquals(refsThatShouldContainCommit, actual);
+ assertEquals(new HashSet<>(refsThatShouldContainCommit), new HashSet<>(actual));
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleAddTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleAddTest.java
index 300c869..4306975 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleAddTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleAddTest.java
@@ -114,6 +114,13 @@ public void addSubmodule() throws Exception {
try (Repository subModRepo = generator.getRepository()) {
assertNotNull(subModRepo);
assertEquals(subCommit, commit);
+ String worktreeDir = subModRepo.getConfig().getString(
+ ConfigConstants.CONFIG_CORE_SECTION, null,
+ ConfigConstants.CONFIG_KEY_WORKTREE);
+ assertEquals("../../../sub", worktreeDir);
+ String gitdir = read(new File(subModRepo.getWorkTree(),
+ Constants.DOT_GIT));
+ assertEquals("gitdir: ../.git/modules/sub", gitdir);
}
}
Status status = Git.wrap(db).status().call();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleUpdateTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleUpdateTest.java
index b10bd73..d541170 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleUpdateTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleUpdateTest.java
@@ -17,21 +17,25 @@
import java.io.IOException;
import java.util.Collection;
+import org.eclipse.jgit.api.CheckoutCommand;
import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.InitCommand;
+import org.eclipse.jgit.api.SubmoduleAddCommand;
import org.eclipse.jgit.api.SubmoduleUpdateCommand;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheEditor;
import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
import org.eclipse.jgit.dircache.DirCacheEntry;
+import org.eclipse.jgit.junit.JGitTestUtil;
import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.StoredConfig;
-import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.junit.Test;
@@ -40,6 +44,91 @@
*/
public class SubmoduleUpdateTest extends RepositoryTestCase {
+ private Repository submoduleRepo;
+
+ private Git git;
+
+ private AnyObjectId subRepoCommit2;
+
+ private void createSubmoduleRepo() throws IOException, GitAPIException {
+ File directory = createTempDirectory("submodule_repo");
+ InitCommand init = Git.init();
+ init.setDirectory(directory);
+ init.call();
+ submoduleRepo = Git.open(directory).getRepository();
+ try (Git sub = Git.wrap(submoduleRepo)) {
+ // commit something
+ JGitTestUtil.writeTrashFile(submoduleRepo, "commit1.txt",
+ "commit 1");
+ sub.add().addFilepattern("commit1.txt").call();
+ sub.commit().setMessage("commit 1").call().getId();
+
+ JGitTestUtil.writeTrashFile(submoduleRepo, "commit2.txt",
+ "commit 2");
+ sub.add().addFilepattern("commit2.txt").call();
+ subRepoCommit2 = sub.commit().setMessage("commit 2").call().getId();
+ }
+ }
+
+ private void addSubmodule(String path) throws GitAPIException {
+ SubmoduleAddCommand command = new SubmoduleAddCommand(db);
+ command.setPath(path);
+ String uri = submoduleRepo.getDirectory().toURI().toString();
+ command.setURI(uri);
+ try (Repository repo = command.call()) {
+ assertNotNull(repo);
+ }
+ git.add().addFilepattern(path).addFilepattern(Constants.DOT_GIT_MODULES)
+ .call();
+ git.commit().setMessage("adding submodule").call();
+ recursiveDelete(new File(git.getRepository().getWorkTree(), path));
+ recursiveDelete(
+ new File(new File(git.getRepository().getCommonDirectory(),
+ Constants.MODULES), path));
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ createSubmoduleRepo();
+
+ git = Git.wrap(db);
+ // commit something
+ writeTrashFile("initial.txt", "initial");
+ git.add().addFilepattern("initial.txt").call();
+ git.commit().setMessage("initial commit").call();
+ }
+
+ public void updateModeClonedRestoredSubmoduleTemplate(String mode)
+ throws Exception {
+ String path = "sub";
+ addSubmodule(path);
+
+ StoredConfig cfg = git.getRepository().getConfig();
+ if (mode != null) {
+ cfg.load();
+ cfg.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
+ ConfigConstants.CONFIG_KEY_UPDATE, mode);
+ cfg.save();
+ }
+ SubmoduleUpdateCommand update = new SubmoduleUpdateCommand(db);
+ update.call();
+ try (Git subGit = Git.open(new File(db.getWorkTree(), path))) {
+ update.call();
+ assertEquals(subRepoCommit2.getName(),
+ subGit.getRepository().getBranch());
+ }
+
+ recursiveDelete(new File(db.getWorkTree(), path));
+
+ update.call();
+ try (Git subGit = Git.open(new File(db.getWorkTree(), path))) {
+ update.call();
+ assertEquals(subRepoCommit2.getName(),
+ subGit.getRepository().getBranch());
+ }
+ }
+
@Test
public void repositoryWithNoSubmodules() throws GitAPIException {
SubmoduleUpdateCommand command = new SubmoduleUpdateCommand(db);
@@ -50,35 +139,9 @@ public void repositoryWithNoSubmodules() throws GitAPIException {
@Test
public void repositoryWithSubmodule() throws Exception {
- writeTrashFile("file.txt", "content");
- Git git = Git.wrap(db);
- git.add().addFilepattern("file.txt").call();
- final RevCommit commit = git.commit().setMessage("create file").call();
final String path = "sub";
- DirCache cache = db.lockDirCache();
- DirCacheEditor editor = cache.editor();
- editor.add(new PathEdit(path) {
-
- @Override
- public void apply(DirCacheEntry ent) {
- ent.setFileMode(FileMode.GITLINK);
- ent.setObjectId(commit);
- }
- });
- editor.commit();
-
- StoredConfig config = db.getConfig();
- config.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
- ConfigConstants.CONFIG_KEY_URL, db.getDirectory().toURI()
- .toString());
- config.save();
-
- FileBasedConfig modulesConfig = new FileBasedConfig(new File(
- db.getWorkTree(), Constants.DOT_GIT_MODULES), db.getFS());
- modulesConfig.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
- ConfigConstants.CONFIG_KEY_PATH, path);
- modulesConfig.save();
+ addSubmodule(path);
SubmoduleUpdateCommand command = new SubmoduleUpdateCommand(db);
Collection<String> updated = command.call();
@@ -90,14 +153,22 @@ public void apply(DirCacheEntry ent) {
assertTrue(generator.next());
try (Repository subRepo = generator.getRepository()) {
assertNotNull(subRepo);
- assertEquals(commit, subRepo.resolve(Constants.HEAD));
+ assertEquals(subRepoCommit2, subRepo.resolve(Constants.HEAD));
+ String worktreeDir = subRepo.getConfig().getString(
+ ConfigConstants.CONFIG_CORE_SECTION, null,
+ ConfigConstants.CONFIG_KEY_WORKTREE);
+ assertEquals("../../../sub", worktreeDir);
+ String gitdir = read(
+ new File(subRepo.getWorkTree(), Constants.DOT_GIT));
+ assertEquals("gitdir: ../.git/modules/sub", gitdir);
+
}
}
}
@Test
- public void repositoryWithUnconfiguredSubmodule() throws IOException,
- GitAPIException {
+ public void repositoryWithUnconfiguredSubmodule()
+ throws IOException, GitAPIException {
final ObjectId id = ObjectId
.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234");
final String path = "sub";
@@ -113,16 +184,14 @@ public void apply(DirCacheEntry ent) {
});
editor.commit();
- FileBasedConfig modulesConfig = new FileBasedConfig(new File(
- db.getWorkTree(), Constants.DOT_GIT_MODULES), db.getFS());
+ FileBasedConfig modulesConfig = new FileBasedConfig(
+ new File(db.getWorkTree(), Constants.DOT_GIT_MODULES),
+ db.getFS());
modulesConfig.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
ConfigConstants.CONFIG_KEY_PATH, path);
String url = "git://server/repo.git";
modulesConfig.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
ConfigConstants.CONFIG_KEY_URL, url);
- String update = "rebase";
- modulesConfig.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
- ConfigConstants.CONFIG_KEY_UPDATE, update);
modulesConfig.save();
SubmoduleUpdateCommand command = new SubmoduleUpdateCommand(db);
@@ -132,8 +201,8 @@ public void apply(DirCacheEntry ent) {
}
@Test
- public void repositoryWithInitializedSubmodule() throws IOException,
- GitAPIException {
+ public void repositoryWithInitializedSubmodule()
+ throws IOException, GitAPIException {
final ObjectId id = ObjectId
.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234");
final String path = "sub";
@@ -160,4 +229,77 @@ public void apply(DirCacheEntry ent) {
assertNotNull(updated);
assertTrue(updated.isEmpty());
}
+
+ @Test
+ public void updateModeMergeClonedRestoredSubmodule() throws Exception {
+ updateModeClonedRestoredSubmoduleTemplate(
+ ConfigConstants.CONFIG_KEY_MERGE);
+ }
+
+ @Test
+ public void updateModeRebaseClonedRestoredSubmodule() throws Exception {
+ updateModeClonedRestoredSubmoduleTemplate(
+ ConfigConstants.CONFIG_KEY_REBASE);
+ }
+
+ @Test
+ public void updateModeCheckoutClonedRestoredSubmodule() throws Exception {
+ updateModeClonedRestoredSubmoduleTemplate(
+ ConfigConstants.CONFIG_KEY_CHECKOUT);
+ }
+
+ @Test
+ public void updateModeMissingClonedRestoredSubmodule() throws Exception {
+ updateModeClonedRestoredSubmoduleTemplate(null);
+ }
+
+ @Test
+ public void updateMode() throws Exception {
+ String path = "sub";
+ addSubmodule(path);
+
+ StoredConfig cfg = git.getRepository().getConfig();
+ cfg.load();
+ cfg.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
+ ConfigConstants.CONFIG_KEY_UPDATE,
+ ConfigConstants.CONFIG_KEY_REBASE);
+ cfg.save();
+
+ SubmoduleUpdateCommand update = new SubmoduleUpdateCommand(db);
+ update.call();
+ try (Git subGit = Git.open(new File(db.getWorkTree(), path))) {
+ CheckoutCommand checkout = subGit.checkout();
+ checkout.setName("master");
+ checkout.call();
+ update.call();
+ assertEquals("master", subGit.getRepository().getBranch());
+ }
+
+ cfg.load();
+ cfg.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
+ ConfigConstants.CONFIG_KEY_UPDATE,
+ ConfigConstants.CONFIG_KEY_CHECKOUT);
+ cfg.save();
+
+ update.call();
+ try (Git subGit = Git.open(new File(db.getWorkTree(), path))) {
+ assertEquals(subRepoCommit2.getName(),
+ subGit.getRepository().getBranch());
+ }
+
+ cfg.load();
+ cfg.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
+ ConfigConstants.CONFIG_KEY_UPDATE,
+ ConfigConstants.CONFIG_KEY_MERGE);
+ cfg.save();
+
+ update.call();
+ try (Git subGit = Git.open(new File(db.getWorkTree(), path))) {
+ CheckoutCommand checkout = subGit.checkout();
+ checkout.setName("master");
+ checkout.call();
+ update.call();
+ assertEquals("master", subGit.getRepository().getBranch());
+ }
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/AtomicPushTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/AtomicPushTest.java
index c47e591..0ba8926 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/AtomicPushTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/AtomicPushTest.java
@@ -25,10 +25,6 @@
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.transport.resolver.ReceivePackFactory;
-import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
-import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -46,16 +42,8 @@ public class AtomicPushTest {
public void setUp() throws Exception {
server = newRepo("server");
client = newRepo("client");
- testProtocol = new TestProtocol<>(
- null,
- new ReceivePackFactory<Object>() {
- @Override
- public ReceivePack create(Object req, Repository db)
- throws ServiceNotEnabledException,
- ServiceNotAuthorizedException {
- return new ReceivePack(db);
- }
- });
+ testProtocol = new TestProtocol<>(null,
+ (req, db) -> new ReceivePack(db));
uri = testProtocol.register(ctx, server);
try (TestRepository<?> clientRepo = new TestRepository<>(client)) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateIdentTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateIdentTest.java
index cee023d..6290b79 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateIdentTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateIdentTest.java
@@ -138,6 +138,51 @@ public void incompleteCasesMatchPersonIdent() throws Exception {
"Me <me@example.com>");
}
+ @Test
+ public void timezoneRange_hours() {
+ int HOUR_TO_MS = 60 * 60 * 1000;
+
+ // java.util.TimeZone: Hours must be between 0 to 23
+ PushCertificateIdent hourLimit = PushCertificateIdent
+ .parse("A U. Thor <a_u_thor@example.com> 1218123387 +2300");
+ assertEquals(1380, hourLimit.getTimeZoneOffset());
+ assertEquals(23 * HOUR_TO_MS,
+ hourLimit.getTimeZone().getOffset(1218123387));
+
+ PushCertificateIdent hourDubious = PushCertificateIdent
+ .parse("A U. Thor <a_u_thor@example.com> 1218123387 +2400");
+ assertEquals(1440, hourDubious.getTimeZoneOffset());
+ assertEquals(0, hourDubious.getTimeZone().getOffset(1218123387));
+ }
+
+ @Test
+ public void timezoneRange_minutes() {
+ PushCertificateIdent hourLimit = PushCertificateIdent
+ .parse("A U. Thor <a_u_thor@example.com> 1218123387 +0059");
+ assertEquals(59, hourLimit.getTimeZoneOffset());
+ assertEquals(59 * 60 * 1000,
+ hourLimit.getTimeZone().getOffset(1218123387));
+
+ // This becomes one hour and one minute (!)
+ PushCertificateIdent hourDubious = PushCertificateIdent
+ .parse("A U. Thor <a_u_thor@example.com> 1218123387 +0061");
+ assertEquals(61, hourDubious.getTimeZoneOffset());
+ assertEquals(61 * 60 * 1000,
+ hourDubious.getTimeZone().getOffset(1218123387));
+
+ PushCertificateIdent weirdCase = PushCertificateIdent
+ .parse("A U. Thor <a_u_thor@example.com> 1218123387 +0099");
+ assertEquals(99, weirdCase.getTimeZoneOffset());
+ assertEquals(99 * 60 * 1000,
+ weirdCase.getTimeZone().getOffset(1218123387));
+
+ PushCertificateIdent weirdCase2 = PushCertificateIdent
+ .parse("A U. Thor <a_u_thor@example.com> 1218123387 +0199");
+ assertEquals(60 + 99, weirdCase2.getTimeZoneOffset());
+ assertEquals((60 + 99) * 60 * 1000,
+ weirdCase2.getTimeZone().getOffset(1218123387));
+ }
+
private static void assertMatchesPersonIdent(String raw,
PersonIdent expectedPersonIdent, String expectedUserId) {
PushCertificateIdent certIdent = PushCertificateIdent.parse(raw);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateStoreTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateStoreTest.java
index 4f01e4d..a03222b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateStoreTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateStoreTest.java
@@ -23,6 +23,8 @@
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
+import java.time.Instant;
+import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -318,8 +320,8 @@ public void putMatchingWithSomeMatchingRefs() throws Exception {
}
private PersonIdent newIdent() {
- return new PersonIdent(
- "A U. Thor", "author@example.com", ts.getAndIncrement(), 0);
+ return new PersonIdent("A U. Thor", "author@example.com",
+ Instant.ofEpochMilli(ts.getAndIncrement()), ZoneOffset.UTC);
}
private PushCertificateStore newStore() {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportHttpTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportHttpTest.java
index 029b45e..96d3a58 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportHttpTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportHttpTest.java
@@ -14,10 +14,10 @@
import java.io.File;
import java.io.IOException;
import java.net.HttpCookie;
+import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
import java.util.Collections;
-import java.util.Date;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
@@ -101,7 +101,7 @@ public void testProcessResponseCookies() throws IOException {
.singletonList("cookie2=some value; Max-Age=1234; Path=/"));
try (TransportHttp transportHttp = new TransportHttp(db, uri)) {
- Date creationDate = new Date();
+ Instant creationDate = Instant.now();
transportHttp.processResponseCookies(connection);
// evaluate written cookie file
@@ -112,8 +112,9 @@ public void testProcessResponseCookies() throws IOException {
cookie.setPath("/u/2/");
cookie.setMaxAge(
- (Instant.parse("2100-01-01T11:00:00.000Z").toEpochMilli()
- - creationDate.getTime()) / 1000);
+ Duration.between(creationDate,
+ Instant.parse("2100-01-01T11:00:00.000Z"))
+ .getSeconds());
cookie.setSecure(true);
cookie.setHttpOnly(true);
expectedCookies.add(cookie);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java
index d403624..6792002 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java
@@ -82,6 +82,43 @@ public void testWindowsFile2() throws Exception {
}
@Test
+ public void testBrokenFilePath() throws Exception {
+ String str = "D:\\\\my\\\\x";
+ URIish u = new URIish(str);
+ assertNull(u.getScheme());
+ assertFalse(u.isRemote());
+ assertEquals(str, u.getPath());
+ assertEquals(u, new URIish(str));
+ }
+
+ @Test
+ public void testStackOverflow() throws Exception {
+ StringBuilder b = new StringBuilder("D:\\");
+ for (int i = 0; i < 4000; i++) {
+ b.append("x\\");
+ }
+ String str = b.toString();
+ URIish u = new URIish(str);
+ assertNull(u.getScheme());
+ assertFalse(u.isRemote());
+ assertEquals(str, u.getPath());
+ }
+
+ @Test
+ public void testStackOverflow2() throws Exception {
+ StringBuilder b = new StringBuilder("D:\\");
+ for (int i = 0; i < 4000; i++) {
+ b.append("x\\");
+ }
+ b.append('y');
+ String str = b.toString();
+ URIish u = new URIish(str);
+ assertNull(u.getScheme());
+ assertFalse(u.isRemote());
+ assertEquals(str, u.getPath());
+ }
+
+ @Test
public void testRelativePath() throws Exception {
final String str = "../../foo/bar";
URIish u = new URIish(str);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackReachabilityTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackReachabilityTest.java
index 2711762..a5507c8 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackReachabilityTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackReachabilityTest.java
@@ -27,9 +27,6 @@
import org.eclipse.jgit.revwalk.RevBlob;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.transport.UploadPack.RequestPolicy;
-import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
-import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
-import org.eclipse.jgit.transport.resolver.UploadPackFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -264,15 +261,10 @@ private void generateBitmaps(InMemoryRepository repo) throws Exception {
}
private static TestProtocol<Object> generateReachableCommitUploadPackProtocol() {
- return new TestProtocol<>(new UploadPackFactory<Object>() {
- @Override
- public UploadPack create(Object req, Repository db)
- throws ServiceNotEnabledException,
- ServiceNotAuthorizedException {
- UploadPack up = new UploadPack(db);
- up.setRequestPolicy(RequestPolicy.REACHABLE_COMMIT);
- return up;
- }
+ return new TestProtocol<>((req, db) -> {
+ UploadPack up = new UploadPack(db);
+ up.setRequestPolicy(RequestPolicy.REACHABLE_COMMIT);
+ return up;
}, null);
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java
index def73ac..5c2f0e5 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java
@@ -1,5 +1,6 @@
package org.eclipse.jgit.transport;
+import static java.time.ZoneOffset.UTC;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsString;
@@ -11,12 +12,14 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
+import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -501,15 +504,15 @@ public void testV2Capabilities() throws Exception {
assertThat(hook.capabilitiesRequest, notNullValue());
assertThat(pckIn.readString(), is("version 2"));
assertThat(
- Arrays.asList(pckIn.readString(), pckIn.readString(),
- pckIn.readString()),
+ Arrays.asList(pckIn.readString(),pckIn.readString(),
+ pckIn.readString(), pckIn.readString()),
// TODO(jonathantanmy) This check is written this way
// to make it simple to see that we expect this list of
// capabilities, but probably should be loosened to
// allow additional commands to be added to the list,
// and additional capabilities to be added to existing
// commands without requiring test changes.
- hasItems("ls-refs", "fetch=shallow", "server-option"));
+ hasItems("agent=" + UserAgent.get() ,"ls-refs", "fetch=shallow", "server-option"));
assertTrue(PacketLineIn.isEnd(pckIn.readString()));
}
@@ -535,7 +538,7 @@ private void checkAdvertisedIfAllowed(String configSection, String configName,
lines.add(line);
}
}
- assertThat(lines, containsInAnyOrder("ls-refs", "fetch", "server-option"));
+ assertThat(lines, containsInAnyOrder("ls-refs", "fetch", "server-option", "agent=" + UserAgent.get()));
}
private void checkUnadvertisedIfUnallowed(String configSection,
@@ -564,6 +567,47 @@ private void checkUnadvertisedIfUnallowed(String configSection,
}
@Test
+ public void testV0CapabilitiesAllowAnySha1InWant() throws Exception {
+ checkAvertisedCapabilityProtocolV0IfAllowed("uploadpack",
+ "allowanysha1inwant", "allow-reachable-sha1-in-want",
+ "allow-tip-sha1-in-want");
+ }
+
+ @Test
+ public void testV0CapabilitiesAllowReachableSha1InWant() throws Exception {
+ checkAvertisedCapabilityProtocolV0IfAllowed("uploadpack",
+ "allowreachablesha1inwant", "allow-reachable-sha1-in-want");
+ }
+
+ @Test
+ public void testV0CapabilitiesAllowTipSha1InWant() throws Exception {
+ checkAvertisedCapabilityProtocolV0IfAllowed("uploadpack",
+ "allowtipsha1inwant", "allow-tip-sha1-in-want");
+ }
+
+ private void checkAvertisedCapabilityProtocolV0IfAllowed(
+ String configSection, String configName, String... capabilities)
+ throws Exception {
+ server.getConfig().setBoolean(configSection, null, configName, true);
+ ByteArrayInputStream recvStream = uploadPackSetup(
+ TransferConfig.ProtocolVersion.V0.version(), null,
+ PacketLineIn.end());
+ PacketLineIn pckIn = new PacketLineIn(recvStream);
+
+ String line;
+ while (!PacketLineIn.isEnd((line = pckIn.readString()))) {
+ if (line.contains("capabilities")) {
+ List<String> linesCapabilities = Arrays.asList(line.substring(
+ line.indexOf(" ", line.indexOf("capabilities")) + 1)
+ .split(" "));
+ assertThat(linesCapabilities, hasItems(capabilities));
+ return;
+ }
+ }
+ fail("Server side protocol did not contain any capabilities'");
+ }
+
+ @Test
public void testV2CapabilitiesAllowFilter() throws Exception {
checkAdvertisedIfAllowed("uploadpack", "allowfilter", "filter");
checkUnadvertisedIfUnallowed("uploadpack", "allowfilter", "filter");
@@ -601,9 +645,9 @@ public void testV2CapabilitiesRefInWantNotAdvertisedIfAdvertisingForbidden() thr
assertThat(pckIn.readString(), is("version 2"));
assertThat(
- Arrays.asList(pckIn.readString(), pckIn.readString(),
+ Arrays.asList(pckIn.readString(),pckIn.readString(), pckIn.readString(),
pckIn.readString()),
- hasItems("ls-refs", "fetch=shallow", "server-option"));
+ hasItems("agent="+ UserAgent.get(),"ls-refs", "fetch=shallow", "server-option"));
assertTrue(PacketLineIn.isEnd(pckIn.readString()));
}
@@ -1464,14 +1508,19 @@ public void testV2FetchDeepenWithoutDone() throws Exception {
public void testV2FetchShallowSince() throws Exception {
PersonIdent person = new PersonIdent(remote.getRepository());
- RevCommit beyondBoundary = remote.commit()
- .committer(new PersonIdent(person, 1510000000, 0)).create();
- RevCommit boundary = remote.commit().parent(beyondBoundary)
- .committer(new PersonIdent(person, 1520000000, 0)).create();
- RevCommit tooOld = remote.commit()
- .committer(new PersonIdent(person, 1500000000, 0)).create();
+ RevCommit beyondBoundary = remote.commit().committer(
+ new PersonIdent(person, Instant.ofEpochSecond(1510000), UTC))
+ .create();
+ RevCommit boundary = remote.commit().parent(beyondBoundary).committer(
+ new PersonIdent(person, Instant.ofEpochSecond(1520000), UTC))
+ .create();
+ RevCommit tooOld = remote.commit().committer(
+ new PersonIdent(person, Instant.ofEpochSecond(1500000), UTC))
+ .create();
RevCommit merge = remote.commit().parent(boundary).parent(tooOld)
- .committer(new PersonIdent(person, 1530000000, 0)).create();
+ .committer(new PersonIdent(person,
+ Instant.ofEpochSecond(1530000), UTC))
+ .create();
remote.update("branch1", merge);
@@ -1517,12 +1566,15 @@ public void testV2FetchShallowSince() throws Exception {
public void testV2FetchShallowSince_excludedParentWithMultipleChildren() throws Exception {
PersonIdent person = new PersonIdent(remote.getRepository());
- RevCommit base = remote.commit()
- .committer(new PersonIdent(person, 1500000000, 0)).create();
- RevCommit child1 = remote.commit().parent(base)
- .committer(new PersonIdent(person, 1510000000, 0)).create();
- RevCommit child2 = remote.commit().parent(base)
- .committer(new PersonIdent(person, 1520000000, 0)).create();
+ RevCommit base = remote.commit().committer(
+ new PersonIdent(person, Instant.ofEpochSecond(1500000), UTC))
+ .create();
+ RevCommit child1 = remote.commit().parent(base).committer(
+ new PersonIdent(person, Instant.ofEpochSecond(1510000), UTC))
+ .create();
+ RevCommit child2 = remote.commit().parent(base).committer(
+ new PersonIdent(person, Instant.ofEpochSecond(1520000), UTC))
+ .create();
remote.update("branch1", child1);
remote.update("branch2", child2);
@@ -1559,8 +1611,9 @@ public void testV2FetchShallowSince_excludedParentWithMultipleChildren() throws
public void testV2FetchShallowSince_noCommitsSelected() throws Exception {
PersonIdent person = new PersonIdent(remote.getRepository());
- RevCommit tooOld = remote.commit()
- .committer(new PersonIdent(person, 1500000000, 0)).create();
+ RevCommit tooOld = remote.commit().committer(
+ new PersonIdent(person, Instant.ofEpochSecond(1500000), UTC))
+ .create();
remote.update("branch1", tooOld);
@@ -1684,12 +1737,15 @@ public void testV2FetchDeepenNot_supportAnnotatedTags() throws Exception {
public void testV2FetchDeepenNot_excludedParentWithMultipleChildren() throws Exception {
PersonIdent person = new PersonIdent(remote.getRepository());
- RevCommit base = remote.commit()
- .committer(new PersonIdent(person, 1500000000, 0)).create();
- RevCommit child1 = remote.commit().parent(base)
- .committer(new PersonIdent(person, 1510000000, 0)).create();
- RevCommit child2 = remote.commit().parent(base)
- .committer(new PersonIdent(person, 1520000000, 0)).create();
+ RevCommit base = remote.commit().committer(
+ new PersonIdent(person, Instant.ofEpochSecond(1500000), UTC))
+ .create();
+ RevCommit child1 = remote.commit().parent(base).committer(
+ new PersonIdent(person, Instant.ofEpochSecond(1510000), UTC))
+ .create();
+ RevCommit child2 = remote.commit().parent(base).committer(
+ new PersonIdent(person, Instant.ofEpochSecond(1520000), UTC))
+ .create();
remote.update("base", base);
remote.update("branch1", child1);
@@ -2820,7 +2876,7 @@ public void testSingleBranchCloneTagChain() throws Exception {
RevTag heavyTag2 = remote.tag("middleTagRing", heavyTag1);
remote.lightweightTag("refTagRing", heavyTag2);
- UploadPack uploadPack = new UploadPack(remote.getRepository());
+ try (UploadPack uploadPack = new UploadPack(remote.getRepository())) {
ByteArrayOutputStream cli = new ByteArrayOutputStream();
PacketLineOut clientWant = new PacketLineOut(cli);
@@ -2830,7 +2886,6 @@ public void testSingleBranchCloneTagChain() throws Exception {
clientWant.writeString("done\n");
try (ByteArrayOutputStream serverResponse = new ByteArrayOutputStream()) {
-
uploadPack.setPreUploadHook(new PreUploadHook() {
@Override
public void onBeginNegotiateRound(UploadPack up,
@@ -2883,6 +2938,7 @@ public void onSendPack(UploadPack up,
assertTrue(objDb.has(heavyTag2.toObjectId()));
}
}
+}
@Test
public void testSingleBranchShallowCloneTagChainWithReflessTag() throws Exception {
@@ -2894,7 +2950,7 @@ public void testSingleBranchShallowCloneTagChainWithReflessTag() throws Exceptio
RevTag tag3 = remote.tag("t3", tag2);
remote.lightweightTag("t3", tag3);
- UploadPack uploadPack = new UploadPack(remote.getRepository());
+ try (UploadPack uploadPack = new UploadPack(remote.getRepository())) {
ByteArrayOutputStream cli = new ByteArrayOutputStream();
PacketLineOut clientWant = new PacketLineOut(cli);
@@ -2904,7 +2960,6 @@ public void testSingleBranchShallowCloneTagChainWithReflessTag() throws Exceptio
clientWant.writeString("done\n");
try (ByteArrayOutputStream serverResponse = new ByteArrayOutputStream()) {
-
uploadPack.setPreUploadHook(new PreUploadHook() {
@Override
public void onBeginNegotiateRound(UploadPack up,
@@ -2952,6 +3007,7 @@ public void onSendPack(UploadPack up,
assertTrue(objDb.has(one.toObjectId()));
}
}
+}
@Test
public void testSafeToClearRefsInFetchV0() throws Exception {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java
index e463e90..7b9e70d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java
@@ -25,8 +25,9 @@
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.ResetCommand.ResetType;
+import org.eclipse.jgit.dircache.Checkout;
import org.eclipse.jgit.dircache.DirCache;
-import org.eclipse.jgit.dircache.DirCacheCheckout;
+import org.eclipse.jgit.dircache.DirCacheCheckout.CheckoutMetadata;
import org.eclipse.jgit.dircache.DirCacheEditor;
import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
import org.eclipse.jgit.dircache.DirCacheEntry;
@@ -38,6 +39,7 @@
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
@@ -303,11 +305,12 @@ public void testIsModifiedSymlinkAsFile() throws Exception {
DirCacheEntry dce = db.readDirCache().getEntry("symlink");
dce.setFileMode(FileMode.SYMLINK);
try (ObjectReader objectReader = db.newObjectReader()) {
+ Checkout checkout = new Checkout(db).setRecursiveDeletion(false);
+ checkout.checkout(dce,
+ new CheckoutMetadata(EolStreamType.DIRECT, null),
+ objectReader, null);
WorkingTreeOptions options = db.getConfig()
.get(WorkingTreeOptions.KEY);
- DirCacheCheckout.checkoutEntry(db, dce, objectReader, false, null,
- options);
-
FileTreeIterator fti = new FileTreeIterator(trash, db.getFS(),
options);
while (!fti.getEntryPathString().equals("symlink")) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/ChangeIdUtilTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/ChangeIdUtilTest.java
index 3265249..44e8632 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/ChangeIdUtilTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/ChangeIdUtilTest.java
@@ -12,7 +12,9 @@
import static org.junit.Assert.assertEquals;
-import java.util.concurrent.TimeUnit;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.ZoneId;
import org.eclipse.jgit.junit.MockSystemReader;
import org.eclipse.jgit.lib.ObjectId;
@@ -53,9 +55,9 @@ public class ChangeIdUtilTest {
MockSystemReader mockSystemReader = new MockSystemReader();
- final long when = mockSystemReader.getCurrentTime();
+ Instant when = mockSystemReader.now();
- final int tz = new MockSystemReader().getTimezone(when);
+ ZoneId tz = new MockSystemReader().getTimeZoneAt(when);
PersonIdent author = new PersonIdent("J. Author", "ja@example.com");
{
@@ -218,23 +220,23 @@ public void testACommitWithSubject_NonFooterAndBugAndSob() throws Exception {
@Test
public void testACommitWithSubjectBodyBugBrackersAndSob() throws Exception {
assertEquals(
- "a commit with subject body, bug. brackers and sob\n\nText\n\nBug: 33\nChange-Id: I90ecb589bef766302532c3e00915e10114b00f62\n[bracket]\nSigned-off-by: me@you.too\n",
- call("a commit with subject body, bug. brackers and sob\n\nText\n\nBug: 33\n[bracket]\nSigned-off-by: me@you.too\n\n"));
+ "a commit with subject body, bug, brackers and sob\n\nText\n\nBug: 33\n[bracket]\nChange-Id: I94dc6ed919a4baaa7c1bf8712717b888c6b90363\nSigned-off-by: me@you.too\n",
+ call("a commit with subject body, bug, brackers and sob\n\nText\n\nBug: 33\n[bracket]\nSigned-off-by: me@you.too\n\n"));
}
@Test
public void testACommitWithSubjectBodyBugLineWithASpaceAndSob()
throws Exception {
assertEquals(
- "a commit with subject body, bug. line with a space and sob\n\nText\n\nBug: 33\nChange-Id: I864e2218bdee033c8ce9a7f923af9e0d5dc16863\n \nSigned-off-by: me@you.too\n",
- call("a commit with subject body, bug. line with a space and sob\n\nText\n\nBug: 33\n \nSigned-off-by: me@you.too\n\n"));
+ "a commit with subject body, bug, line with a space and sob\n\nText\n\nBug: 33\n \nChange-Id: I126b472d2e0e64ad8187d61857f0169f9ccdae86\nSigned-off-by: me@you.too\n",
+ call("a commit with subject body, bug, line with a space and sob\n\nText\n\nBug: 33\n \nSigned-off-by: me@you.too\n\n"));
}
@Test
public void testACommitWithSubjectBodyBugEmptyLineAndSob() throws Exception {
assertEquals(
- "a commit with subject body, bug. empty line and sob\n\nText\n\nBug: 33\nChange-Id: I33f119f533313883e6ada3df600c4f0d4db23a76\n \nSigned-off-by: me@you.too\n",
- call("a commit with subject body, bug. empty line and sob\n\nText\n\nBug: 33\n \nSigned-off-by: me@you.too\n\n"));
+ "a commit with subject body, bug, empty line and sob\n\nText\n\nBug: 33\n\nChange-Id: Ic3b61b6e39a0815669b65302e9e75e6a5a019a26\nSigned-off-by: me@you.too\n",
+ call("a commit with subject body, bug, empty line and sob\n\nText\n\nBug: 33\n\nSigned-off-by: me@you.too\n\n"));
}
@Test
@@ -342,9 +344,7 @@ public void testTimeAltersId() throws Exception {
/** Increment the {@link #author} and {@link #committer} times. */
protected void tick() {
- final long delta = TimeUnit.MILLISECONDS.convert(5 * 60,
- TimeUnit.SECONDS);
- final long now = author.getWhen().getTime() + delta;
+ Instant now = author.getWhenAsInstant().plus(Duration.ofMinutes(5));
author = new PersonIdent(author, now, tz);
committer = new PersonIdent(committer, now, tz);
@@ -528,7 +528,7 @@ public void testKernelStyleFooter() throws Exception {
}
@Test
- public void testChangeIdAfterBugOrIssue() throws Exception {
+ public void testChangeIdAfterOtherFooters() throws Exception {
assertEquals("a\n" + //
"\n" + //
"Bug: 42\n" + //
@@ -541,6 +541,18 @@ public void testChangeIdAfterBugOrIssue() throws Exception {
assertEquals("a\n" + //
"\n" + //
+ "Bug: 42\n" + //
+ " multi-line Bug footer\n" + //
+ "Change-Id: Icc953ef35f1a4ee5eb945132aefd603ae3d9dd9f\n" + //
+ SOB1,//
+ call("a\n" + //
+ "\n" + //
+ "Bug: 42\n" + //
+ " multi-line Bug footer\n" + //
+ SOB1));
+
+ assertEquals("a\n" + //
+ "\n" + //
"Issue: 42\n" + //
"Change-Id: Ie66e07d89ae5b114c0975b49cf326e90331dd822\n" + //
SOB1,//
@@ -548,6 +560,14 @@ public void testChangeIdAfterBugOrIssue() throws Exception {
"\n" + //
"Issue: 42\n" + //
SOB1));
+
+ assertEquals("a\n" + //
+ "\n" + //
+ "Other: none\n" + //
+ "Change-Id: Ide70e625dea61854206378a377dd12e462ae720f\n",//
+ call("a\n" + //
+ "\n" + //
+ "Other: none\n"));
}
@Test
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/GitDateFormatterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/GitDateFormatterTest.java
index 6a531fe..7ef386f 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/GitDateFormatterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/GitDateFormatterTest.java
@@ -89,7 +89,6 @@ public void RAW() {
@Test
public void LOCALE() {
String date = new GitDateFormatter(Format.LOCALE).formatDate(ident);
- System.out.println(date);
assertTrue("Sep 20, 2011 7:09:25 PM -0400".equals(date)
|| "Sep 20, 2011, 7:09:25 PM -0400".equals(date) // JDK-8206961
|| "Sep 20, 2011, 7:09:25\u202FPM -0400".equals(date)); // JDK-8304925
@@ -99,7 +98,6 @@ public void LOCALE() {
public void LOCALELOCAL() {
String date = new GitDateFormatter(Format.LOCALELOCAL)
.formatDate(ident);
- System.out.println(date);
assertTrue("Sep 20, 2011 7:39:25 PM".equals(date)
|| "Sep 20, 2011, 7:39:25 PM".equals(date) // JDK-8206961
|| "Sep 20, 2011, 7:39:25\u202FPM".equals(date)); // JDK-8304925
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/GitTimeParserBadlyFormattedTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/GitTimeParserBadlyFormattedTest.java
new file mode 100644
index 0000000..a59d7bc
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/GitTimeParserBadlyFormattedTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2012, Christian Halstrick and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.util;
+
+import static org.junit.Assert.assertThrows;
+
+import java.text.ParseException;
+
+import org.eclipse.jgit.junit.MockSystemReader;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.experimental.theories.DataPoints;
+import org.junit.experimental.theories.Theories;
+import org.junit.experimental.theories.Theory;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests which assert that unparseable Strings lead to ParseExceptions
+ */
+@RunWith(Theories.class)
+public class GitTimeParserBadlyFormattedTest {
+ private String dateStr;
+
+ @Before
+ public void setUp() {
+ MockSystemReader mockSystemReader = new MockSystemReader();
+ SystemReader.setInstance(mockSystemReader);
+ }
+
+ @After
+ public void tearDown() {
+ SystemReader.setInstance(null);
+ }
+
+ public GitTimeParserBadlyFormattedTest(String dateStr) {
+ this.dateStr = dateStr;
+ }
+
+ @DataPoints
+ public static String[] getDataPoints() {
+ return new String[] { "", ".", "...", "1970", "3000.3000.3000", "3 yesterday ago",
+ "now yesterday ago", "yesterdays", "3.day. 2.week.ago",
+ "day ago", "Gra Feb 21 15:35:00 2007 +0100",
+ "Sun Feb 21 15:35:00 2007 +0100",
+ "Wed Feb 21 15:35:00 Grand +0100" };
+ }
+
+ @Theory
+ public void badlyFormattedWithoutRef() {
+ assertThrows(
+ "The expected ParseException while parsing '" + dateStr
+ + "' did not occur.",
+ ParseException.class, () -> GitTimeParser.parse(dateStr));
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/GitTimeParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/GitTimeParserTest.java
new file mode 100644
index 0000000..0e5eb28
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/GitTimeParserTest.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2024, Christian Halstrick and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.eclipse.jgit.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.text.ParseException;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.Period;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoField;
+import java.time.temporal.TemporalAccessor;
+
+import org.eclipse.jgit.junit.MockSystemReader;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class GitTimeParserTest {
+ MockSystemReader mockSystemReader;
+
+ @Before
+ public void setUp() {
+ mockSystemReader = new MockSystemReader();
+ SystemReader.setInstance(mockSystemReader);
+ }
+
+ @After
+ public void tearDown() {
+ SystemReader.setInstance(null);
+ }
+
+ @Test
+ public void yesterday() throws ParseException {
+ LocalDateTime parse = GitTimeParser.parse("yesterday");
+
+ LocalDateTime now = SystemReader.getInstance().civilNow();
+ assertEquals(Period.between(parse.toLocalDate(), now.toLocalDate()),
+ Period.ofDays(1));
+ }
+
+ @Test
+ public void never() throws ParseException {
+ LocalDateTime parse = GitTimeParser.parse("never");
+ assertEquals(LocalDateTime.MAX, parse);
+ }
+
+ @Test
+ public void now_pointInTime() throws ParseException {
+ LocalDateTime aTime = asLocalDateTime("2007-02-21 15:35:00 +0100");
+
+ LocalDateTime parsedNow = GitTimeParser.parse("now", aTime);
+
+ assertEquals(aTime, parsedNow);
+ }
+
+ @Test
+ public void now_systemTime() throws ParseException {
+ LocalDateTime firstNow = GitTimeParser.parse("now");
+ assertEquals(SystemReader.getInstance().civilNow(), firstNow);
+ mockSystemReader.tick(10);
+ LocalDateTime secondNow = GitTimeParser.parse("now");
+ assertTrue(secondNow.isAfter(firstNow));
+ }
+
+ @Test
+ public void weeksAgo() throws ParseException {
+ LocalDateTime aTime = asLocalDateTime("2007-02-21 15:35:00 +0100");
+
+ LocalDateTime parse = GitTimeParser.parse("2 weeks ago", aTime);
+ assertEquals(asLocalDateTime("2007-02-07 15:35:00 +0100"), parse);
+ }
+
+ @Test
+ public void daysAndWeeksAgo() throws ParseException {
+ LocalDateTime aTime = asLocalDateTime("2007-02-21 15:35:00 +0100");
+
+ LocalDateTime twoWeeksAgoActual = GitTimeParser.parse("2 weeks ago",
+ aTime);
+
+ LocalDateTime twoWeeksAgoExpected = asLocalDateTime(
+ "2007-02-07 15:35:00 +0100");
+ assertEquals(twoWeeksAgoExpected, twoWeeksAgoActual);
+
+ LocalDateTime combinedWhitespace = GitTimeParser
+ .parse("3 days 2 weeks ago", aTime);
+ LocalDateTime combinedWhitespaceExpected = asLocalDateTime(
+ "2007-02-04 15:35:00 +0100");
+ assertEquals(combinedWhitespaceExpected, combinedWhitespace);
+
+ LocalDateTime combinedDots = GitTimeParser.parse("3.day.2.week.ago",
+ aTime);
+ LocalDateTime combinedDotsExpected = asLocalDateTime(
+ "2007-02-04 15:35:00 +0100");
+ assertEquals(combinedDotsExpected, combinedDots);
+ }
+
+ @Test
+ public void hoursAgo() throws ParseException {
+ LocalDateTime aTime = asLocalDateTime("2007-02-21 17:35:00 +0100");
+
+ LocalDateTime twoHoursAgoActual = GitTimeParser.parse("2 hours ago",
+ aTime);
+
+ LocalDateTime twoHoursAgoExpected = asLocalDateTime(
+ "2007-02-21 15:35:00 +0100");
+ assertEquals(twoHoursAgoExpected, twoHoursAgoActual);
+ }
+
+ @Test
+ public void hoursAgo_acrossDay() throws ParseException {
+ LocalDateTime aTime = asLocalDateTime("2007-02-21 00:35:00 +0100");
+
+ LocalDateTime twoHoursAgoActual = GitTimeParser.parse("2 hours ago",
+ aTime);
+
+ LocalDateTime twoHoursAgoExpected = asLocalDateTime(
+ "2007-02-20 22:35:00 +0100");
+ assertEquals(twoHoursAgoExpected, twoHoursAgoActual);
+ }
+
+ @Test
+ public void minutesHoursAgoCombined() throws ParseException {
+ LocalDateTime aTime = asLocalDateTime("2007-02-04 15:35:00 +0100");
+
+ LocalDateTime combinedWhitespace = GitTimeParser
+ .parse("3 hours 2 minutes ago", aTime);
+ LocalDateTime combinedWhitespaceExpected = asLocalDateTime(
+ "2007-02-04 12:33:00 +0100");
+ assertEquals(combinedWhitespaceExpected, combinedWhitespace);
+
+ LocalDateTime combinedDots = GitTimeParser
+ .parse("3.hours.2.minutes.ago", aTime);
+ LocalDateTime combinedDotsExpected = asLocalDateTime(
+ "2007-02-04 12:33:00 +0100");
+ assertEquals(combinedDotsExpected, combinedDots);
+ }
+
+ @Test
+ public void minutesAgo() throws ParseException {
+ LocalDateTime aTime = asLocalDateTime("2007-02-21 17:35:10 +0100");
+
+ LocalDateTime twoMinutesAgo = GitTimeParser.parse("2 minutes ago",
+ aTime);
+
+ LocalDateTime twoMinutesAgoExpected = asLocalDateTime(
+ "2007-02-21 17:33:10 +0100");
+ assertEquals(twoMinutesAgoExpected, twoMinutesAgo);
+ }
+
+ @Test
+ public void minutesAgo_acrossDay() throws ParseException {
+ LocalDateTime aTime = asLocalDateTime("2007-02-21 00:35:10 +0100");
+
+ LocalDateTime minutesAgoActual = GitTimeParser.parse("40 minutes ago",
+ aTime);
+
+ LocalDateTime minutesAgoExpected = asLocalDateTime(
+ "2007-02-20 23:55:10 +0100");
+ assertEquals(minutesAgoExpected, minutesAgoActual);
+ }
+
+ @Test
+ public void iso() throws ParseException {
+ String dateStr = "2007-02-21 15:35:00 +0100";
+
+ LocalDateTime actual = GitTimeParser.parse(dateStr);
+
+ LocalDateTime expected = asLocalDateTime(dateStr);
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ public void rfc() throws ParseException {
+ String dateStr = "Wed, 21 Feb 2007 15:35:00 +0100";
+
+ LocalDateTime actual = GitTimeParser.parse(dateStr);
+
+ LocalDateTime expected = asLocalDateTime(dateStr,
+ "EEE, dd MMM yyyy HH:mm:ss Z");
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ public void shortFmt() throws ParseException {
+ assertParsing("2007-02-21", "yyyy-MM-dd");
+ }
+
+ @Test
+ public void shortWithDots() throws ParseException {
+ assertParsing("2007.02.21", "yyyy.MM.dd");
+ }
+
+ @Test
+ public void shortWithSlash() throws ParseException {
+ assertParsing("02/21/2007", "MM/dd/yyyy");
+ }
+
+ @Test
+ public void shortWithDotsReverse() throws ParseException {
+ assertParsing("21.02.2007", "dd.MM.yyyy");
+ }
+
+ @Test
+ public void defaultFmt() throws ParseException {
+ assertParsing("Wed Feb 21 15:35:00 2007 +0100",
+ "EEE MMM dd HH:mm:ss yyyy Z");
+ }
+
+ @Test
+ public void local() throws ParseException {
+ assertParsing("Wed Feb 21 15:35:00 2007", "EEE MMM dd HH:mm:ss yyyy");
+ }
+
+ private static void assertParsing(String dateStr, String format)
+ throws ParseException {
+ LocalDateTime actual = GitTimeParser.parse(dateStr);
+
+ LocalDateTime expected = asLocalDateTime(dateStr, format);
+ assertEquals(expected, actual);
+ }
+
+ private static LocalDateTime asLocalDateTime(String dateStr) {
+ return asLocalDateTime(dateStr, "yyyy-MM-dd HH:mm:ss Z");
+ }
+
+ private static LocalDateTime asLocalDateTime(String dateStr,
+ String pattern) {
+ DateTimeFormatter fmt = DateTimeFormatter.ofPattern(pattern);
+ TemporalAccessor ta = fmt
+ .withZone(SystemReader.getInstance().getTimeZoneId())
+ .withLocale(SystemReader.getInstance().getLocale())
+ .parse(dateStr);
+ return ta.isSupported(ChronoField.HOUR_OF_DAY) ? LocalDateTime.from(ta)
+ : LocalDate.from(ta).atStartOfDay();
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RawParseUtils_ParsePersonIdentTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RawParseUtils_ParsePersonIdentTest.java
index 355bbba..6d23db8 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RawParseUtils_ParsePersonIdentTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RawParseUtils_ParsePersonIdentTest.java
@@ -10,10 +10,13 @@
package org.eclipse.jgit.util;
+import static java.time.Instant.EPOCH;
+import static java.time.ZoneOffset.UTC;
import static org.junit.Assert.assertEquals;
-import java.util.Date;
-import java.util.TimeZone;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
import org.eclipse.jgit.lib.PersonIdent;
import org.junit.Test;
@@ -22,8 +25,8 @@ public class RawParseUtils_ParsePersonIdentTest {
@Test
public void testParsePersonIdent_legalCases() {
- final Date when = new Date(1234567890000L);
- final TimeZone tz = TimeZone.getTimeZone("GMT-7");
+ Instant when = Instant.ofEpochMilli(1234567890000L);
+ ZoneId tz = ZoneOffset.ofHours(-7);
assertPersonIdent("Me <me@example.com> 1234567890 -0700",
new PersonIdent("Me", "me@example.com", when, tz));
@@ -50,8 +53,8 @@ public void testParsePersonIdent_legalCases() {
@Test
public void testParsePersonIdent_fuzzyCases() {
- final Date when = new Date(1234567890000L);
- final TimeZone tz = TimeZone.getTimeZone("GMT-7");
+ Instant when = Instant.ofEpochMilli(1234567890000L);
+ ZoneId tz = ZoneOffset.ofHours(-7);
assertPersonIdent(
"A U Thor <author@example.com>, C O. Miter <comiter@example.com> 1234567890 -0700",
@@ -64,8 +67,8 @@ public void testParsePersonIdent_fuzzyCases() {
@Test
public void testParsePersonIdent_incompleteCases() {
- final Date when = new Date(1234567890000L);
- final TimeZone tz = TimeZone.getTimeZone("GMT-7");
+ Instant when = Instant.ofEpochMilli(1234567890000L);
+ ZoneId tz = ZoneOffset.ofHours(-7);
assertPersonIdent("Me <> 1234567890 -0700", new PersonIdent("Me", "",
when, tz));
@@ -76,26 +79,26 @@ public void testParsePersonIdent_incompleteCases() {
assertPersonIdent(" <> 1234567890 -0700", new PersonIdent("", "", when,
tz));
- assertPersonIdent("<>", new PersonIdent("", "", 0, 0));
+ assertPersonIdent("<>", new PersonIdent("", "", EPOCH, UTC));
- assertPersonIdent(" <>", new PersonIdent("", "", 0, 0));
+ assertPersonIdent(" <>", new PersonIdent("", "", EPOCH, UTC));
assertPersonIdent("<me@example.com>", new PersonIdent("",
- "me@example.com", 0, 0));
+ "me@example.com", EPOCH, UTC));
assertPersonIdent(" <me@example.com>", new PersonIdent("",
- "me@example.com", 0, 0));
+ "me@example.com", EPOCH, UTC));
- assertPersonIdent("Me <>", new PersonIdent("Me", "", 0, 0));
+ assertPersonIdent("Me <>", new PersonIdent("Me", "", EPOCH, UTC));
assertPersonIdent("Me <me@example.com>", new PersonIdent("Me",
- "me@example.com", 0, 0));
+ "me@example.com", EPOCH, UTC));
assertPersonIdent("Me <me@example.com> 1234567890", new PersonIdent(
- "Me", "me@example.com", 0, 0));
+ "Me", "me@example.com", EPOCH, UTC));
assertPersonIdent("Me <me@example.com> 1234567890 ", new PersonIdent(
- "Me", "me@example.com", 0, 0));
+ "Me", "me@example.com", EPOCH, UTC));
}
@Test
@@ -104,6 +107,21 @@ public void testParsePersonIdent_malformedCases() {
assertPersonIdent("Me <me@example.com 1234567890 -0700", null);
}
+ @Test
+ public void testParsePersonIdent_badTz() {
+ PersonIdent tooBig = RawParseUtils
+ .parsePersonIdent("Me <me@example.com> 1234567890 +8315");
+ assertEquals(tooBig.getZoneOffset().getTotalSeconds(), 0);
+
+ PersonIdent tooSmall = RawParseUtils
+ .parsePersonIdent("Me <me@example.com> 1234567890 -8315");
+ assertEquals(tooSmall.getZoneOffset().getTotalSeconds(), 0);
+
+ PersonIdent notATime = RawParseUtils
+ .parsePersonIdent("Me <me@example.com> 1234567890 -0370");
+ assertEquals(notATime.getZoneOffset().getTotalSeconds(), 0);
+ }
+
private static void assertPersonIdent(String line, PersonIdent expected) {
PersonIdent actual = RawParseUtils.parsePersonIdent(line);
assertEquals(expected, actual);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RelativeDateFormatterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RelativeDateFormatterTest.java
index 214bbca..a927d8d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RelativeDateFormatterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RelativeDateFormatterTest.java
@@ -16,7 +16,7 @@
import static org.eclipse.jgit.util.RelativeDateFormatter.YEAR_IN_MILLIS;
import static org.junit.Assert.assertEquals;
-import java.util.Date;
+import java.time.Instant;
import org.eclipse.jgit.junit.MockSystemReader;
import org.junit.After;
@@ -37,9 +37,9 @@ public void tearDown() {
private static void assertFormat(long ageFromNow, long timeUnit,
String expectedFormat) {
- Date d = new Date(SystemReader.getInstance().getCurrentTime()
- - ageFromNow * timeUnit);
- String s = RelativeDateFormatter.format(d);
+ long millis = ageFromNow * timeUnit;
+ Instant aTime = SystemReader.getInstance().now().minusMillis(millis);
+ String s = RelativeDateFormatter.format(aTime);
assertEquals(expectedFormat, s);
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/StringUtilsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/StringUtilsTest.java
index 015da16..9a1c710 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/StringUtilsTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/StringUtilsTest.java
@@ -12,6 +12,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
@@ -172,4 +173,22 @@ public void testCommonPrefix() {
assertEquals("foo bar ",
StringUtils.commonPrefix("foo bar 42", "foo bar 24"));
}
+
+ @Test
+ public void testTrim() {
+ assertEquals("a", StringUtils.trim("a", '/'));
+ assertEquals("aaaa", StringUtils.trim("aaaa", '/'));
+ assertEquals("aaa", StringUtils.trim("/aaa", '/'));
+ assertEquals("aaa", StringUtils.trim("aaa/", '/'));
+ assertEquals("aaa", StringUtils.trim("/aaa/", '/'));
+ assertEquals("aa/aa", StringUtils.trim("/aa/aa/", '/'));
+ assertEquals("aa/aa", StringUtils.trim("aa/aa", '/'));
+
+ assertEquals("", StringUtils.trim("", '/'));
+ assertEquals("", StringUtils.trim("/", '/'));
+ assertEquals("", StringUtils.trim("//", '/'));
+ assertEquals("", StringUtils.trim("///", '/'));
+
+ assertNull(StringUtils.trim(null, '/'));
+ }
}
diff --git a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
index 04bda44..e0b049c 100644
--- a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
@@ -4,14 +4,14 @@
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit.ui
Bundle-SymbolicName: org.eclipse.jgit.ui
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-Vendor: %Bundle-Vendor
Bundle-RequiredExecutionEnvironment: JavaSE-17
-Export-Package: org.eclipse.jgit.awtui;version="7.0.0"
-Import-Package: org.eclipse.jgit.errors;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.lib;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.nls;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.revplot;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.revwalk;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.transport;version="[7.0.0,7.1.0)",
- org.eclipse.jgit.util;version="[7.0.0,7.1.0)"
+Export-Package: org.eclipse.jgit.awtui;version="7.3.0"
+Import-Package: org.eclipse.jgit.errors;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.lib;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.nls;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.revplot;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.revwalk;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.transport;version="[7.3.0,7.4.0)",
+ org.eclipse.jgit.util;version="[7.3.0,7.4.0)"
diff --git a/org.eclipse.jgit.ui/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.ui/META-INF/SOURCE-MANIFEST.MF
index 8170fc6..66617ca 100644
--- a/org.eclipse.jgit.ui/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.ui/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@
Bundle-Name: org.eclipse.jgit.ui - Sources
Bundle-SymbolicName: org.eclipse.jgit.ui.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.0.0.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.ui;version="7.0.0.qualifier";roots="."
+Bundle-Version: 7.3.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.ui;version="7.3.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.ui/pom.xml b/org.eclipse.jgit.ui/pom.xml
index 6d1e267..3d8d2a1 100644
--- a/org.eclipse.jgit.ui/pom.xml
+++ b/org.eclipse.jgit.ui/pom.xml
@@ -19,7 +19,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.ui</artifactId>
diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters
index caefce3..877a488 100644
--- a/org.eclipse.jgit/.settings/.api_filters
+++ b/org.eclipse.jgit/.settings/.api_filters
@@ -1,10 +1,62 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<component id="org.eclipse.jgit" version="2">
- <resource path="src/org/eclipse/jgit/lib/GpgSignatureVerifier.java" type="org.eclipse.jgit.lib.GpgSignatureVerifier">
- <filter id="404000815">
+ <resource path="src/org/eclipse/jgit/lib/RefDatabase.java" type="org.eclipse.jgit.lib.RefDatabase">
+ <filter id="336695337">
<message_arguments>
- <message_argument value="org.eclipse.jgit.lib.GpgSignatureVerifier"/>
- <message_argument value="verify(GpgConfig, byte[], byte[])"/>
+ <message_argument value="org.eclipse.jgit.lib.RefDatabase"/>
+ <message_argument value="getReflogReader(Ref)"/>
+ </message_arguments>
+ </filter>
+ <filter id="336695337">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.lib.RefDatabase"/>
+ <message_argument value="getReflogReader(String)"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/jgit/lib/TypedConfigGetter.java" type="org.eclipse.jgit.lib.TypedConfigGetter">
+ <filter id="403804204">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.lib.TypedConfigGetter"/>
+ <message_argument value="getBoolean(Config, String, String, String, Boolean)"/>
+ </message_arguments>
+ </filter>
+ <filter id="403804204">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.lib.TypedConfigGetter"/>
+ <message_argument value="getInt(Config, String, String, String, Integer)"/>
+ </message_arguments>
+ </filter>
+ <filter id="403804204">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.lib.TypedConfigGetter"/>
+ <message_argument value="getIntInRange(Config, String, String, String, Integer, Integer, Integer)"/>
+ </message_arguments>
+ </filter>
+ <filter id="403804204">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.lib.TypedConfigGetter"/>
+ <message_argument value="getIntInRange(Config, String, String, String, int, int, Integer)"/>
+ </message_arguments>
+ </filter>
+ <filter id="403804204">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.lib.TypedConfigGetter"/>
+ <message_argument value="getLong(Config, String, String, String, Long)"/>
+ </message_arguments>
+ </filter>
+ <filter id="403804204">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.lib.TypedConfigGetter"/>
+ <message_argument value="getTimeUnit(Config, String, String, String, Long, TimeUnit)"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/jgit/revwalk/RevWalk.java" type="org.eclipse.jgit.revwalk.RevWalk">
+ <filter id="1142947843">
+ <message_arguments>
+ <message_argument value="6.10.1"/>
+ <message_argument value="isMergedIntoAnyCommit(RevCommit, Collection<RevCommit>)"/>
</message_arguments>
</filter>
</resource>
diff --git a/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs
index c4dc76f..ef3d8ec 100644
--- a/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs
@@ -40,7 +40,7 @@
org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=error
org.eclipse.jdt.core.compiler.problem.invalidJavadoc=error
org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
-org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=disabled
org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private
org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF
index 2cdfa99..5f851ec 100644
--- a/org.eclipse.jgit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/MANIFEST.MF
@@ -3,14 +3,14 @@
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit
Bundle-SymbolicName: org.eclipse.jgit
-Bundle-Version: 7.0.0.qualifier
+Bundle-Version: 7.3.0.qualifier
Bundle-Localization: OSGI-INF/l10n/plugin
Bundle-Vendor: %Bundle-Vendor
Bundle-ActivationPolicy: lazy
Service-Component: OSGI-INF/org.eclipse.jgit.internal.util.CleanupService.xml
Eclipse-ExtensibleAPI: true
-Export-Package: org.eclipse.jgit.annotations;version="7.0.0",
- org.eclipse.jgit.api;version="7.0.0";
+Export-Package: org.eclipse.jgit.annotations;version="7.3.0",
+ org.eclipse.jgit.api;version="7.3.0";
uses:="org.eclipse.jgit.transport,
org.eclipse.jgit.notes,
org.eclipse.jgit.dircache,
@@ -25,72 +25,77 @@
org.eclipse.jgit.revwalk.filter,
org.eclipse.jgit.blame,
org.eclipse.jgit.merge",
- org.eclipse.jgit.api.errors;version="7.0.0";
+ org.eclipse.jgit.api.errors;version="7.3.0";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.errors",
- org.eclipse.jgit.attributes;version="7.0.0";
+ org.eclipse.jgit.attributes;version="7.3.0";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.treewalk",
- org.eclipse.jgit.blame;version="7.0.0";
+ org.eclipse.jgit.blame;version="7.3.0";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.revwalk,
- org.eclipse.jgit.treewalk.filter,
- org.eclipse.jgit.diff",
- org.eclipse.jgit.diff;version="7.0.0";
+ org.eclipse.jgit.blame.cache,
+ org.eclipse.jgit.diff,
+ org.eclipse.jgit.treewalk.filter",
+ org.eclipse.jgit.blame.cache;version="7.3.0";
+ uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.diff;version="7.3.0";
uses:="org.eclipse.jgit.lib,
- org.eclipse.jgit.attributes,
org.eclipse.jgit.revwalk,
org.eclipse.jgit.patch,
+ org.eclipse.jgit.attributes,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.treewalk,
org.eclipse.jgit.util",
- org.eclipse.jgit.dircache;version="7.0.0";
+ org.eclipse.jgit.dircache;version="7.3.0";
uses:="org.eclipse.jgit.events,
org.eclipse.jgit.lib,
org.eclipse.jgit.attributes,
org.eclipse.jgit.treewalk,
org.eclipse.jgit.util",
- org.eclipse.jgit.errors;version="7.0.0";
+ org.eclipse.jgit.errors;version="7.3.0";
uses:="org.eclipse.jgit.transport,
org.eclipse.jgit.dircache,
- org.eclipse.jgit.lib,
- org.eclipse.jgit.internal.storage.pack",
- org.eclipse.jgit.events;version="7.0.0";
+ org.eclipse.jgit.lib",
+ org.eclipse.jgit.events;version="7.3.0";
uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.fnmatch;version="7.0.0",
- org.eclipse.jgit.gitrepo;version="7.0.0";
+ org.eclipse.jgit.fnmatch;version="7.3.0",
+ org.eclipse.jgit.gitrepo;version="7.3.0";
uses:="org.xml.sax.helpers,
org.eclipse.jgit.api,
+ org.eclipse.jgit.api.errors,
org.eclipse.jgit.lib,
org.eclipse.jgit.revwalk,
org.xml.sax",
- org.eclipse.jgit.gitrepo.internal;version="7.0.0";x-internal:=true,
- org.eclipse.jgit.hooks;version="7.0.0";uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.ignore;version="7.0.0",
- org.eclipse.jgit.ignore.internal;version="7.0.0";
+ org.eclipse.jgit.gitrepo.internal;version="7.3.0";x-internal:=true,
+ org.eclipse.jgit.hooks;version="7.3.0";
+ uses:="org.eclipse.jgit.lib,
+ org.eclipse.jgit.util",
+ org.eclipse.jgit.ignore;version="7.3.0",
+ org.eclipse.jgit.ignore.internal;version="7.3.0";
x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal;version="7.0.0";
+ org.eclipse.jgit.internal;version="7.3.0";
x-friends:="org.eclipse.jgit.test,
org.eclipse.jgit.http.test",
- org.eclipse.jgit.internal.diff;version="7.0.0";
+ org.eclipse.jgit.internal.diff;version="7.3.0";
x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.diffmergetool;version="7.0.0";
+ org.eclipse.jgit.internal.diffmergetool;version="7.3.0";
x-friends:="org.eclipse.jgit.test,
org.eclipse.jgit.pgm.test,
org.eclipse.jgit.pgm,
org.eclipse.egit.ui",
- org.eclipse.jgit.internal.fsck;version="7.0.0";
+ org.eclipse.jgit.internal.fsck;version="7.3.0";
x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.revwalk;version="7.0.0";
+ org.eclipse.jgit.internal.revwalk;version="7.3.0";
x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.storage.commitgraph;version="7.0.0";
+ org.eclipse.jgit.internal.storage.commitgraph;version="7.3.0";
x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.storage.dfs;version="7.0.0";
+ org.eclipse.jgit.internal.storage.dfs;version="7.3.0";
x-friends:="org.eclipse.jgit.test,
org.eclipse.jgit.http.server,
org.eclipse.jgit.http.test,
org.eclipse.jgit.lfs.test",
- org.eclipse.jgit.internal.storage.file;version="7.0.0";
+ org.eclipse.jgit.internal.storage.file;version="7.3.0";
x-friends:="org.eclipse.jgit.test,
org.eclipse.jgit.junit,
org.eclipse.jgit.junit.http,
@@ -99,41 +104,43 @@
org.eclipse.jgit.pgm,
org.eclipse.jgit.pgm.test,
org.eclipse.jgit.ssh.apache",
- org.eclipse.jgit.internal.storage.io;version="7.0.0";
+ org.eclipse.jgit.internal.storage.io;version="7.3.0";
x-friends:="org.eclipse.jgit.junit,
org.eclipse.jgit.test,
org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.memory;version="7.0.0";
+ org.eclipse.jgit.internal.storage.memory;version="7.3.0";
x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.storage.pack;version="7.0.0";
+ org.eclipse.jgit.internal.storage.midx;version="7.3.0";x-internal:=true,
+ org.eclipse.jgit.internal.storage.pack;version="7.3.0";
x-friends:="org.eclipse.jgit.junit,
org.eclipse.jgit.test,
org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.reftable;version="7.0.0";
+ org.eclipse.jgit.internal.storage.reftable;version="7.3.0";
x-friends:="org.eclipse.jgit.http.test,
org.eclipse.jgit.junit,
org.eclipse.jgit.test,
org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.submodule;version="7.0.0";x-internal:=true,
- org.eclipse.jgit.internal.transport.connectivity;version="7.0.0";
+ org.eclipse.jgit.internal.submodule;version="7.3.0";x-internal:=true,
+ org.eclipse.jgit.internal.transport.connectivity;version="7.3.0";
x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.transport.http;version="7.0.0";
+ org.eclipse.jgit.internal.transport.http;version="7.3.0";
x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.transport.parser;version="7.0.0";
+ org.eclipse.jgit.internal.transport.parser;version="7.3.0";
x-friends:="org.eclipse.jgit.http.server,
org.eclipse.jgit.test",
- org.eclipse.jgit.internal.transport.ssh;version="7.0.0";
+ org.eclipse.jgit.internal.transport.ssh;version="7.3.0";
x-friends:="org.eclipse.jgit.ssh.apache,
org.eclipse.jgit.ssh.jsch,
org.eclipse.jgit.test",
- org.eclipse.jgit.internal.util;version="7.0.0";
- x-friends:=" org.eclipse.jgit.junit",
- org.eclipse.jgit.lib;version="7.0.0";
+ org.eclipse.jgit.internal.util;version="7.3.0";
+ x-friends:="org.eclipse.jgit.junit",
+ org.eclipse.jgit.lib;version="7.3.0";
uses:="org.eclipse.jgit.transport,
org.eclipse.jgit.util.sha1,
org.eclipse.jgit.dircache,
org.eclipse.jgit.revwalk,
org.eclipse.jgit.internal.storage.file,
+ org.eclipse.jgit.api,
org.eclipse.jgit.attributes,
org.eclipse.jgit.events,
com.googlecode.javaewah,
@@ -142,12 +149,12 @@
org.eclipse.jgit.util,
org.eclipse.jgit.submodule,
org.eclipse.jgit.util.time",
- org.eclipse.jgit.lib.internal;version="7.0.0";
+ org.eclipse.jgit.lib.internal;version="7.3.0";
x-friends:="org.eclipse.jgit.test,
org.eclipse.jgit.pgm,
org.eclipse.egit.ui",
- org.eclipse.jgit.logging;version="7.0.0",
- org.eclipse.jgit.merge;version="7.0.0";
+ org.eclipse.jgit.logging;version="7.3.0",
+ org.eclipse.jgit.merge;version="7.3.0";
uses:="org.eclipse.jgit.dircache,
org.eclipse.jgit.lib,
org.eclipse.jgit.revwalk,
@@ -156,67 +163,69 @@
org.eclipse.jgit.util,
org.eclipse.jgit.api,
org.eclipse.jgit.attributes",
- org.eclipse.jgit.nls;version="7.0.0",
- org.eclipse.jgit.notes;version="7.0.0";
+ org.eclipse.jgit.nls;version="7.3.0",
+ org.eclipse.jgit.notes;version="7.3.0";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.revwalk,
org.eclipse.jgit.treewalk,
org.eclipse.jgit.merge",
- org.eclipse.jgit.patch;version="7.0.0";
+ org.eclipse.jgit.patch;version="7.3.0";
uses:="org.eclipse.jgit.lib,
+ org.eclipse.jgit.revwalk,
org.eclipse.jgit.diff",
- org.eclipse.jgit.revplot;version="7.0.0";
- uses:="org.eclipse.jgit.lib,
- org.eclipse.jgit.revwalk",
- org.eclipse.jgit.revwalk;version="7.0.0";
- uses:="org.eclipse.jgit.lib,
- org.eclipse.jgit.diff,
- org.eclipse.jgit.treewalk.filter,
- org.eclipse.jgit.revwalk.filter,
- org.eclipse.jgit.treewalk",
- org.eclipse.jgit.revwalk.filter;version="7.0.0";
+ org.eclipse.jgit.revplot;version="7.3.0";
uses:="org.eclipse.jgit.revwalk,
- org.eclipse.jgit.lib,
- org.eclipse.jgit.util",
- org.eclipse.jgit.storage.file;version="7.0.0";
+ org.eclipse.jgit.lib",
+ org.eclipse.jgit.revwalk;version="7.3.0";
uses:="org.eclipse.jgit.lib,
- org.eclipse.jgit.util",
- org.eclipse.jgit.storage.pack;version="7.0.0";
- uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.submodule;version="7.0.0";
- uses:="org.eclipse.jgit.lib,
+ org.eclipse.jgit.revwalk.filter,
org.eclipse.jgit.diff,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.treewalk,
+ org.eclipse.jgit.internal.storage.commitgraph",
+ org.eclipse.jgit.revwalk.filter;version="7.3.0";
+ uses:="org.eclipse.jgit.revwalk,
+ org.eclipse.jgit.lib,
org.eclipse.jgit.util",
- org.eclipse.jgit.transport;version="7.0.0";
+ org.eclipse.jgit.storage.file;version="7.3.0";
+ uses:="org.eclipse.jgit.lib,
+ org.eclipse.jgit.util",
+ org.eclipse.jgit.storage.pack;version="7.3.0";
+ uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.submodule;version="7.3.0";
+ uses:="org.eclipse.jgit.lib,
+ org.eclipse.jgit.treewalk.filter,
+ org.eclipse.jgit.diff,
+ org.eclipse.jgit.treewalk,
+ org.eclipse.jgit.util",
+ org.eclipse.jgit.transport;version="7.3.0";
uses:="javax.crypto,
+ org.eclipse.jgit.hooks,
org.eclipse.jgit.util.io,
org.eclipse.jgit.lib,
- org.eclipse.jgit.revwalk,
org.eclipse.jgit.transport.http,
- org.eclipse.jgit.internal.storage.file,
+ org.eclipse.jgit.revwalk,
org.eclipse.jgit.treewalk,
org.eclipse.jgit.util,
org.eclipse.jgit.internal.storage.pack,
org.eclipse.jgit.transport.resolver,
org.eclipse.jgit.storage.pack,
org.eclipse.jgit.errors",
- org.eclipse.jgit.transport.http;version="7.0.0";
+ org.eclipse.jgit.transport.http;version="7.3.0";
uses:="javax.net.ssl",
- org.eclipse.jgit.transport.resolver;version="7.0.0";
+ org.eclipse.jgit.transport.resolver;version="7.3.0";
uses:="org.eclipse.jgit.transport,
org.eclipse.jgit.lib",
- org.eclipse.jgit.treewalk;version="7.0.0";
+ org.eclipse.jgit.treewalk;version="7.3.0";
uses:="org.eclipse.jgit.dircache,
org.eclipse.jgit.lib,
org.eclipse.jgit.attributes,
org.eclipse.jgit.revwalk,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.util",
- org.eclipse.jgit.treewalk.filter;version="7.0.0";
+ org.eclipse.jgit.treewalk.filter;version="7.3.0";
uses:="org.eclipse.jgit.treewalk",
- org.eclipse.jgit.util;version="7.0.0";
+ org.eclipse.jgit.util;version="7.3.0";
uses:="org.eclipse.jgit.transport,
org.eclipse.jgit.hooks,
org.eclipse.jgit.revwalk,
@@ -229,18 +238,18 @@
org.eclipse.jgit.treewalk,
javax.net.ssl,
org.eclipse.jgit.util.time",
- org.eclipse.jgit.util.io;version="7.0.0";
+ org.eclipse.jgit.util.io;version="7.3.0";
uses:="org.eclipse.jgit.attributes,
org.eclipse.jgit.lib,
org.eclipse.jgit.treewalk",
- org.eclipse.jgit.util.sha1;version="7.0.0",
- org.eclipse.jgit.util.time;version="7.0.0"
+ org.eclipse.jgit.util.sha1;version="7.3.0",
+ org.eclipse.jgit.util.time;version="7.3.0"
Bundle-RequiredExecutionEnvironment: JavaSE-17
Import-Package: com.googlecode.javaewah;version="[1.1.6,2.0.0)",
javax.crypto,
javax.management,
javax.net.ssl,
- org.apache.commons.codec.digest;version="1.15.0",
+ org.apache.commons.codec.digest;version="[1.15.0,2.0.0)",
org.slf4j;version="[1.7.0,3.0.0)",
org.xml.sax,
org.xml.sax.helpers
diff --git a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
index 7531018..a9e669b 100644
--- a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@
Bundle-Name: org.eclipse.jgit - Sources
Bundle-SymbolicName: org.eclipse.jgit.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 7.0.0.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit;version="7.0.0.qualifier";roots="."
+Bundle-Version: 7.3.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit;version="7.3.0.qualifier";roots="."
diff --git a/org.eclipse.jgit/pom.xml b/org.eclipse.jgit/pom.xml
index 9397674..c65a440 100644
--- a/org.eclipse.jgit/pom.xml
+++ b/org.eclipse.jgit/pom.xml
@@ -20,7 +20,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit</artifactId>
@@ -49,7 +49,6 @@
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
- <version>1.16.0</version>
</dependency>
</dependencies>
diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
index 19c9008..27270a1 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
@@ -64,8 +64,6 @@
binaryHunkInvalidLength=Binary hunk, line {0}: input corrupt; expected length byte, got 0x{1}
binaryHunkLineTooShort=Binary hunk, line {0}: input ended prematurely
binaryHunkMissingNewline=Binary hunk, line {0}: input line not terminated by newline
-bitmapAccessErrorForPackfile=Error whilst trying to access bitmap file for {}
-bitmapFailedToGet=Failed to get bitmap index file {}
bitmapMissingObject=Bitmap at {0} is missing {1}.
bitmapsMustBePrepared=Bitmaps must be prepared before they may be written.
bitmapUseNoopNoListener=Use NOOP instance for no listener
@@ -78,6 +76,7 @@
buildingBitmaps=Building bitmaps
cachedPacksPreventsIndexCreation=Using cached packs prevents index creation
cachedPacksPreventsListingObjects=Using cached packs prevents listing objects
+cacheRegionAllOrNoneNull=expected all null or none: {0}, {1}
cannotAccessLastModifiedForSafeDeletion=Unable to access lastModifiedTime of file {0}, skip deletion since we cannot safely avoid race condition
cannotBeCombined=Cannot be combined.
cannotBeRecursiveWhenTreesAreIncluded=TreeWalk shouldn't be recursive when tree objects are included.
@@ -267,6 +266,7 @@
deletingNotSupported=Deleting {0} not supported.
depthMustBeAt1=Depth must be >= 1
depthWithUnshallow=Depth and unshallow can\'t be used together
+deprecatedTrustFolderStat=Option core.trustFolderStat is deprecated, replace it by core.trustStat.
destinationIsNotAWildcard=Destination is not a wildcard.
detachedHeadDetected=HEAD is detached
diffToolNotGivenError=No diff tool provided and no defaults configured.
@@ -285,6 +285,9 @@
downloadCancelled=Download cancelled
downloadCancelledDuringIndexing=Download cancelled during indexing
duplicateAdvertisementsOf=duplicate advertisements of {0}
+duplicateCacheTablesGiven=Duplicate cache tables given
+duplicatePackExtensionsForCacheTables=Duplicate pack extension {0} in cache tables
+duplicatePackExtensionsSet=Attempting to configure duplicate pack extensions: {0}.{1}.{2} contains {3}
duplicateRef=Duplicate ref: {0}
duplicateRefAttribute=Duplicate ref attribute: {0}
duplicateRemoteRefUpdateIsIllegal=Duplicate remote ref update is illegal. Affected remote name: {0}
@@ -461,6 +464,7 @@
invalidTimeUnitValue2=Invalid time unit value: {0}.{1}={2}
invalidTimeUnitValue3=Invalid time unit value: {0}.{1}.{2}={3}
invalidTreeZeroLengthName=Cannot append a tree entry with zero-length name
+invalidTrustStat=core.trustStat must not be set to TrustStat.INHERIT, falling back to TrustStat.ALWAYS.
invalidURL=Invalid URL {0}
invalidWildcards=Invalid wildcards {0}
invalidRefSpec=Invalid refspec {0}
@@ -499,7 +503,7 @@
mergeStrategyAlreadyExistsAsDefault=Merge strategy "{0}" already exists as a default strategy
mergeStrategyDoesNotSupportHeads=merge strategy {0} does not support {1} heads to be merged into HEAD
mergeUsingStrategyResultedInDescription=Merge of revisions {0} with base {1} using strategy {2} resulted in: {3}. {4}
-mergeRecursiveConflictsWhenMergingCommonAncestors=Multiple common ancestors were found and merging them resulted in a conflict: {0}, {1}
+mergeRecursiveConflictsWhenMergingCommonAncestors=Multiple common ancestors were found and merging them resulted in a conflict: {0}, {1}\nFailing paths: {2}
mergeRecursiveTooManyMergeBasesFor = "More than {0} merge bases for:\n a {1}\n b {2} found:\n count {3}"
mergeToolNotGivenError=No merge tool provided and no defaults configured.
mergeToolNullError=Parameter for merge tool cannot be null.
@@ -524,6 +528,8 @@
month=month
months=months
monthsAgo={0} months ago
+multiPackIndexUnexpectedSize=MultiPack index: expected %d bytes but out has %d bytes
+multiPackIndexWritingCancelled=Multipack index writing was canceled
multipleMergeBasesFor=Multiple merge bases for:\n {0}\n {1} found:\n {2}\n {3}
nameMustNotBeNullOrEmpty=Ref name must not be null or empty.
need2Arguments=Need 2 arguments
@@ -539,6 +545,8 @@
noMergeHeadSpecified=No merge head specified
nonBareLinkFilesNotSupported=Link files are not supported with nonbare repos
nonCommitToHeads=Cannot point a branch to a non-commit object
+noPackExtConfigurationGiven=No PackExt configuration given
+noPackExtGivenForConfiguration=No PackExt given for configuration
noPathAttributesFound=No Attributes found for {0}.
noSuchRef=no such ref
noSuchRefKnown=no such ref: {0}
@@ -571,7 +579,6 @@
oldIdMustNotBeNull=Expected old ID must not be null
onlyOneFetchSupported=Only one fetch supported
onlyOneOperationCallPerConnectionIsSupported=Only one operation call per connection is supported.
-onlyOpenPgpSupportedForSigning=OpenPGP is the only supported signing option with JGit at this time (gpg.format must be set to openpgp).
openFilesMustBeAtLeast1=Open files must be >= 1
openingConnection=Opening connection
operationCanceled=Operation {0} was canceled
@@ -593,6 +600,8 @@
packingCancelledDuringObjectsWriting=Packing cancelled during objects writing
packObjectCountMismatch=Pack object count mismatch: pack {0} index {1}: {2}
packRefs=Pack refs
+packRefsFailed=Packing refs failed
+packRefsSuccessful=Packed refs successfully
packSizeNotSetYet=Pack size not yet set since it has not yet been received
packTooLargeForIndexVersion1=Pack too large for index version 1
packWasDeleted=Pack file {0} was deleted, removing it from pack list
@@ -609,6 +618,7 @@
personIdentEmailNonNull=E-mail address of PersonIdent must not be null.
personIdentNameNonNull=Name of PersonIdent must not be null.
postCommitHookFailed=Execution of post-commit hook failed: {0}.
+precedenceTrustConfig=Both core.trustFolderStat and core.trustStat are set, ignoring trustFolderStat since trustStat takes precedence. Remove core.trustFolderStat from your configuration.
prefixRemote=remote:
problemWithResolvingPushRefSpecsLocally=Problem with resolving push ref specs locally: {0}
progressMonUploading=Uploading {0}
@@ -636,8 +646,6 @@
readerIsRequired=Reader is required
readingObjectsFromLocalRepositoryFailed=reading objects from local repository failed: {0}
readLastModifiedFailed=Reading lastModified of {0} failed
-readPipeIsNotAllowed=FS.readPipe() isn't allowed for command ''{0}''. Working directory: ''{1}''.
-readPipeIsNotAllowedRequiredPermission=FS.readPipe() isn't allowed for command ''{0}''. Working directory: ''{1}''. Required permission: {2}.
readTimedOut=Read timed out after {0} ms
receivePackObjectTooLarge1=Object too large, rejecting the pack. Max object size limit is {0} bytes.
receivePackObjectTooLarge2=Object too large ({0} bytes), rejecting the pack. Max object size limit is {1} bytes.
@@ -718,6 +726,8 @@
shutdownCleanup=Cleanup {} during JVM shutdown
shutdownCleanupFailed=Cleanup during JVM shutdown failed
shutdownCleanupListenerFailed=Cleanup of {0} during JVM shutdown failed
+signatureServiceConflict={0} conflict for type {1}. Already registered is {2}; additional factory {3} is ignored.
+signatureTypeUnknown=No signer for {0} signatures. Use another signature type for git config gpg.format, or do not sign.
signatureVerificationError=Signature verification failed
signatureVerificationUnavailable=No signature verifier registered
signedTagMessageNoLf=A non-empty message of a signed tag must end in LF.
@@ -803,6 +813,7 @@
tSizeMustBeGreaterOrEqual1=tSize must be >= 1
unableToCheckConnectivity=Unable to check connectivity.
unableToCreateNewObject=Unable to create new object: {0}
+unableToReadFullArray=Unable to read an array with {0} elements from the stream
unableToReadFullInt=Unable to read a full int from the stream
unableToReadPackfile=Unable to read packfile {0}
unableToRemovePath=Unable to remove path ''{0}''
@@ -829,6 +840,7 @@
unknownObjectInIndex=unknown object {0} found in index but not in pack file
unknownObjectType=Unknown object type {0}.
unknownObjectType2=unknown
+unknownPackExtension=Unknown pack extension: {0}.{1}.{2}={3}
unknownPositionEncoding=Unknown position encoding %s
unknownRefStorageFormat=Unknown ref storage format "{0}"
unknownRepositoryFormat=Unknown repository format
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java
index c895dc9..b4d1cab 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com>
- * Copyright (C) 2010, Stefan Lay <stefan.lay@sap.com> and others
+ * Copyright (C) 2010, 2025 Stefan Lay <stefan.lay@sap.com> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -17,6 +17,7 @@
import java.io.IOException;
import java.io.InputStream;
+import java.text.MessageFormat;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
@@ -59,8 +60,15 @@ public class AddCommand extends GitCommand<DirCache> {
private WorkingTreeIterator workingTreeIterator;
+ // Update only known index entries, don't add new ones. If there's no file
+ // for an index entry, remove it: stage deletions.
private boolean update = false;
+ // If TRUE, also stage deletions, otherwise only update and add index
+ // entries.
+ // If not set explicitly
+ private Boolean all;
+
// This defaults to true because it's what JGit has been doing
// traditionally. The C git default would be false.
private boolean renormalize = true;
@@ -82,6 +90,17 @@ public AddCommand(Repository repo) {
* A directory name (e.g. <code>dir</code> to add <code>dir/file1</code> and
* <code>dir/file2</code>) can also be given to add all files in the
* directory, recursively. Fileglobs (e.g. *.c) are not yet supported.
+ * </p>
+ * <p>
+ * If a pattern {@code "."} is added, all changes in the git repository's
+ * working tree will be added.
+ * </p>
+ * <p>
+ * File patterns are required unless {@code isUpdate() == true} or
+ * {@link #setAll(boolean)} is called. If so and no file patterns are given,
+ * all changes will be added (i.e., a file pattern of {@code "."} is
+ * implied).
+ * </p>
*
* @param filepattern
* repository-relative path of file/directory to add (with
@@ -113,15 +132,41 @@ public AddCommand setWorkingTreeIterator(WorkingTreeIterator f) {
* Executes the {@code Add} command. Each instance of this class should only
* be used for one invocation of the command. Don't call this method twice
* on an instance.
+ * </p>
+ *
+ * @throws JGitInternalException
+ * on errors, but also if {@code isUpdate() == true} _and_
+ * {@link #setAll(boolean)} had been called
+ * @throws NoFilepatternException
+ * if no file patterns are given if {@code isUpdate() == false}
+ * and {@link #setAll(boolean)} was not called
*/
@Override
public DirCache call() throws GitAPIException, NoFilepatternException {
-
- if (filepatterns.isEmpty())
- throw new NoFilepatternException(JGitText.get().atLeastOnePatternIsRequired);
checkCallable();
+
+ if (update && all != null) {
+ throw new JGitInternalException(MessageFormat.format(
+ JGitText.get().illegalCombinationOfArguments,
+ "--update", "--all/--no-all")); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ boolean addAll;
+ if (filepatterns.isEmpty()) {
+ if (update || all != null) {
+ addAll = true;
+ } else {
+ throw new NoFilepatternException(
+ JGitText.get().atLeastOnePatternIsRequired);
+ }
+ } else {
+ addAll = filepatterns.contains("."); //$NON-NLS-1$
+ if (all == null && !update) {
+ all = Boolean.TRUE;
+ }
+ }
+ boolean stageDeletions = update || (all != null && all.booleanValue());
+
DirCache dc = null;
- boolean addAll = filepatterns.contains("."); //$NON-NLS-1$
try (ObjectInserter inserter = repo.newObjectInserter();
NameConflictTreeWalk tw = new NameConflictTreeWalk(repo)) {
@@ -181,7 +226,8 @@ public DirCache call() throws GitAPIException, NoFilepatternException {
if (f == null) { // working tree file does not exist
if (entry != null
- && (!update || GITLINK == entry.getFileMode())) {
+ && (!stageDeletions
+ || GITLINK == entry.getFileMode())) {
builder.add(entry);
}
continue;
@@ -252,7 +298,8 @@ public DirCache call() throws GitAPIException, NoFilepatternException {
}
/**
- * Set whether to only match against already tracked files
+ * Set whether to only match against already tracked files. If
+ * {@code update == true}, re-sets a previous {@link #setAll(boolean)}.
*
* @param update
* If set to true, the command only matches {@code filepattern}
@@ -314,4 +361,32 @@ public AddCommand setRenormalize(boolean renormalize) {
public boolean isRenormalize() {
return renormalize;
}
+
+ /**
+ * Defines whether the command will use '--all' mode: update existing index
+ * entries, add new entries, and remove index entries for which there is no
+ * file. (In other words: also stage deletions.)
+ * <p>
+ * The setting is independent of {@link #setUpdate(boolean)}.
+ * </p>
+ *
+ * @param all
+ * whether to enable '--all' mode
+ * @return {@code this}
+ * @since 7.2
+ */
+ public AddCommand setAll(boolean all) {
+ this.all = Boolean.valueOf(all);
+ return this;
+ }
+
+ /**
+ * Tells whether '--all' has been set for this command.
+ *
+ * @return {@code true} if it was set; {@code false} otherwise
+ * @since 7.2
+ */
+ public boolean isAll() {
+ return all != null && all.booleanValue();
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
index c133219..32c242f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
@@ -644,24 +644,6 @@ public CheckoutCommand setOrphan(boolean orphan) {
/**
* Specify to force the ref update in case of a branch switch.
*
- * @param force
- * if <code>true</code> and the branch with the given name
- * already exists, the start-point of an existing branch will be
- * set to a new start-point; if false, the existing branch will
- * not be changed
- * @return this instance
- * @deprecated this method was badly named comparing its semantics to native
- * git's checkout --force option, use
- * {@link #setForceRefUpdate(boolean)} instead
- */
- @Deprecated
- public CheckoutCommand setForce(boolean force) {
- return setForceRefUpdate(force);
- }
-
- /**
- * Specify to force the ref update in case of a branch switch.
- *
* In releases prior to 5.2 this method was called setForce() but this name
* was misunderstood to implement native git's --force option, which is not
* true.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
index 3e034f1..4a536b9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
@@ -67,6 +67,8 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
private boolean bare;
+ private boolean relativePaths;
+
private FS fs;
private String remote = Constants.DEFAULT_REMOTE_NAME;
@@ -264,6 +266,7 @@ void verifyDirectories(URIish u) {
private Repository init() throws GitAPIException {
InitCommand command = Git.init();
command.setBare(bare);
+ command.setRelativeDirs(relativePaths);
if (fs != null) {
command.setFs(fs);
}
@@ -555,6 +558,20 @@ public CloneCommand setBare(boolean bare) throws IllegalStateException {
}
/**
+ * Set whether the cloned repository shall use relative paths for GIT_DIR
+ * and GIT_WORK_TREE
+ *
+ * @param relativePaths
+ * if true, use relative paths for GIT_DIR and GIT_WORK_TREE
+ * @return this instance
+ * @since 7.2
+ */
+ public CloneCommand setRelativePaths(boolean relativePaths) {
+ this.relativePaths = relativePaths;
+ return this;
+ }
+
+ /**
* Set the file system abstraction to be used for repositories created by
* this command.
*
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
index a1a2cc0..a7d409c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
@@ -51,9 +51,6 @@
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.GpgConfig;
-import org.eclipse.jgit.lib.GpgConfig.GpgFormat;
-import org.eclipse.jgit.lib.GpgObjectSigner;
-import org.eclipse.jgit.lib.GpgSigner;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.PersonIdent;
@@ -62,6 +59,8 @@
import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryState;
+import org.eclipse.jgit.lib.Signer;
+import org.eclipse.jgit.lib.Signers;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevTag;
@@ -129,7 +128,7 @@ public class CommitCommand extends GitCommand<RevCommit> {
private String signingKey;
- private GpgSigner gpgSigner;
+ private Signer signer;
private GpgConfig gpgConfig;
@@ -319,30 +318,22 @@ private void checkIfEmpty(RevWalk rw, ObjectId headId, ObjectId indexTreeId)
}
}
- private void sign(CommitBuilder commit) throws ServiceUnavailableException,
- CanceledException, UnsupportedSigningFormatException {
- if (gpgSigner == null) {
- gpgSigner = GpgSigner.getDefault();
- if (gpgSigner == null) {
- throw new ServiceUnavailableException(
- JGitText.get().signingServiceUnavailable);
+ private void sign(CommitBuilder commit)
+ throws CanceledException, IOException,
+ UnsupportedSigningFormatException {
+ if (signer == null) {
+ signer = Signers.get(gpgConfig.getKeyFormat());
+ if (signer == null) {
+ throw new UnsupportedSigningFormatException(MessageFormat
+ .format(JGitText.get().signatureTypeUnknown,
+ gpgConfig.getKeyFormat().toConfigValue()));
}
}
if (signingKey == null) {
signingKey = gpgConfig.getSigningKey();
}
- if (gpgSigner instanceof GpgObjectSigner) {
- ((GpgObjectSigner) gpgSigner).signObject(commit,
- signingKey, committer, credentialsProvider,
- gpgConfig);
- } else {
- if (gpgConfig.getKeyFormat() != GpgFormat.OPENPGP) {
- throw new UnsupportedSigningFormatException(JGitText
- .get().onlyOpenPgpSupportedForSigning);
- }
- gpgSigner.sign(commit, signingKey, committer,
- credentialsProvider);
- }
+ signer.signObject(repo, gpgConfig, commit, committer, signingKey,
+ credentialsProvider);
}
private void updateRef(RepositoryState state, ObjectId headId,
@@ -1097,22 +1088,22 @@ public CommitCommand setSign(Boolean sign) {
}
/**
- * Sets the {@link GpgSigner} to use if the commit is to be signed.
+ * Sets the {@link Signer} to use if the commit is to be signed.
*
* @param signer
* to use; if {@code null}, the default signer will be used
* @return {@code this}
- * @since 5.11
+ * @since 7.0
*/
- public CommitCommand setGpgSigner(GpgSigner signer) {
+ public CommitCommand setSigner(Signer signer) {
checkCallable();
- this.gpgSigner = signer;
+ this.signer = signer;
return this;
}
/**
* Sets an external {@link GpgConfig} to use. Whether it will be used is at
- * the discretion of the {@link #setGpgSigner(GpgSigner)}.
+ * the discretion of the {@link #setSigner(Signer)}.
*
* @param config
* to set; if {@code null}, the config will be loaded from the
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java
index 805a886..d252628 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java
@@ -15,11 +15,11 @@
import java.io.IOException;
import java.text.MessageFormat;
+import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
-import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -76,6 +76,11 @@ public class DescribeCommand extends GitCommand<String> {
private List<FileNameMatcher> matchers = new ArrayList<>();
/**
+ * Pattern matchers to be applied to tags for exclusion.
+ */
+ private List<FileNameMatcher> excludeMatchers = new ArrayList<>();
+
+ /**
* Whether to use all refs in the refs/ namespace
*/
private boolean useAll;
@@ -263,6 +268,27 @@ public DescribeCommand setMatch(String... patterns) throws InvalidPatternExcepti
return this;
}
+ /**
+ * Sets one or more {@code glob(7)} patterns that tags must not match to be
+ * considered. If multiple patterns are provided, they will all be applied.
+ *
+ * @param patterns
+ * the {@code glob(7)} pattern or patterns
+ * @return {@code this}
+ * @throws org.eclipse.jgit.errors.InvalidPatternException
+ * if the pattern passed in was invalid.
+ * @see <a href=
+ * "https://www.kernel.org/pub/software/scm/git/docs/git-describe.html"
+ * >Git documentation about describe</a>
+ * @since 7.2
+ */
+ public DescribeCommand setExclude(String... patterns) throws InvalidPatternException {
+ for (String p : patterns) {
+ excludeMatchers.add(new FileNameMatcher(p, null));
+ }
+ return this;
+ }
+
private final Comparator<Ref> TAG_TIE_BREAKER = new Comparator<>() {
@Override
@@ -274,25 +300,28 @@ public int compare(Ref o1, Ref o2) {
}
}
- private Date tagDate(Ref tag) throws IOException {
+ private Instant tagDate(Ref tag) throws IOException {
RevTag t = w.parseTag(tag.getObjectId());
w.parseBody(t);
- return t.getTaggerIdent().getWhen();
+ return t.getTaggerIdent().getWhenAsInstant();
}
};
private Optional<Ref> getBestMatch(List<Ref> tags) {
if (tags == null || tags.isEmpty()) {
return Optional.empty();
- } else if (matchers.isEmpty()) {
+ } else if (matchers.isEmpty() && excludeMatchers.isEmpty()) {
Collections.sort(tags, TAG_TIE_BREAKER);
return Optional.of(tags.get(0));
- } else {
+ }
+
+ Stream<Ref> matchingTags;
+ if (!matchers.isEmpty()) {
// Find the first tag that matches in the stream of all tags
// filtered by matchers ordered by tie break order
- Stream<Ref> matchingTags = Stream.empty();
+ matchingTags = Stream.empty();
for (FileNameMatcher matcher : matchers) {
- Stream<Ref> m = tags.stream().filter(
+ Stream<Ref> m = tags.stream().filter( //
tag -> {
matcher.append(formatRefName(tag.getName()));
boolean result = matcher.isMatch();
@@ -301,8 +330,22 @@ private Optional<Ref> getBestMatch(List<Ref> tags) {
});
matchingTags = Stream.of(matchingTags, m).flatMap(i -> i);
}
- return matchingTags.sorted(TAG_TIE_BREAKER).findFirst();
+ } else {
+ // If there are no matchers, there are only excluders
+ // Assume all tags match for now before applying excluders
+ matchingTags = tags.stream();
}
+
+ for (FileNameMatcher matcher : excludeMatchers) {
+ matchingTags = matchingTags.filter( //
+ tag -> {
+ matcher.append(formatRefName(tag.getName()));
+ boolean result = matcher.isMatch();
+ matcher.reset();
+ return !result;
+ });
+ }
+ return matchingTags.sorted(TAG_TIE_BREAKER).findFirst();
}
private ObjectId getObjectIdFromRef(Ref r) throws JGitInternalException {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java
index 0713c38..f24127b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java
@@ -124,7 +124,7 @@ private FetchRecurseSubmodulesMode getRecurseMode(String path) {
FetchRecurseSubmodulesMode mode = repo.getConfig().getEnum(
FetchRecurseSubmodulesMode.values(),
ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
- ConfigConstants.CONFIG_KEY_FETCH_RECURSE_SUBMODULES, null);
+ ConfigConstants.CONFIG_KEY_FETCH_RECURSE_SUBMODULES);
if (mode != null) {
return mode;
}
@@ -132,7 +132,7 @@ private FetchRecurseSubmodulesMode getRecurseMode(String path) {
// Fall back to fetch.recurseSubmodules, if set
mode = repo.getConfig().getEnum(FetchRecurseSubmodulesMode.values(),
ConfigConstants.CONFIG_FETCH_SECTION, null,
- ConfigConstants.CONFIG_KEY_RECURSE_SUBMODULES, null);
+ ConfigConstants.CONFIG_KEY_RECURSE_SUBMODULES);
if (mode != null) {
return mode;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/GarbageCollectCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/GarbageCollectCommand.java
index 88d7e91..f6935e1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/GarbageCollectCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/GarbageCollectCommand.java
@@ -12,6 +12,7 @@
import java.io.IOException;
import java.text.MessageFormat;
import java.text.ParseException;
+import java.time.Instant;
import java.util.Date;
import java.util.Properties;
import java.util.concurrent.ExecutionException;
@@ -59,7 +60,7 @@ public class GarbageCollectCommand extends GitCommand<Properties> {
private ProgressMonitor monitor;
- private Date expire;
+ private Instant expire;
private PackConfig pconfig;
@@ -98,8 +99,29 @@ public GarbageCollectCommand setProgressMonitor(ProgressMonitor monitor) {
* @param expire
* minimal age of objects to be pruned.
* @return this instance
+ * @deprecated use {@link #setExpire(Instant)} instead
*/
+ @Deprecated(since = "7.2")
public GarbageCollectCommand setExpire(Date expire) {
+ if (expire != null) {
+ this.expire = expire.toInstant();
+ }
+ return this;
+ }
+
+ /**
+ * During gc() or prune() each unreferenced, loose object which has been
+ * created or modified after <code>expire</code> will not be pruned. Only
+ * older objects may be pruned. If set to null then every object is a
+ * candidate for pruning. Use {@link org.eclipse.jgit.util.GitTimeParser} to
+ * parse time formats used by git gc.
+ *
+ * @param expire
+ * minimal age of objects to be pruned.
+ * @return this instance
+ * @since 7.2
+ */
+ public GarbageCollectCommand setExpire(Instant expire) {
this.expire = expire;
return this;
}
@@ -108,8 +130,8 @@ public GarbageCollectCommand setExpire(Date expire) {
* Whether to use aggressive mode or not. If set to true JGit behaves more
* similar to native git's "git gc --aggressive". If set to
* <code>true</code> compressed objects found in old packs are not reused
- * but every object is compressed again. Configuration variables
- * pack.window and pack.depth are set to 250 for this GC.
+ * but every object is compressed again. Configuration variables pack.window
+ * and pack.depth are set to 250 for this GC.
*
* @since 3.6
* @param aggressive
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
index 3dc53ec..5bc035a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
@@ -714,6 +714,16 @@ public GarbageCollectCommand gc() {
}
/**
+ * Return a command object to execute a {@code PackRefs} command
+ *
+ * @return a {@link org.eclipse.jgit.api.PackRefsCommand}
+ * @since 7.1
+ */
+ public PackRefsCommand packRefs() {
+ return new PackRefsCommand(repo);
+ }
+
+ /**
* Return a command object to find human-readable names of revisions.
*
* @return a {@link org.eclipse.jgit.api.NameRevCommand}.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/InitCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/InitCommand.java
index 240290f..1da71aa 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/InitCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/InitCommand.java
@@ -19,6 +19,7 @@
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.file.FileRepository;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Repository;
@@ -44,6 +45,8 @@ public class InitCommand implements Callable<Git> {
private String initialBranch;
+ private boolean relativePaths;
+
/**
* {@inheritDoc}
* <p>
@@ -100,7 +103,11 @@ public Git call() throws GitAPIException {
: initialBranch);
Repository repository = builder.build();
if (!repository.getObjectDatabase().exists())
- repository.create(bare);
+ if (repository instanceof FileRepository) {
+ ((FileRepository) repository).create(bare, relativePaths);
+ } else {
+ repository.create(bare);
+ }
return new Git(repository, true);
} catch (IOException | ConfigInvalidException e) {
throw new JGitInternalException(e.getMessage(), e);
@@ -214,4 +221,18 @@ public InitCommand setInitialBranch(String branch)
this.initialBranch = branch;
return this;
}
+
+ /**
+ * * Set whether the repository shall use relative paths for GIT_DIR and
+ * GIT_WORK_TREE
+ *
+ * @param relativePaths
+ * if true, use relative paths for GIT_DIR and GIT_WORK_TREE
+ * @return {@code this}
+ * @since 7.2
+ */
+ public InitCommand setRelativeDirs(boolean relativePaths) {
+ this.relativePaths = relativePaths;
+ return this;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/PackRefsCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/PackRefsCommand.java
new file mode 100644
index 0000000..29a69c5
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/PackRefsCommand.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.eclipse.jgit.api;
+
+import java.io.IOException;
+
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ProgressMonitor;
+import org.eclipse.jgit.lib.Repository;
+
+/**
+ * Optimize storage of references.
+ *
+ * @since 7.1
+ */
+public class PackRefsCommand extends GitCommand<String> {
+ private ProgressMonitor monitor;
+
+ private boolean all;
+
+ /**
+ * Creates a new {@link PackRefsCommand} instance with default values.
+ *
+ * @param repo
+ * the repository this command will be used on
+ */
+ public PackRefsCommand(Repository repo) {
+ super(repo);
+ this.monitor = NullProgressMonitor.INSTANCE;
+ }
+
+ /**
+ * Set progress monitor
+ *
+ * @param monitor
+ * a progress monitor
+ * @return this instance
+ */
+ public PackRefsCommand setProgressMonitor(ProgressMonitor monitor) {
+ this.monitor = monitor;
+ return this;
+ }
+
+ /**
+ * Specify whether to pack all the references.
+ *
+ * @param all
+ * if <code>true</code> all the loose refs will be packed
+ * @return this instance
+ */
+ public PackRefsCommand setAll(boolean all) {
+ this.all = all;
+ return this;
+ }
+
+ /**
+ * Whether to pack all the references
+ *
+ * @return whether to pack all the references
+ */
+ public boolean isAll() {
+ return all;
+ }
+
+ @Override
+ public String call() throws GitAPIException {
+ checkCallable();
+ try {
+ repo.getRefDatabase().packRefs(monitor, this);
+ return JGitText.get().packRefsSuccessful;
+ } catch (IOException e) {
+ throw new JGitInternalException(JGitText.get().packRefsFailed, e);
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
index 83ae0fc..4b2cee4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
@@ -533,9 +533,9 @@ public static BranchRebaseMode getRebaseMode(String branchName,
Config config) {
BranchRebaseMode mode = config.getEnum(BranchRebaseMode.values(),
ConfigConstants.CONFIG_BRANCH_SECTION,
- branchName, ConfigConstants.CONFIG_KEY_REBASE, null);
+ branchName, ConfigConstants.CONFIG_KEY_REBASE);
if (mode == null) {
- mode = config.getEnum(BranchRebaseMode.values(),
+ mode = config.getEnum(
ConfigConstants.CONFIG_PULL_SECTION, null,
ConfigConstants.CONFIG_KEY_REBASE, BranchRebaseMode.NONE);
}
@@ -549,7 +549,7 @@ private FastForwardMode getFastForwardMode() {
Config config = repo.getConfig();
Merge ffMode = config.getEnum(Merge.values(),
ConfigConstants.CONFIG_PULL_SECTION, null,
- ConfigConstants.CONFIG_KEY_FF, null);
+ ConfigConstants.CONFIG_KEY_FF);
return ffMode != null ? FastForwardMode.valueOf(ffMode) : null;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
index 858bd96..3ae7a6c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
@@ -18,6 +18,8 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.MessageFormat;
+import java.time.Instant;
+import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -1835,23 +1837,26 @@ PersonIdent parseAuthor(byte[] raw) {
// the time is saved as <seconds since 1970> <timezone offset>
int timeStart = 0;
- if (time.startsWith("@")) //$NON-NLS-1$
+ if (time.startsWith("@")) { //$NON-NLS-1$
timeStart = 1;
- else
+ } else {
timeStart = 0;
- long when = Long
- .parseLong(time.substring(timeStart, time.indexOf(' '))) * 1000;
+ }
+ Instant when = Instant.ofEpochSecond(
+ Long.parseLong(time.substring(timeStart, time.indexOf(' '))));
String tzOffsetString = time.substring(time.indexOf(' ') + 1);
int multiplier = -1;
- if (tzOffsetString.charAt(0) == '+')
+ if (tzOffsetString.charAt(0) == '+') {
multiplier = 1;
+ }
int hours = Integer.parseInt(tzOffsetString.substring(1, 3));
int minutes = Integer.parseInt(tzOffsetString.substring(3, 5));
// this is in format (+/-)HHMM (hours and minutes)
- // we need to convert into minutes
- int tz = (hours * 60 + minutes) * multiplier;
- if (name != null && email != null)
+ ZoneOffset tz = ZoneOffset.ofHoursMinutes(hours * multiplier,
+ minutes * multiplier);
+ if (name != null && email != null) {
return new PersonIdent(name, email, when, tz);
+ }
return null;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ReflogCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ReflogCommand.java
index dead274..a149649 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ReflogCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ReflogCommand.java
@@ -68,7 +68,7 @@ public Collection<ReflogEntry> call() throws GitAPIException,
checkCallable();
try {
- ReflogReader reader = repo.getReflogReader(ref);
+ ReflogReader reader = repo.getRefDatabase().getReflogReader(ref);
if (reader == null)
throw new RefNotFoundException(MessageFormat.format(
JGitText.get().refNotResolved, ref));
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteRemoveCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteRemoveCommand.java
index 553fc2e..ad553f0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteRemoveCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteRemoveCommand.java
@@ -49,18 +49,6 @@ protected RemoteRemoveCommand(Repository repo) {
/**
* The name of the remote to remove.
*
- * @param name
- * a remote name
- * @deprecated use {@link #setRemoteName} instead
- */
- @Deprecated
- public void setName(String name) {
- this.remoteName = name;
- }
-
- /**
- * The name of the remote to remove.
- *
* @param remoteName
* a remote name
* @return {@code this}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteSetUrlCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteSetUrlCommand.java
index e3d0186..68ddce3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteSetUrlCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteSetUrlCommand.java
@@ -71,18 +71,6 @@ protected RemoteSetUrlCommand(Repository repo) {
/**
* The name of the remote to change the URL for.
*
- * @param name
- * a remote name
- * @deprecated use {@link #setRemoteName} instead
- */
- @Deprecated
- public void setName(String name) {
- this.remoteName = name;
- }
-
- /**
- * The name of the remote to change the URL for.
- *
* @param remoteName
* a remote remoteName
* @return {@code this}
@@ -96,18 +84,6 @@ public RemoteSetUrlCommand setRemoteName(String remoteName) {
/**
* The new URL for the remote.
*
- * @param uri
- * an URL for the remote
- * @deprecated use {@link #setRemoteUri} instead
- */
- @Deprecated
- public void setUri(URIish uri) {
- this.remoteUri = uri;
- }
-
- /**
- * The new URL for the remote.
- *
* @param remoteUri
* an URL for the remote
* @return {@code this}
@@ -121,23 +97,6 @@ public RemoteSetUrlCommand setRemoteUri(URIish remoteUri) {
/**
* Whether to change the push URL of the remote instead of the fetch URL.
*
- * @param push
- * <code>true</code> to set the push url, <code>false</code> to
- * set the fetch url
- * @deprecated use {@link #setUriType} instead
- */
- @Deprecated
- public void setPush(boolean push) {
- if (push) {
- setUriType(UriType.PUSH);
- } else {
- setUriType(UriType.FETCH);
- }
- }
-
- /**
- * Whether to change the push URL of the remote instead of the fetch URL.
- *
* @param type
* the <code>UriType</code> value to set
* @return {@code this}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java
index 855c3b1..6643c83 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com> and others
+ * Copyright (C) 2010, 2024 Christian Halstrick <christian.halstrick@sap.com> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -143,8 +143,8 @@ public RevCommit call() throws NoMessageException, UnmergedPathsException,
merger.setCommitNames(new String[] {
"BASE", ourName, revertName }); //$NON-NLS-1$
- String shortMessage = "Revert \"" + srcCommit.getShortMessage() //$NON-NLS-1$
- + "\""; //$NON-NLS-1$
+ String shortMessage = "Revert \"" //$NON-NLS-1$
+ + srcCommit.getFirstMessageLine() + '"';
String newMessage = shortMessage + "\n\n" //$NON-NLS-1$
+ "This reverts commit " + srcCommit.getId().getName() //$NON-NLS-1$
+ ".\n"; //$NON-NLS-1$
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java
index e415728..b0b715e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java
@@ -263,18 +263,6 @@ public ObjectId call() throws GitAPIException,
/**
* Whether to restore the index state
*
- * @param applyIndex
- * true (default) if the command should restore the index state
- * @deprecated use {@link #setRestoreIndex} instead
- */
- @Deprecated
- public void setApplyIndex(boolean applyIndex) {
- this.restoreIndex = applyIndex;
- }
-
- /**
- * Whether to restore the index state
- *
* @param restoreIndex
* true (default) if the command should restore the index state
* @return {@code this}
@@ -319,19 +307,6 @@ public StashApplyCommand setContentMergeStrategy(
/**
* Whether the command should restore untracked files
*
- * @param applyUntracked
- * true (default) if the command should restore untracked files
- * @since 3.4
- * @deprecated use {@link #setRestoreUntracked} instead
- */
- @Deprecated
- public void setApplyUntracked(boolean applyUntracked) {
- this.restoreUntracked = applyUntracked;
- }
-
- /**
- * Whether the command should restore untracked files
- *
* @param restoreUntracked
* true (default) if the command should restore untracked files
* @return {@code this}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashDropCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashDropCommand.java
index 23fbe01..2dba0ef 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashDropCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashDropCommand.java
@@ -165,7 +165,8 @@ public ObjectId call() throws GitAPIException {
List<ReflogEntry> entries;
try {
- ReflogReader reader = repo.getReflogReader(R_STASH);
+ ReflogReader reader = repo.getRefDatabase()
+ .getReflogReader(R_STASH);
if (reader == null) {
throw new RefNotFoundException(MessageFormat
.format(JGitText.get().refNotResolved, stashRef));
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java
index 8fb5d60..5105dfc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java
@@ -176,8 +176,9 @@ public Repository call() throws GitAPIException {
CloneCommand clone = Git.cloneRepository();
configure(clone);
clone.setDirectory(moduleDirectory);
- clone.setGitDir(new File(new File(repo.getDirectory(),
- Constants.MODULES), path));
+ clone.setGitDir(new File(
+ new File(repo.getCommonDirectory(), Constants.MODULES), path));
+ clone.setRelativePaths(true);
clone.setURI(resolvedUri);
if (monitor != null)
clone.setProgressMonitor(monitor);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java
index df73164..5e4b2ee 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java
@@ -28,6 +28,7 @@
import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
import org.eclipse.jgit.dircache.DirCacheCheckout;
import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.internal.storage.file.LockFile;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.NullProgressMonitor;
@@ -39,6 +40,7 @@
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.submodule.SubmoduleWalk;
import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
+import org.eclipse.jgit.util.FileUtils;
/**
* A class used to execute a submodule update command.
@@ -62,6 +64,8 @@ public class SubmoduleUpdateCommand extends
private boolean fetch = false;
+ private boolean clonedRestored;
+
/**
* <p>
* Constructor for SubmoduleUpdateCommand.
@@ -116,25 +120,77 @@ public SubmoduleUpdateCommand addPath(String path) {
return this;
}
+ private static boolean submoduleExists(File gitDir) {
+ if (gitDir != null && gitDir.isDirectory()) {
+ File[] files = gitDir.listFiles();
+ return files != null && files.length != 0;
+ }
+ return false;
+ }
+
+ private static void restoreSubmodule(File gitDir, File workingTree)
+ throws IOException {
+ LockFile dotGitLock = new LockFile(
+ new File(workingTree, Constants.DOT_GIT));
+ if (dotGitLock.lock()) {
+ String content = Constants.GITDIR
+ + getRelativePath(gitDir, workingTree);
+ dotGitLock.write(Constants.encode(content));
+ dotGitLock.commit();
+ }
+ }
+
+ private static String getRelativePath(File gitDir, File workingTree) {
+ File relPath;
+ try {
+ relPath = workingTree.toPath().relativize(gitDir.toPath())
+ .toFile();
+ } catch (IllegalArgumentException e) {
+ relPath = gitDir;
+ }
+ return FileUtils.pathToString(relPath);
+ }
+
+ private String determineUpdateMode(String mode) {
+ if (clonedRestored) {
+ return ConfigConstants.CONFIG_KEY_CHECKOUT;
+ }
+ return mode;
+ }
+
private Repository getOrCloneSubmodule(SubmoduleWalk generator, String url)
throws IOException, GitAPIException {
Repository repository = generator.getRepository();
+ boolean restored = false;
+ boolean cloned = false;
if (repository == null) {
- if (callback != null) {
- callback.cloningSubmodule(generator.getPath());
+ File gitDir = new File(
+ new File(repo.getCommonDirectory(), Constants.MODULES),
+ generator.getPath());
+ if (submoduleExists(gitDir)) {
+ restoreSubmodule(gitDir, generator.getDirectory());
+ restored = true;
+ clonedRestored = true;
+ repository = generator.getRepository();
+ } else {
+ if (callback != null) {
+ callback.cloningSubmodule(generator.getPath());
+ }
+ CloneCommand clone = Git.cloneRepository();
+ configure(clone);
+ clone.setURI(url);
+ clone.setDirectory(generator.getDirectory());
+ clone.setGitDir(gitDir);
+ clone.setRelativePaths(true);
+ if (monitor != null) {
+ clone.setProgressMonitor(monitor);
+ }
+ repository = clone.call().getRepository();
+ cloned = true;
+ clonedRestored = true;
}
- CloneCommand clone = Git.cloneRepository();
- configure(clone);
- clone.setURI(url);
- clone.setDirectory(generator.getDirectory());
- clone.setGitDir(
- new File(new File(repo.getDirectory(), Constants.MODULES),
- generator.getPath()));
- if (monitor != null) {
- clone.setProgressMonitor(monitor);
- }
- repository = clone.call().getRepository();
- } else if (this.fetch) {
+ }
+ if ((this.fetch || restored) && !cloned) {
if (fetchCallback != null) {
fetchCallback.fetchingSubmodule(generator.getPath());
}
@@ -171,15 +227,17 @@ public Collection<String> call() throws InvalidConfigurationException,
continue;
// Skip submodules not registered in parent repository's config
String url = generator.getConfigUrl();
- if (url == null)
+ if (url == null) {
continue;
-
+ }
+ clonedRestored = false;
try (Repository submoduleRepo = getOrCloneSubmodule(generator,
url); RevWalk walk = new RevWalk(submoduleRepo)) {
RevCommit commit = walk
.parseCommit(generator.getObjectId());
- String update = generator.getConfigUpdate();
+ String update = determineUpdateMode(
+ generator.getConfigUpdate());
if (ConfigConstants.CONFIG_KEY_MERGE.equals(update)) {
MergeCommand merge = new MergeCommand(submoduleRepo);
merge.include(commit);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/TagCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/TagCommand.java
index 3edaf5e..cc8589f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/TagCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/TagCommand.java
@@ -18,14 +18,11 @@
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.api.errors.NoHeadException;
import org.eclipse.jgit.api.errors.RefAlreadyExistsException;
-import org.eclipse.jgit.api.errors.ServiceUnavailableException;
import org.eclipse.jgit.api.errors.UnsupportedSigningFormatException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.GpgConfig;
import org.eclipse.jgit.lib.GpgConfig.GpgFormat;
-import org.eclipse.jgit.lib.GpgObjectSigner;
-import org.eclipse.jgit.lib.GpgSigner;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.PersonIdent;
@@ -33,6 +30,8 @@
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.Signer;
+import org.eclipse.jgit.lib.Signers;
import org.eclipse.jgit.lib.TagBuilder;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevWalk;
@@ -79,7 +78,7 @@ public class TagCommand extends GitCommand<Ref> {
private GpgConfig gpgConfig;
- private GpgObjectSigner gpgSigner;
+ private Signer signer;
private CredentialsProvider credentialsProvider;
@@ -133,9 +132,9 @@ public Ref call() throws GitAPIException, ConcurrentRefUpdateException,
newTag.setTagger(tagger);
newTag.setObjectId(id);
- if (gpgSigner != null) {
- gpgSigner.signObject(newTag, signingKey, tagger,
- credentialsProvider, gpgConfig);
+ if (signer != null) {
+ signer.signObject(repo, gpgConfig, newTag, tagger, signingKey,
+ credentialsProvider);
}
// write the tag object
@@ -196,15 +195,12 @@ private Ref updateTagRef(ObjectId tagId, RevWalk revWalk,
*
* @throws InvalidTagNameException
* if the tag name is null or invalid
- * @throws ServiceUnavailableException
- * if the tag should be signed but no signer can be found
* @throws UnsupportedSigningFormatException
* if the tag should be signed but {@code gpg.format} is not
* {@link GpgFormat#OPENPGP}
*/
private void processOptions()
- throws InvalidTagNameException, ServiceUnavailableException,
- UnsupportedSigningFormatException {
+ throws InvalidTagNameException, UnsupportedSigningFormatException {
if (name == null
|| !Repository.isValidRefName(Constants.R_TAGS + name)) {
throw new InvalidTagNameException(
@@ -230,16 +226,15 @@ private void processOptions()
doSign = gpgConfig.isSignAnnotated();
}
if (doSign) {
- if (signingKey == null) {
- signingKey = gpgConfig.getSigningKey();
- }
- if (gpgSigner == null) {
- GpgSigner signer = GpgSigner.getDefault();
- if (!(signer instanceof GpgObjectSigner)) {
- throw new ServiceUnavailableException(
- JGitText.get().signingServiceUnavailable);
+ if (signer == null) {
+ signer = Signers.get(gpgConfig.getKeyFormat());
+ if (signer == null) {
+ throw new UnsupportedSigningFormatException(
+ MessageFormat.format(
+ JGitText.get().signatureTypeUnknown,
+ gpgConfig.getKeyFormat()
+ .toConfigValue()));
}
- gpgSigner = (GpgObjectSigner) signer;
}
// The message of a signed tag must end in a newline because
// the signature will be appended.
@@ -326,22 +321,22 @@ public TagCommand setSigned(boolean signed) {
}
/**
- * Sets the {@link GpgSigner} to use if the commit is to be signed.
+ * Sets the {@link Signer} to use if the commit is to be signed.
*
* @param signer
* to use; if {@code null}, the default signer will be used
* @return {@code this}
- * @since 5.11
+ * @since 7.0
*/
- public TagCommand setGpgSigner(GpgObjectSigner signer) {
+ public TagCommand setSigner(Signer signer) {
checkCallable();
- this.gpgSigner = signer;
+ this.signer = signer;
return this;
}
/**
* Sets an external {@link GpgConfig} to use. Whether it will be used is at
- * the discretion of the {@link #setGpgSigner(GpgObjectSigner)}.
+ * the discretion of the {@link #setSigner(Signer)}.
*
* @param config
* to set; if {@code null}, the config will be loaded from the
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/VerificationResult.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/VerificationResult.java
index 21cddf7..f5f4b06 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/VerificationResult.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/VerificationResult.java
@@ -9,7 +9,7 @@
*/
package org.eclipse.jgit.api;
-import org.eclipse.jgit.lib.GpgSignatureVerifier;
+import org.eclipse.jgit.lib.SignatureVerifier;
import org.eclipse.jgit.revwalk.RevObject;
/**
@@ -34,8 +34,9 @@ public interface VerificationResult {
* Retrieves the signature verification result.
*
* @return the result, or {@code null} if none was computed
+ * @since 7.0
*/
- GpgSignatureVerifier.SignatureVerification getVerification();
+ SignatureVerifier.SignatureVerification getVerification();
/**
* Retrieves the git object of which the signature was verified.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/VerifySignatureCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/VerifySignatureCommand.java
index 6a2a44e..487ff04 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/VerifySignatureCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/VerifySignatureCommand.java
@@ -25,11 +25,10 @@
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.GpgConfig;
-import org.eclipse.jgit.lib.GpgSignatureVerifier;
-import org.eclipse.jgit.lib.GpgSignatureVerifier.SignatureVerification;
-import org.eclipse.jgit.lib.GpgSignatureVerifierFactory;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.SignatureVerifier.SignatureVerification;
+import org.eclipse.jgit.lib.SignatureVerifiers;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevWalk;
@@ -65,12 +64,8 @@ public enum VerifyMode {
private VerifyMode mode = VerifyMode.ANY;
- private GpgSignatureVerifier verifier;
-
private GpgConfig config;
- private boolean ownVerifier;
-
/**
* Creates a new {@link VerifySignatureCommand} for the given {@link Repository}.
*
@@ -140,22 +135,7 @@ public VerifySignatureCommand setMode(@NonNull VerifyMode mode) {
}
/**
- * Sets the {@link GpgSignatureVerifier} to use.
- *
- * @param verifier
- * the {@link GpgSignatureVerifier} to use, or {@code null} to
- * use the default verifier
- * @return {@code this}
- */
- public VerifySignatureCommand setVerifier(GpgSignatureVerifier verifier) {
- checkCallable();
- this.verifier = verifier;
- return this;
- }
-
- /**
- * Sets an external {@link GpgConfig} to use. Whether it will be used it at
- * the discretion of the {@link #setVerifier(GpgSignatureVerifier)}.
+ * Sets an external {@link GpgConfig} to use.
*
* @param config
* to set; if {@code null}, the config will be loaded from the
@@ -170,16 +150,6 @@ public VerifySignatureCommand setGpgConfig(GpgConfig config) {
}
/**
- * Retrieves the currently set {@link GpgSignatureVerifier}. Can be used
- * after a successful {@link #call()} to get the verifier that was used.
- *
- * @return the {@link GpgSignatureVerifier}
- */
- public GpgSignatureVerifier getVerifier() {
- return verifier;
- }
-
- /**
* {@link Repository#resolve(String) Resolves} all names added to the
* command to git objects and verifies their signature. Non-existing objects
* are ignored.
@@ -193,9 +163,6 @@ public GpgSignatureVerifier getVerifier() {
*
* @return a map of the given names to the corresponding
* {@link VerificationResult}, excluding ignored or skipped objects.
- * @throws ServiceUnavailableException
- * if no {@link GpgSignatureVerifier} was set and no
- * {@link GpgSignatureVerifierFactory} is available
* @throws WrongObjectTypeException
* if a name resolves to an object of a type not allowed by the
* {@link #setMode(VerifyMode)} mode
@@ -207,16 +174,6 @@ public Map<String, VerificationResult> call()
checkCallable();
setCallable(false);
Map<String, VerificationResult> result = new HashMap<>();
- if (verifier == null) {
- GpgSignatureVerifierFactory factory = GpgSignatureVerifierFactory
- .getDefault();
- if (factory == null) {
- throw new ServiceUnavailableException(
- JGitText.get().signatureVerificationUnavailable);
- }
- verifier = factory.getVerifier();
- ownVerifier = true;
- }
if (config == null) {
config = new GpgConfig(repo.getConfig());
}
@@ -239,10 +196,6 @@ public Map<String, VerificationResult> call()
} catch (IOException e) {
throw new JGitInternalException(
JGitText.get().signatureVerificationError, e);
- } finally {
- if (ownVerifier) {
- verifier.clear();
- }
}
return result;
}
@@ -258,8 +211,8 @@ private VerificationResult verifyOne(RevObject object)
}
if (type == Constants.OBJ_COMMIT || type == Constants.OBJ_TAG) {
try {
- GpgSignatureVerifier.SignatureVerification verification = verifier
- .verifySignature(object, config);
+ SignatureVerification verification = SignatureVerifiers
+ .verify(repo, config, object);
if (verification == null) {
// Not signed
return null;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attribute.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attribute.java
index fe3e22a..9c4d870 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attribute.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attribute.java
@@ -9,6 +9,8 @@
*/
package org.eclipse.jgit.attributes;
+import org.eclipse.jgit.annotations.Nullable;
+
/**
* Represents an attribute.
* <p>
@@ -139,6 +141,7 @@ public State getState() {
*
* @return the attribute value (may be <code>null</code>)
*/
+ @Nullable
public String getValue() {
return value;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameGenerator.java
index 77967df..2d499ca 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameGenerator.java
@@ -28,6 +28,8 @@
import org.eclipse.jgit.blame.Candidate.HeadCandidate;
import org.eclipse.jgit.blame.Candidate.ReverseCandidate;
import org.eclipse.jgit.blame.ReverseWalk.ReverseCommit;
+import org.eclipse.jgit.blame.cache.BlameCache;
+import org.eclipse.jgit.blame.cache.CacheRegion;
import org.eclipse.jgit.diff.DiffAlgorithm;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.diff.DiffEntry.ChangeType;
@@ -129,8 +131,19 @@ public class BlameGenerator implements AutoCloseable {
/** Blame is currently assigned to this source. */
private Candidate outCandidate;
+
private Region outRegion;
+ private final BlameCache blameCache;
+
+ /**
+ * Blame in reverse order needs the source lines, but we don't have them in
+ * the cache. We need to ignore the cache in that case.
+ */
+ private boolean useCache = true;
+
+ private final Stats stats = new Stats();
+
/**
* Create a blame generator for the repository and path (relative to
* repository)
@@ -142,6 +155,25 @@ public class BlameGenerator implements AutoCloseable {
* repository).
*/
public BlameGenerator(Repository repository, String path) {
+ this(repository, path, null);
+ }
+
+ /**
+ * Create a blame generator for the repository and path (relative to
+ * repository)
+ *
+ * @param repository
+ * repository to access revision data from.
+ * @param path
+ * initial path of the file to start scanning (relative to the
+ * repository).
+ * @param blameCache
+ * previously calculated blames. This generator will *not*
+ * populate it, just consume it.
+ * @since 7.2
+ */
+ public BlameGenerator(Repository repository, String path,
+ @Nullable BlameCache blameCache) {
this.repository = repository;
this.resultPath = PathFilter.create(path);
@@ -150,6 +182,7 @@ public BlameGenerator(Repository repository, String path) {
initRevPool(false);
remaining = -1;
+ this.blameCache = blameCache;
}
private void initRevPool(boolean reverse) {
@@ -159,10 +192,12 @@ private void initRevPool(boolean reverse) {
if (revPool != null)
revPool.close();
- if (reverse)
+ if (reverse) {
+ useCache = false;
revPool = new ReverseWalk(getRepository());
- else
+ } else {
revPool = new RevWalk(getRepository());
+ }
SEEN = revPool.newFlag("SEEN"); //$NON-NLS-1$
reader = revPool.getObjectReader();
@@ -245,6 +280,31 @@ public RenameDetector getRenameDetector() {
}
/**
+ * Stats about this generator
+ *
+ * @return the stats of this generator
+ * @since 7.2
+ */
+ public Stats getStats() {
+ return stats;
+ }
+
+ /**
+ * Enable/disable the use of cache (if present). Enabled by default.
+ * <p>
+ * If caller need source line numbers, the generator cannot use the cache
+ * (source lines are not there). Use this method to disable the cache in
+ * that case.
+ *
+ * @param useCache
+ * should this generator use the cache.
+ * @since 7.2
+ */
+ public void setUseCache(boolean useCache) {
+ this.useCache = useCache;
+ }
+
+ /**
* Push a candidate blob onto the generator's traversal stack.
* <p>
* Candidates should be pushed in history order from oldest-to-newest.
@@ -591,6 +651,20 @@ public boolean next() throws IOException {
Candidate n = pop();
if (n == null)
return done();
+ stats.candidatesVisited += 1;
+ if (blameCache != null && useCache) {
+ List<CacheRegion> cachedBlame = blameCache.get(repository,
+ n.sourceCommit, n.sourcePath.getPath());
+ if (cachedBlame != null) {
+ BlameRegionMerger rb = new BlameRegionMerger(repository,
+ revPool, cachedBlame);
+ Candidate fullyBlamed = rb.mergeCandidate(n);
+ if (fullyBlamed != null) {
+ stats.cacheHit = true;
+ return result(fullyBlamed);
+ }
+ }
+ }
int pCnt = n.getParentCount();
if (pCnt == 1) {
@@ -605,7 +679,7 @@ public boolean next() throws IOException {
// Do not generate a tip of a reverse. The region
// survives and should not appear to be deleted.
- } else /* if (pCnt == 0) */{
+ } else /* if (pCnt == 0) */ {
// Root commit, with at least one surviving region.
// Assign the remaining blame here.
return result(n);
@@ -846,8 +920,8 @@ private boolean processMerge(Candidate n) throws IOException {
editList = new EditList(0);
} else {
p.loadText(reader);
- editList = diffAlgorithm.diff(textComparator,
- p.sourceText, n.sourceText);
+ editList = diffAlgorithm.diff(textComparator, p.sourceText,
+ n.sourceText);
}
if (editList.isEmpty()) {
@@ -981,6 +1055,10 @@ public int getRenameScore() {
/**
* Get first line of the source data that has been blamed for the current
* region
+ * <p>
+ * This value is not reliable when the generator is reusing cached values.
+ * Cache doesn't keep the source lines, the returned value is based on the
+ * result and can be off if the region moved in previous commits.
*
* @return first line of the source data that has been blamed for the
* current region. This is line number of where the region was added
@@ -994,6 +1072,10 @@ public int getSourceStart() {
/**
* Get one past the range of the source data that has been blamed for the
* current region
+ * <p>
+ * This value is not reliable when the generator is reusing cached values.
+ * Cache doesn't keep the source lines, the returned value is based on the
+ * result and can be off if the region moved in previous commits.
*
* @return one past the range of the source data that has been blamed for
* the current region. This is line number of where the region was
@@ -1124,4 +1206,39 @@ private static boolean isRename(DiffEntry ent) {
return ent.getChangeType() == ChangeType.RENAME
|| ent.getChangeType() == ChangeType.COPY;
}
+
+ /**
+ * Stats about the work done by the generator
+ *
+ * @since 7.2
+ */
+ public static class Stats {
+
+ /** Candidates taken from the queue */
+ private int candidatesVisited;
+
+ private boolean cacheHit;
+
+ /**
+ * Number of candidates taken from the queue
+ * <p>
+ * The generator could signal it's done without exhausting all
+ * candidates if there is no more remaining lines or the last visited
+ * candidate is found in the cache.
+ *
+ * @return number of candidates taken from the queue
+ */
+ public int getCandidatesVisited() {
+ return candidatesVisited;
+ }
+
+ /**
+ * The generator found a blamed version in the cache
+ *
+ * @return true if we used results from the cache
+ */
+ public boolean isCacheHit() {
+ return cacheHit;
+ }
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameRegionMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameRegionMerger.java
new file mode 100644
index 0000000..67bc6fb
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameRegionMerger.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2025, Google LLC.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.blame;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.jgit.blame.cache.CacheRegion;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.treewalk.filter.PathFilter;
+
+/**
+ * Translates an unblamed region into one or more blamed regions, using the
+ * fully blamed data from cache.
+ * <p>
+ * Blamed and unblamed regions are not symmetrical: An unblamed region is just a
+ * range of lines over the file. A blamed region is a Candidate (with the commit
+ * info) with a region inside (the range blamed).
+ */
+class BlameRegionMerger {
+ private final Repository repo;
+
+ private final List<CacheRegion> cachedRegions;
+
+ private final RevWalk rw;
+
+ BlameRegionMerger(Repository repo, RevWalk rw,
+ List<CacheRegion> cachedRegions) {
+ this.repo = repo;
+ List<CacheRegion> sorted = new ArrayList<>(cachedRegions);
+ Collections.sort(sorted);
+ this.cachedRegions = sorted;
+ this.rw = rw;
+ }
+
+ /**
+ * Return one or more candidates blaming all the regions of the "unblamed"
+ * incoming candidate.
+ *
+ * @param candidate
+ * a candidate with a list of unblamed regions
+ * @return A linked list of Candidates with their blamed regions, null if
+ * there was any error.
+ */
+ Candidate mergeCandidate(Candidate candidate) {
+ List<Candidate> newCandidates = new ArrayList<>();
+ Region r = candidate.regionList;
+ while (r != null) {
+ try {
+ newCandidates.addAll(mergeOneRegion(r));
+ } catch (IOException e) {
+ return null;
+ }
+ r = r.next;
+ }
+ return asLinkedCandidate(newCandidates);
+ }
+
+ // Visible for testing
+ List<Candidate> mergeOneRegion(Region region) throws IOException {
+ List<CacheRegion> overlaps = findOverlaps(region);
+ if (overlaps.isEmpty()) {
+ throw new IOException(
+ "Cached blame should cover all lines");
+ }
+ /*
+ * Cached regions cover the whole file. We find first which ones overlap
+ * with our unblamed region. Then we take the overlapping portions with
+ * the corresponding blame.
+ */
+ List<Candidate> candidates = new ArrayList<>();
+ for (CacheRegion overlap : overlaps) {
+ Region blamedRegions = intersectRegions(region, overlap);
+ Candidate c = new Candidate(repo, parse(overlap.getSourceCommit()),
+ PathFilter.create(overlap.getSourcePath()));
+ c.regionList = blamedRegions;
+ candidates.add(c);
+ }
+ return candidates;
+ }
+
+ // Visible for testing
+ List<CacheRegion> findOverlaps(Region unblamed) {
+ int unblamedStart = unblamed.sourceStart;
+ int unblamedEnd = unblamedStart + unblamed.length;
+ List<CacheRegion> overlapping = new ArrayList<>();
+ for (CacheRegion blamed : cachedRegions) {
+ // End is not included
+ if (blamed.getEnd() <= unblamedStart) {
+ // Blamed region is completely before
+ continue;
+ }
+
+ if (blamed.getStart() >= unblamedEnd) {
+ // Blamed region is completely after
+ // Blamed regions are sorted by start position, nothing will
+ // match anymore
+ break;
+ }
+ overlapping.add(blamed);
+ }
+ return overlapping;
+ }
+
+ // Visible for testing
+ /**
+ * Calculate the intersection between a Region and a CacheRegion, adjusting
+ * the start if needed.
+ * <p>
+ * This should be called only if there is an overlap (filtering the cached
+ * regions with {@link #findOverlaps(Region)}), otherwise the result is
+ * meaningless.
+ *
+ * @param unblamed
+ * a region from the blame generator
+ * @param cached
+ * a cached region
+ * @return a new region with the intersection.
+ */
+ static Region intersectRegions(Region unblamed, CacheRegion cached) {
+ int blamedStart = Math.max(cached.getStart(), unblamed.sourceStart);
+ int blamedEnd = Math.min(cached.getEnd(),
+ unblamed.sourceStart + unblamed.length);
+ int length = blamedEnd - blamedStart;
+
+ // result start and source start should move together
+ int blameStartDelta = blamedStart - unblamed.sourceStart;
+ return new Region(unblamed.resultStart + blameStartDelta, blamedStart,
+ length);
+ }
+
+ // Tests can override this, so they don't need a real repo, commit and walk
+ protected RevCommit parse(ObjectId oid) throws IOException {
+ return rw.parseCommit(oid);
+ }
+
+ private static Candidate asLinkedCandidate(List<Candidate> c) {
+ Candidate head = c.get(0);
+ Candidate tail = head;
+ for (int i = 1; i < c.size(); i++) {
+ tail.queueNext = c.get(i);
+ tail = tail.queueNext;
+ }
+ return head;
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameResult.java b/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameResult.java
index 5e2746c..48f6b7e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameResult.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameResult.java
@@ -79,6 +79,7 @@ public static BlameResult create(BlameGenerator gen) throws IOException {
BlameResult(BlameGenerator bg, String path, RawText text) {
generator = bg;
+ generator.setUseCache(false);
resultPath = path;
resultContents = text;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/blame/cache/BlameCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/blame/cache/BlameCache.java
new file mode 100644
index 0000000..d44fb5f
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/blame/cache/BlameCache.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2025, Google LLC.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.blame.cache;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+
+/**
+ * Keeps the blame information for a path at certain commit.
+ * <p>
+ * If there is a result, it covers the whole file at that revision
+ *
+ * @since 7.2
+ */
+public interface BlameCache {
+ /**
+ * Gets the blame of a path at a given commit if available.
+ * <p>
+ * Since this cache is used in blame calculation, this get() method should
+ * only retrieve the cache value, and not re-trigger blame calculation. In
+ * other words, this acts as "getIfPresent", and not "computeIfAbsent".
+ *
+ * @param repo
+ * repository containing the commit
+ * @param commitId
+ * we are looking at the file in this revision
+ * @param path
+ * path a file in the repo
+ *
+ * @return the blame of a path at a given commit or null if not in cache
+ * @throws IOException
+ * error retrieving/parsing values from storage
+ */
+ List<CacheRegion> get(Repository repo, ObjectId commitId, String path)
+ throws IOException;
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/blame/cache/CacheRegion.java b/org.eclipse.jgit/src/org/eclipse/jgit/blame/cache/CacheRegion.java
new file mode 100644
index 0000000..cf3f978
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/blame/cache/CacheRegion.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2025, Google LLC.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.blame.cache;
+
+import java.text.MessageFormat;
+
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.ObjectId;
+
+/**
+ * Region of the blame of a file.
+ * <p>
+ * Usually all parameters are non-null, except when the Region was created
+ * to fill an unblamed gap (to cover for bugs in the calculation). In that
+ * case, path, commit and author will be null.
+ *
+ * @since 7.2
+ **/
+public class CacheRegion implements Comparable<CacheRegion> {
+ private final String sourcePath;
+
+ private final ObjectId sourceCommit;
+
+ private final int end;
+
+ private final int start;
+
+ /**
+ * A blamed portion of a file
+ *
+ * @param path
+ * location of the file
+ * @param commit
+ * commit that is modifying this region
+ * @param start
+ * first line of this region (inclusive)
+ * @param end
+ * last line of this region (non-inclusive!)
+ */
+ public CacheRegion(String path, ObjectId commit,
+ int start, int end) {
+ allOrNoneNull(path, commit);
+ this.sourcePath = path;
+ this.sourceCommit = commit;
+ this.start = start;
+ this.end = end;
+ }
+
+ /**
+ * First line of this region. Starting by 0, inclusive
+ *
+ * @return first line of this region.
+ */
+ public int getStart() {
+ return start;
+ }
+
+ /**
+ * One after last line in this region (or: last line non-inclusive)
+ *
+ * @return one after last line in this region.
+ */
+ public int getEnd() {
+ return end;
+ }
+
+
+ /**
+ * Path of the file this region belongs to
+ *
+ * @return path in the repo/commit
+ */
+ public String getSourcePath() {
+ return sourcePath;
+ }
+
+ /**
+ * Commit this region belongs to
+ *
+ * @return commit for this region
+ */
+ public ObjectId getSourceCommit() {
+ return sourceCommit;
+ }
+
+ @Override
+ public int compareTo(CacheRegion o) {
+ return start - o.start;
+ }
+
+ @SuppressWarnings("nls")
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ if (sourceCommit != null) {
+ sb.append(sourceCommit.name(), 0, 7).append(' ')
+ .append(" (")
+ .append(sourcePath).append(')');
+ } else {
+ sb.append("<unblamed region>");
+ }
+ sb.append(' ').append("start=").append(start).append(", count=")
+ .append(end - start);
+ return sb.toString();
+ }
+
+ private static void allOrNoneNull(String path, ObjectId commit) {
+ if (path != null && commit != null) {
+ return;
+ }
+
+ if (path == null && commit == null) {
+ return;
+ }
+ throw new IllegalArgumentException(MessageFormat
+ .format(JGitText.get().cacheRegionAllOrNoneNull, path, commit));
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffDriver.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffDriver.java
new file mode 100644
index 0000000..b744444
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffDriver.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.eclipse.jgit.diff;
+
+import java.util.List;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+/**
+ * Built-in drivers for various languages, sorted by name. These drivers will be
+ * used to determine function names for a hunk.
+ * <p>
+ * When writing or updating patterns, assume the contents are syntactically
+ * correct. Patterns can be simple and need not cover all syntactical corner
+ * cases, as long as they are sufficiently permissive.
+ *
+ * @since 6.10.1
+ */
+@SuppressWarnings({"ImmutableEnumChecker", "nls"})
+public enum DiffDriver {
+ /**
+ * Built-in diff driver for <a href=
+ * "https://learn.microsoft.com/en-us/cpp/cpp/cpp-language-reference">c++</a>
+ */
+ cpp(List.of(
+ /* Jump targets or access declarations */
+ "^[ \\t]*[A-Za-z_][A-Za-z_0-9]*:\\s*($|/[/*])"), List.of(
+ /* functions/methods, variables, and compounds at top level */
+ "^((::\\s*)?[A-Za-z_].*)$")),
+ /**
+ * Built-in diff driver for <a href=
+ * "https://devicetree-specification.readthedocs.io/en/stable/source-language.html">device
+ * tree files</a>
+ */
+ dts(List.of(";", "="), List.of(
+ /* lines beginning with a word optionally preceded by '&' or the root */
+ "^[ \\t]*((/[ \\t]*\\{|&?[a-zA-Z_]).*)")),
+ /**
+ * Built-in diff driver for <a href=
+ * "https://docs.oracle.com/javase/specs/jls/se21/html/index.html">java</a>
+ */
+ java(List.of(
+ "^[ \\t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)"),
+ List.of(
+ /* Class, enum, interface, and record declarations */
+ "^[ \\t]*(([a-z-]+[ \\t]+)*(class|enum|interface|record)[ \\t]+.*)$",
+ /* Method definitions; note that constructor signatures are not */
+ /* matched because they are indistinguishable from method calls. */
+ "^[ \\t]*(([A-Za-z_<>&\\]\\[][?&<>.,A-Za-z_0-9]*[ \\t]+)+[A-Za-z_]"
+ + "[A-Za-z_0-9]*[ \\t]*\\([^;]*)$")),
+ /**
+ * Built-in diff driver for
+ * <a href="https://docs.python.org/3/reference/index.html">python</a>
+ */
+ python(List.of("^[ \\t]*((class|(async[ \\t]+)?def)[ \\t].*)$")),
+ /**
+ * Built-in diff driver for
+ * <a href="https://doc.rust-lang.org/reference/introduction.html">rust</a>
+ */
+ rust(List.of("^[\\t ]*((pub(\\([^\\)]+\\))?[\\t ]+)?"
+ + "((async|const|unsafe|extern([\\t ]+\"[^\"]+\"))[\\t ]+)?"
+ + "(struct|enum|union|mod|trait|fn|impl|macro_rules!)[< \\t]+[^;]*)$"));
+
+ private final List<Pattern> negatePatterns;
+
+ private final List<Pattern> matchPatterns;
+
+ DiffDriver(List<String> negate, List<String> match, int flags) {
+ if (negate != null) {
+ this.negatePatterns = negate.stream()
+ .map(r -> Pattern.compile(r, flags))
+ .collect(Collectors.toList());
+ } else {
+ this.negatePatterns = null;
+ }
+ this.matchPatterns = match.stream().map(r -> Pattern.compile(r, flags))
+ .collect(Collectors.toList());
+ }
+
+ DiffDriver(List<String> match) {
+ this(null, match, 0);
+ }
+
+ DiffDriver(List<String> negate, List<String> match) {
+ this(negate, match, 0);
+ }
+
+ /**
+ * Returns the list of patterns used to exclude certain lines from being
+ * considered as function names.
+ *
+ * @return the list of negate patterns
+ */
+ public List<Pattern> getNegatePatterns() {
+ return negatePatterns;
+ }
+
+ /**
+ * Returns the list of patterns used to match lines for potential function
+ * names.
+ *
+ * @return the list of match patterns
+ */
+ public List<Pattern> getMatchPatterns() {
+ return matchPatterns;
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java
index 2f472b5..cbac3f9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java
@@ -30,7 +30,9 @@
import java.util.Collections;
import java.util.List;
+import java.util.regex.Pattern;
import org.eclipse.jgit.api.errors.CanceledException;
+import org.eclipse.jgit.attributes.Attribute;
import org.eclipse.jgit.diff.DiffAlgorithm.SupportedAlgorithm;
import org.eclipse.jgit.diff.DiffEntry.ChangeType;
import org.eclipse.jgit.dircache.DirCacheIterator;
@@ -703,7 +705,7 @@ public void format(List<? extends DiffEntry> entries) throws IOException {
*/
public void format(DiffEntry ent) throws IOException {
FormatResult res = createFormatResult(ent);
- format(res.header, res.a, res.b);
+ format(res.header, res.a, res.b, getDiffDriver(ent));
}
private static byte[] writeGitLinkText(AbbreviatedObjectId id) {
@@ -749,11 +751,14 @@ private String quotePath(String path) {
* text source for the post-image version of the content. This
* must match the content of
* {@link org.eclipse.jgit.patch.FileHeader#getNewId()}.
+ * @param diffDriver
+ * the diff driver used to obtain function names in hunk headers
* @throws java.io.IOException
- * writing to the supplied stream failed.
+ * writing to the supplied stream failed.
+ * @since 6.10.1
*/
- public void format(FileHeader head, RawText a, RawText b)
- throws IOException {
+ public void format(FileHeader head, RawText a, RawText b,
+ DiffDriver diffDriver) throws IOException {
// Reuse the existing FileHeader as-is by blindly copying its
// header lines, but avoiding its hunks. Instead we recreate
// the hunks from the text instances we have been supplied.
@@ -763,8 +768,49 @@ public void format(FileHeader head, RawText a, RawText b)
if (!head.getHunks().isEmpty())
end = head.getHunks().get(0).getStartOffset();
out.write(head.getBuffer(), start, end - start);
- if (head.getPatchType() == PatchType.UNIFIED)
- format(head.toEditList(), a, b);
+ if (head.getPatchType() == PatchType.UNIFIED) {
+ format(head.toEditList(), a, b, diffDriver);
+ }
+ }
+
+ /**
+ * Format a patch script, reusing a previously parsed FileHeader.
+ * <p>
+ * This formatter is primarily useful for editing an existing patch script
+ * to increase or reduce the number of lines of context within the script.
+ * All header lines are reused as-is from the supplied FileHeader.
+ *
+ * @param head
+ * existing file header containing the header lines to copy.
+ * @param a
+ * text source for the pre-image version of the content. This must match
+ * the content of {@link org.eclipse.jgit.patch.FileHeader#getOldId()}.
+ * @param b
+ * text source for the post-image version of the content. This must match
+ * the content of {@link org.eclipse.jgit.patch.FileHeader#getNewId()}.
+ * @throws java.io.IOException
+ * writing to the supplied stream failed.
+ */
+ public void format(FileHeader head, RawText a, RawText b)
+ throws IOException {
+ format(head, a, b, null);
+ }
+
+ /**
+ * Formats a list of edits in unified diff format
+ *
+ * @param edits
+ * some differences which have been calculated between A and B
+ * @param a
+ * the text A which was compared
+ * @param b
+ * the text B which was compared
+ * @throws java.io.IOException
+ * if an IO error occurred
+ */
+ public void format(EditList edits, RawText a, RawText b)
+ throws IOException {
+ format(edits, a, b, null);
}
/**
@@ -776,11 +822,14 @@ public void format(FileHeader head, RawText a, RawText b)
* the text A which was compared
* @param b
* the text B which was compared
+ * @param diffDriver
+ * the diff driver used to obtain function names in hunk headers
* @throws java.io.IOException
* if an IO error occurred
+ * @since 6.10.1
*/
- public void format(EditList edits, RawText a, RawText b)
- throws IOException {
+ public void format(EditList edits, RawText a, RawText b,
+ DiffDriver diffDriver) throws IOException {
for (int curIdx = 0; curIdx < edits.size();) {
Edit curEdit = edits.get(curIdx);
final int endIdx = findCombinedEnd(edits, curIdx);
@@ -791,7 +840,8 @@ public void format(EditList edits, RawText a, RawText b)
final int aEnd = (int) Math.min(a.size(), (long) endEdit.getEndA() + context);
final int bEnd = (int) Math.min(b.size(), (long) endEdit.getEndB() + context);
- writeHunkHeader(aCur, aEnd, bCur, bEnd);
+ writeHunkHeader(aCur, aEnd, bCur, bEnd,
+ getFuncName(a, aCur - 1, diffDriver));
while (aCur < aEnd || bCur < bEnd) {
if (aCur < curEdit.getBeginA() || endIdx + 1 < curIdx) {
@@ -881,8 +931,30 @@ protected void writeRemovedLine(RawText text, int line)
* @throws java.io.IOException
* if an IO error occurred
*/
- protected void writeHunkHeader(int aStartLine, int aEndLine,
- int bStartLine, int bEndLine) throws IOException {
+ protected void writeHunkHeader(int aStartLine, int aEndLine, int bStartLine,
+ int bEndLine) throws IOException {
+ writeHunkHeader(aStartLine, aEndLine, bStartLine, bEndLine, null);
+ }
+
+ /**
+ * Output a hunk header
+ *
+ * @param aStartLine
+ * within first source
+ * @param aEndLine
+ * within first source
+ * @param bStartLine
+ * within second source
+ * @param bEndLine
+ * within second source
+ * @param funcName
+ * function name of this hunk
+ * @throws java.io.IOException
+ * if an IO error occurred
+ * @since 6.10.1
+ */
+ protected void writeHunkHeader(int aStartLine, int aEndLine, int bStartLine,
+ int bEndLine, String funcName) throws IOException {
out.write('@');
out.write('@');
writeRange('-', aStartLine + 1, aEndLine - aStartLine);
@@ -890,6 +962,10 @@ protected void writeHunkHeader(int aStartLine, int aEndLine,
out.write(' ');
out.write('@');
out.write('@');
+ if (funcName != null) {
+ out.write(' ');
+ out.write(funcName.getBytes());
+ }
out.write('\n');
}
@@ -1247,4 +1323,50 @@ private boolean combineB(List<Edit> e, int i) {
private static boolean end(Edit edit, int a, int b) {
return edit.getEndA() <= a && edit.getEndB() <= b;
}
+
+ private String getFuncName(RawText text, int startAt,
+ DiffDriver diffDriver) {
+ if (diffDriver != null) {
+ while (startAt > 0) {
+ String line = text.getString(startAt);
+ startAt--;
+ if (matchesAny(diffDriver.getNegatePatterns(), line)) {
+ continue;
+ }
+ if (matchesAny(diffDriver.getMatchPatterns(), line)) {
+ String funcName = line.replaceAll("^[ \\t]+", ""); //$NON-NLS-1$//$NON-NLS-2$
+ return funcName.substring(0,
+ Math.min(funcName.length(), 80)).trim();
+ }
+ }
+ }
+ return null;
+ }
+
+ private boolean matchesAny(List<Pattern> patterns, String text) {
+ if (patterns != null) {
+ for (Pattern p : patterns) {
+ if (p.matcher(text).find()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private DiffDriver getDiffDriver(DiffEntry entry) {
+ Attribute diffAttr = entry.getDiffAttribute();
+ if (diffAttr == null) {
+ return null;
+ }
+ String diffAttrValue = diffAttr.getValue();
+ if (diffAttrValue == null) {
+ return null;
+ }
+ try {
+ return DiffDriver.valueOf(diffAttrValue);
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/PatchIdDiffFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/PatchIdDiffFormatter.java
index 4343642..b401bbe 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/PatchIdDiffFormatter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/PatchIdDiffFormatter.java
@@ -44,8 +44,8 @@ public ObjectId getCalulatedPatchId() {
}
@Override
- protected void writeHunkHeader(int aStartLine, int aEndLine,
- int bStartLine, int bEndLine) throws IOException {
+ protected void writeHunkHeader(int aStartLine, int aEndLine, int bStartLine,
+ int bEndLine, String funcName) throws IOException {
// The hunk header is not taken into account for patch id calculation
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/RawText.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/RawText.java
index 76dc87e..fdfe533 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/RawText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/RawText.java
@@ -360,18 +360,22 @@ public static boolean isBinary(byte[] raw, int length, boolean complete) {
length = maxLength;
isComplete = false;
}
- byte last = 'x'; // Just something inconspicuous.
- for (int ptr = 0; ptr < length; ptr++) {
- byte curr = raw[ptr];
- if (isBinary(curr, last)) {
+
+ int ptr = -1;
+ byte current;
+ while (ptr < length - 2) {
+ current = raw[++ptr];
+ if (current == '\0' || (current == '\r' && raw[++ptr] != '\n')) {
return true;
}
- last = curr;
}
- if (isComplete) {
- // Buffer contains everything...
- return last == '\r'; // ... so this must be a lone CR
+
+ if (ptr == length - 2) {
+ // if '\r' be last, then if isComplete then return binary
+ current = raw[++ptr];
+ return current == '\0' || (current == '\r' && isComplete);
}
+
return false;
}
@@ -467,26 +471,30 @@ public static boolean isCrLfText(byte[] raw, int length) {
*/
public static boolean isCrLfText(byte[] raw, int length, boolean complete) {
boolean has_crlf = false;
- byte last = 'x'; // Just something inconspicuous
- for (int ptr = 0; ptr < length; ptr++) {
- byte curr = raw[ptr];
- if (isBinary(curr, last)) {
+
+ int ptr = -1;
+ byte current;
+ while (ptr < length - 2) {
+ current = raw[++ptr];
+ if (current == '\0') {
return false;
}
- if (curr == '\n' && last == '\r') {
+ if (current == '\r') {
+ if (raw[++ptr] != '\n') {
+ return false;
+ }
has_crlf = true;
}
- last = curr;
}
- if (last == '\r') {
- if (complete) {
- // Lone CR: it's binary after all.
+
+ if (ptr == length - 2) {
+ // if '\r' be last, then if isComplete then return binary
+ current = raw[++ptr];
+ if (current == '\0' || (current == '\r' && complete)) {
return false;
}
- // Tough call. If the next byte, which we don't have, would be a
- // '\n', it'd be a CR-LF text, otherwise it'd be binary. Just decide
- // based on what we already scanned; it wasn't binary until now.
}
+
return has_crlf;
}
@@ -578,4 +586,5 @@ public static RawText load(ObjectLoader ldr, int threshold)
return new RawText(data, RawParseUtils.lineMapOrBinary(data, 0, (int) sz));
}
}
+
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityRenameDetector.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityRenameDetector.java
index 5de7bac..fb98df7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityRenameDetector.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityRenameDetector.java
@@ -80,7 +80,7 @@ class SimilarityRenameDetector {
private long[] matrix;
/** Score a pair must exceed to be considered a rename. */
- private int renameScore = 60;
+ private int renameScore = 50;
/**
* File size threshold (in bytes) for detecting renames. Files larger
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/Checkout.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/Checkout.java
index accf732..de02aec 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/Checkout.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/Checkout.java
@@ -217,10 +217,18 @@ public void checkout(DirCacheEntry entry, CheckoutMetadata metadata,
}
}
try {
- if (recursiveDelete && Files.isDirectory(f.toPath(),
- LinkOption.NOFOLLOW_LINKS)) {
+ boolean isDir = Files.isDirectory(f.toPath(),
+ LinkOption.NOFOLLOW_LINKS);
+ if (recursiveDelete && isDir) {
FileUtils.delete(f, FileUtils.RECURSIVE);
}
+ if (cache.getRepository().isWorkTreeCaseInsensitive() && !isDir) {
+ // We cannot rely on rename via Files.move() to work correctly
+ // if the target exists in a case variant. For instance with JDK
+ // 17 on Mac OS, the existing case-variant name is kept. On
+ // Windows 11 it would work and use the name given in 'f'.
+ FileUtils.delete(f, FileUtils.SKIP_MISSING);
+ }
FileUtils.rename(tmpFile, f, StandardCopyOption.ATOMIC_MOVE);
cachedParent.remove(f.getName());
} catch (IOException e) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java
index 34dba0b..c650d6e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java
@@ -1037,7 +1037,12 @@ private void updateSmudgedEntries() throws IOException {
}
}
- enum DirCacheVersion implements ConfigEnum {
+ /**
+ * DirCache versions
+ *
+ * @since 7.2
+ */
+ public enum DirCacheVersion implements ConfigEnum {
/** Minimum index version on-disk format that we support. */
DIRC_VERSION_MINIMUM(2),
@@ -1060,6 +1065,9 @@ private DirCacheVersion(int versionCode) {
this.version = versionCode;
}
+ /**
+ * @return the version code for this version
+ */
public int getVersionCode() {
return version;
}
@@ -1078,6 +1086,13 @@ public boolean matchConfigValue(String in) {
}
}
+ /**
+ * Create DirCacheVersion from integer value of the version code.
+ *
+ * @param val
+ * integer value of the version code.
+ * @return the DirCacheVersion instance of the version code.
+ */
public static DirCacheVersion fromInt(int val) {
for (DirCacheVersion v : DirCacheVersion.values()) {
if (val == v.getVersionCode()) {
@@ -1098,9 +1113,8 @@ public DirCacheConfig(Config cfg) {
boolean manyFiles = cfg.getBoolean(
ConfigConstants.CONFIG_FEATURE_SECTION,
ConfigConstants.CONFIG_KEY_MANYFILES, false);
- indexVersion = cfg.getEnum(DirCacheVersion.values(),
- ConfigConstants.CONFIG_INDEX_SECTION, null,
- ConfigConstants.CONFIG_KEY_VERSION,
+ indexVersion = cfg.getEnum(ConfigConstants.CONFIG_INDEX_SECTION,
+ null, ConfigConstants.CONFIG_KEY_VERSION,
manyFiles ? DirCacheVersion.DIRC_VERSION_PATHCOMPRESS
: DirCacheVersion.DIRC_VERSION_EXTENDED);
skipHash = cfg.getBoolean(ConfigConstants.CONFIG_INDEX_SECTION,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
index 6ae5153..18d7748 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
@@ -5,7 +5,7 @@
* Copyright (C) 2006, Shawn O. Pearce <spearce@spearce.org>
* Copyright (C) 2010, Chrisian Halstrick <christian.halstrick@sap.com>
* Copyright (C) 2019, 2020, Andre Bossert <andre.bossert@siemens.com>
- * Copyright (C) 2017, 2023, Thomas Wolf <twolf@apache.org> and others
+ * Copyright (C) 2017, 2025, Thomas Wolf <twolf@apache.org> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -31,6 +31,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.TreeSet;
import org.eclipse.jgit.api.errors.CanceledException;
import org.eclipse.jgit.api.errors.FilterFailedException;
@@ -66,7 +67,6 @@
import org.eclipse.jgit.treewalk.filter.PathFilter;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.FS.ExecutionResult;
-import org.eclipse.jgit.util.IntList;
import org.eclipse.jgit.util.SystemReader;
import org.eclipse.jgit.util.io.EolStreamTypeUtil;
import org.slf4j.Logger;
@@ -113,9 +113,11 @@ public CheckoutMetadata(EolStreamType eolStreamType,
private Map<String, CheckoutMetadata> updated = new LinkedHashMap<>();
+ private Set<String> existing;
+
private ArrayList<String> conflicts = new ArrayList<>();
- private ArrayList<String> removed = new ArrayList<>();
+ private TreeSet<String> removed;
private ArrayList<String> kept = new ArrayList<>();
@@ -185,7 +187,7 @@ public List<String> getToBeDeleted() {
* @return a list of all files removed by this checkout
*/
public List<String> getRemoved() {
- return removed;
+ return new ArrayList<>(removed);
}
/**
@@ -214,6 +216,14 @@ public DirCacheCheckout(Repository repo, ObjectId headCommitTree, DirCache dc,
this.mergeCommitTree = mergeCommitTree;
this.workingTree = workingTree;
this.initialCheckout = !repo.isBare() && !repo.getIndexFile().exists();
+ boolean caseInsensitive = !repo.isBare()
+ && repo.isWorkTreeCaseInsensitive();
+ this.removed = caseInsensitive
+ ? new TreeSet<>(String::compareToIgnoreCase)
+ : new TreeSet<>();
+ this.existing = caseInsensitive
+ ? new TreeSet<>(String::compareToIgnoreCase)
+ : null;
}
/**
@@ -400,9 +410,11 @@ void processEntry(CanonicalTreeParser m, DirCacheBuildIterator i,
// content to be checked out.
update(m);
}
- } else
+ } else {
update(m);
- } else if (f == null || !m.idEqual(i)) {
+ }
+ } else if (f == null || !m.idEqual(i)
+ || m.getEntryRawMode() != i.getEntryRawMode()) {
// The working tree file is missing or the merge content differs
// from index content
update(m);
@@ -410,11 +422,11 @@ void processEntry(CanonicalTreeParser m, DirCacheBuildIterator i,
// The index contains a file (and not a folder)
if (f.isModified(i.getDirCacheEntry(), true,
this.walk.getObjectReader())
- || i.getDirCacheEntry().getStage() != 0)
+ || i.getDirCacheEntry().getStage() != 0) {
// The working tree file is dirty or the index contains a
// conflict
update(m);
- else {
+ } else {
// update the timestamp of the index with the one from the
// file if not set, as we are sure to be in sync here.
DirCacheEntry entry = i.getDirCacheEntry();
@@ -424,9 +436,10 @@ void processEntry(CanonicalTreeParser m, DirCacheBuildIterator i,
}
keep(i.getEntryPathString(), entry, f);
}
- } else
+ } else {
// The index contains a folder
keep(i.getEntryPathString(), i.getDirCacheEntry(), f);
+ }
} else {
// There is no entry in the merge commit. Means: we want to delete
// what's currently in the index and working tree
@@ -521,6 +534,13 @@ private boolean doCheckout() throws CorruptObjectException, IOException,
// update our index
builder.finish();
+ // On case-insensitive file systems we may have a case variant kept
+ // and another one removed. In that case, don't remove it.
+ if (existing != null) {
+ removed.removeAll(existing);
+ existing.clear();
+ }
+
// init progress reporting
int numTotal = removed.size() + updated.size() + conflicts.size();
monitor.beginTask(JGitText.get().checkingOutFiles, numTotal);
@@ -531,9 +551,9 @@ private boolean doCheckout() throws CorruptObjectException, IOException,
// when deleting files process them in the opposite order as they have
// been reported. This ensures the files are deleted before we delete
// their parent folders
- IntList nonDeleted = new IntList();
- for (int i = removed.size() - 1; i >= 0; i--) {
- String r = removed.get(i);
+ Iterator<String> iter = removed.descendingIterator();
+ while (iter.hasNext()) {
+ String r = iter.next();
file = new File(repo.getWorkTree(), r);
if (!file.delete() && repo.getFS().exists(file)) {
// The list of stuff to delete comes from the index
@@ -542,7 +562,7 @@ private boolean doCheckout() throws CorruptObjectException, IOException,
// to delete it. A submodule is not empty, so it
// is safe to check this after a failed delete.
if (!repo.getFS().isDirectory(file)) {
- nonDeleted.add(i);
+ iter.remove();
toBeDeleted.add(r);
}
} else {
@@ -560,8 +580,6 @@ private boolean doCheckout() throws CorruptObjectException, IOException,
if (file != null) {
removeEmptyParents(file);
}
- removed = filterOut(removed, nonDeleted);
- nonDeleted = null;
Iterator<Map.Entry<String, CheckoutMetadata>> toUpdate = updated
.entrySet().iterator();
Map.Entry<String, CheckoutMetadata> e = null;
@@ -633,36 +651,6 @@ private boolean doCheckout() throws CorruptObjectException, IOException,
return toBeDeleted.isEmpty();
}
- private static ArrayList<String> filterOut(ArrayList<String> strings,
- IntList indicesToRemove) {
- int n = indicesToRemove.size();
- if (n == strings.size()) {
- return new ArrayList<>(0);
- }
- switch (n) {
- case 0:
- return strings;
- case 1:
- strings.remove(indicesToRemove.get(0));
- return strings;
- default:
- int length = strings.size();
- ArrayList<String> result = new ArrayList<>(length - n);
- // Process indicesToRemove from the back; we know that it
- // contains indices in descending order.
- int j = n - 1;
- int idx = indicesToRemove.get(j);
- for (int i = 0; i < length; i++) {
- if (i == idx) {
- idx = (--j >= 0) ? indicesToRemove.get(j) : -1;
- } else {
- result.add(strings.get(i));
- }
- }
- return result;
- }
- }
-
private static boolean isSamePrefix(String a, String b) {
int as = a.lastIndexOf('/');
int bs = b.lastIndexOf('/');
@@ -1233,6 +1221,9 @@ private void keep(String path, DirCacheEntry e, WorkingTreeIterator f)
if (!FileMode.TREE.equals(e.getFileMode())) {
builder.add(e);
}
+ if (existing != null) {
+ existing.add(path);
+ }
if (force) {
if (f == null || f.isModified(e, true, walk.getObjectReader())) {
kept.add(path);
@@ -1401,127 +1392,6 @@ private boolean isModifiedSubtree_IndexTree(String path, ObjectId tree)
}
/**
- * Updates the file in the working tree with content and mode from an entry
- * in the index. The new content is first written to a new temporary file in
- * the same directory as the real file. Then that new file is renamed to the
- * final filename.
- *
- * <p>
- * <b>Note:</b> if the entry path on local file system exists as a non-empty
- * directory, and the target entry type is a link or file, the checkout will
- * fail with {@link java.io.IOException} since existing non-empty directory
- * cannot be renamed to file or link without deleting it recursively.
- * </p>
- *
- * @param repo
- * repository managing the destination work tree.
- * @param entry
- * the entry containing new mode and content
- * @param or
- * object reader to use for checkout
- * @throws java.io.IOException
- * if an IO error occurred
- * @since 3.6
- * @deprecated since 5.1, use
- * {@link #checkoutEntry(Repository, DirCacheEntry, ObjectReader, boolean, CheckoutMetadata, WorkingTreeOptions)}
- * instead
- */
- @Deprecated
- public static void checkoutEntry(Repository repo, DirCacheEntry entry,
- ObjectReader or) throws IOException {
- checkoutEntry(repo, entry, or, false, null, null);
- }
-
-
- /**
- * Updates the file in the working tree with content and mode from an entry
- * in the index. The new content is first written to a new temporary file in
- * the same directory as the real file. Then that new file is renamed to the
- * final filename.
- *
- * <p>
- * <b>Note:</b> if the entry path on local file system exists as a file, it
- * will be deleted and if it exists as a directory, it will be deleted
- * recursively, independently if has any content.
- * </p>
- *
- * @param repo
- * repository managing the destination work tree.
- * @param entry
- * the entry containing new mode and content
- * @param or
- * object reader to use for checkout
- * @param deleteRecursive
- * true to recursively delete final path if it exists on the file
- * system
- * @param checkoutMetadata
- * containing
- * <ul>
- * <li>smudgeFilterCommand to be run for smudging the entry to be
- * checked out</li>
- * <li>eolStreamType used for stream conversion</li>
- * </ul>
- * @throws java.io.IOException
- * if an IO error occurred
- * @since 4.2
- * @deprecated since 6.3, use
- * {@link #checkoutEntry(Repository, DirCacheEntry, ObjectReader, boolean, CheckoutMetadata, WorkingTreeOptions)}
- * instead
- */
- @Deprecated
- public static void checkoutEntry(Repository repo, DirCacheEntry entry,
- ObjectReader or, boolean deleteRecursive,
- CheckoutMetadata checkoutMetadata) throws IOException {
- checkoutEntry(repo, entry, or, deleteRecursive, checkoutMetadata, null);
- }
-
- /**
- * Updates the file in the working tree with content and mode from an entry
- * in the index. The new content is first written to a new temporary file in
- * the same directory as the real file. Then that new file is renamed to the
- * final filename.
- *
- * <p>
- * <b>Note:</b> if the entry path on local file system exists as a file, it
- * will be deleted and if it exists as a directory, it will be deleted
- * recursively, independently if has any content.
- * </p>
- *
- * @param repo
- * repository managing the destination work tree.
- * @param entry
- * the entry containing new mode and content
- * @param or
- * object reader to use for checkout
- * @param deleteRecursive
- * true to recursively delete final path if it exists on the file
- * system
- * @param checkoutMetadata
- * containing
- * <ul>
- * <li>smudgeFilterCommand to be run for smudging the entry to be
- * checked out</li>
- * <li>eolStreamType used for stream conversion</li>
- * </ul>
- * @param options
- * {@link WorkingTreeOptions} that are effective; if {@code null}
- * they are loaded from the repository config
- * @throws java.io.IOException
- * if an IO error occurred
- * @since 6.3
- * @deprecated since 6.6.1; use {@link Checkout} instead
- */
- @Deprecated
- public static void checkoutEntry(Repository repo, DirCacheEntry entry,
- ObjectReader or, boolean deleteRecursive,
- CheckoutMetadata checkoutMetadata, WorkingTreeOptions options)
- throws IOException {
- Checkout checkout = new Checkout(repo, options)
- .setRecursiveDeletion(deleteRecursive);
- checkout.checkout(entry, checkoutMetadata, or, null);
- }
-
- /**
* Return filtered content for a specific object (blob). EOL handling and
* smudge-filter handling are applied in the same way as it would be done
* during a checkout.
@@ -1647,6 +1517,8 @@ private static void runExternalFilterCommand(Repository repo, String path,
filterProcessBuilder.directory(repo.getWorkTree());
filterProcessBuilder.environment().put(Constants.GIT_DIR_KEY,
repo.getDirectory().getAbsolutePath());
+ filterProcessBuilder.environment().put(Constants.GIT_COMMON_DIR_KEY,
+ repo.getCommonDirectory().getAbsolutePath());
ExecutionResult result;
int rc;
try {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java
index c5e1e4e..5a22938 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java
@@ -396,28 +396,6 @@ void write(OutputStream os, DirCacheVersion version, DirCacheEntry previous)
* timestamp. This method tests to see if file was written out at the same
* time as the index.
*
- * @param smudge_s
- * seconds component of the index's last modified time.
- * @param smudge_ns
- * nanoseconds component of the index's last modified time.
- * @return true if extra careful checks should be used.
- * @deprecated use {@link #mightBeRacilyClean(Instant)} instead
- */
- @Deprecated
- public final boolean mightBeRacilyClean(int smudge_s, int smudge_ns) {
- return mightBeRacilyClean(Instant.ofEpochSecond(smudge_s, smudge_ns));
- }
-
- /**
- * Is it possible for this entry to be accidentally assumed clean?
- * <p>
- * The "racy git" problem happens when a work file can be updated faster
- * than the filesystem records file modification timestamps. It is possible
- * for an application to edit a work file, update the index, then edit it
- * again before the filesystem will give the work file a new modification
- * timestamp. This method tests to see if file was written out at the same
- * time as the index.
- *
* @param smudge
* index's last modified time.
* @return true if extra careful checks should be used.
@@ -653,22 +631,6 @@ public void setCreationTime(long when) {
}
/**
- * Get the cached last modification date of this file, in milliseconds.
- * <p>
- * One of the indicators that the file has been modified by an application
- * changing the working tree is if the last modification time for the file
- * differs from the time stored in this entry.
- *
- * @return last modification time of this file, in milliseconds since the
- * Java epoch (midnight Jan 1, 1970 UTC).
- * @deprecated use {@link #getLastModifiedInstant()} instead
- */
- @Deprecated
- public long getLastModified() {
- return decodeTS(P_MTIME);
- }
-
- /**
* Get the cached last modification date of this file.
* <p>
* One of the indicators that the file has been modified by an application
@@ -683,18 +645,6 @@ public Instant getLastModifiedInstant() {
}
/**
- * Set the cached last modification date of this file, using milliseconds.
- *
- * @param when
- * new cached modification date of the file, in milliseconds.
- * @deprecated use {@link #setLastModified(Instant)} instead
- */
- @Deprecated
- public void setLastModified(long when) {
- encodeTS(P_MTIME, when);
- }
-
- /**
* Set the cached last modification date of this file.
*
* @param when
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/errors/PackInvalidException.java b/org.eclipse.jgit/src/org/eclipse/jgit/errors/PackInvalidException.java
index 1fd8086..38982fd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/errors/PackInvalidException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/errors/PackInvalidException.java
@@ -23,18 +23,6 @@ public class PackInvalidException extends IOException {
private static final long serialVersionUID = 1L;
/**
- * Construct a pack invalid error.
- *
- * @param path
- * path of the invalid pack file.
- * @deprecated Use {@link #PackInvalidException(File, Throwable)}.
- */
- @Deprecated
- public PackInvalidException(File path) {
- this(path, null);
- }
-
- /**
* Construct a pack invalid error with cause.
*
* @param path
@@ -48,18 +36,6 @@ public PackInvalidException(File path, Throwable cause) {
}
/**
- * Construct a pack invalid error.
- *
- * @param path
- * path of the invalid pack file.
- * @deprecated Use {@link #PackInvalidException(String, Throwable)}.
- */
- @Deprecated
- public PackInvalidException(String path) {
- this(path, null);
- }
-
- /**
* Construct a pack invalid error with cause.
*
* @param path
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/BareSuperprojectWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/BareSuperprojectWriter.java
index 3ce97a4..e511a68 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/BareSuperprojectWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/BareSuperprojectWriter.java
@@ -156,6 +156,9 @@ private void prepareIndex(List<RepoProject> projects, DirCache index,
ObjectId objectId;
if (ObjectId.isId(proj.getRevision())) {
objectId = ObjectId.fromString(proj.getRevision());
+ if (config.recordRemoteBranch && proj.getUpstream() != null) {
+ cfg.setString("submodule", name, "ref", proj.getUpstream()); //$NON-NLS-1$//$NON-NLS-2$
+ }
} else {
objectId = callback.sha1(url, proj.getRevision());
if (objectId == null && !config.ignoreRemoteFailures) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java
index 957b386..b033177 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java
@@ -176,6 +176,10 @@ public void startElement(
attributes.getValue("groups"));
currentProject
.setRecommendShallow(attributes.getValue("clone-depth"));
+ currentProject
+ .setUpstream(attributes.getValue("upstream"));
+ currentProject
+ .setDestBranch(attributes.getValue("dest-branch"));
break;
case "remote":
String alias = attributes.getValue("alias");
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
index 95c1c8b..be77fca 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
@@ -111,32 +111,6 @@ public interface RemoteReader {
public ObjectId sha1(String uri, String ref) throws GitAPIException;
/**
- * Read a file from a remote repository.
- *
- * @param uri
- * The URI of the remote repository
- * @param ref
- * The ref (branch/tag/etc.) to read
- * @param path
- * The relative path (inside the repo) to the file to read
- * @return the file content.
- * @throws GitAPIException
- * If the ref have an invalid or ambiguous name, or it does
- * not exist in the repository,
- * @throws IOException
- * If the object does not exist or is too large
- * @since 3.5
- *
- * @deprecated Use {@link #readFileWithMode(String, String, String)}
- * instead
- */
- @Deprecated
- public default byte[] readFile(String uri, String ref, String path)
- throws GitAPIException, IOException {
- return readFileWithMode(uri, ref, path).getContents();
- }
-
- /**
* Read contents and mode (i.e. permissions) of the file from a remote
* repository.
*
@@ -255,7 +229,8 @@ public RemoteFile readFileWithMode(String uri, String ref, String path)
@SuppressWarnings("serial")
static class ManifestErrorException extends GitAPIException {
ManifestErrorException(Throwable cause) {
- super(RepoText.get().invalidManifest, cause);
+ super(RepoText.get().invalidManifest + " " + cause.getMessage(), //$NON-NLS-1$
+ cause);
}
}
@@ -615,6 +590,7 @@ private List<RepoProject> renameProjects(List<RepoProject> projects) {
p.setUrl(proj.getUrl());
p.addCopyFiles(proj.getCopyFiles());
p.addLinkFiles(proj.getLinkFiles());
+ p.setUpstream(proj.getUpstream());
ret.add(p);
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java
index 8deb738..2630da3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java
@@ -38,6 +38,8 @@ public class RepoProject implements Comparable<RepoProject> {
private final Set<String> groups;
private final List<CopyFile> copyfiles;
private final List<LinkFile> linkfiles;
+ private String upstream;
+ private String destBranch;
private String recommendShallow;
private String url;
private String defaultRevision;
@@ -389,6 +391,57 @@ public void clearLinkFiles() {
this.linkfiles.clear();
}
+ /**
+ * Return the upstream attribute of the project
+ *
+ * @return the upstream value if present, null otherwise.
+ *
+ * @since 6.10
+ */
+ public String getUpstream() {
+ return this.upstream;
+ }
+
+ /**
+ * Return the dest-branch attribute of the project
+ *
+ * @return the dest-branch value if present, null otherwise.
+ *
+ * @since 6.10
+ */
+ public String getDestBranch() {
+ return this.destBranch;
+ }
+
+ /**
+ * Set the upstream attribute of the project
+ *
+ * Name of the git ref in which a sha1 can be found, when the revision is a
+ * sha1.
+ *
+ * @param upstream
+ * value of the attribute in the manifest
+ *
+ * @since 6.10
+ */
+ public void setUpstream(String upstream) {
+ this.upstream = upstream;
+ }
+
+ /**
+ * Set the dest-branch attribute of the project
+ *
+ * Name of a Git branch.
+ *
+ * @param destBranch
+ * value of the attribute in the manifest
+ *
+ * @since 6.10
+ */
+ public void setDestBranch(String destBranch) {
+ this.destBranch = destBranch;
+ }
+
private String getPathWithSlash() {
if (path.endsWith("/")) { //$NON-NLS-1$
return path;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
index 700b54a..bf252f9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
@@ -94,8 +94,6 @@ public static JGitText get() {
/***/ public String binaryHunkInvalidLength;
/***/ public String binaryHunkLineTooShort;
/***/ public String binaryHunkMissingNewline;
- /***/ public String bitmapAccessErrorForPackfile;
- /***/ public String bitmapFailedToGet;
/***/ public String bitmapMissingObject;
/***/ public String bitmapsMustBePrepared;
/***/ public String bitmapUseNoopNoListener;
@@ -108,6 +106,7 @@ public static JGitText get() {
/***/ public String buildingBitmaps;
/***/ public String cachedPacksPreventsIndexCreation;
/***/ public String cachedPacksPreventsListingObjects;
+ /***/ public String cacheRegionAllOrNoneNull;
/***/ public String cannotAccessLastModifiedForSafeDeletion;
/***/ public String cannotBeCombined;
/***/ public String cannotBeRecursiveWhenTreesAreIncluded;
@@ -295,6 +294,7 @@ public static JGitText get() {
/***/ public String deleteTagUnexpectedResult;
/***/ public String deletingBranches;
/***/ public String deletingNotSupported;
+ /***/ public String deprecatedTrustFolderStat;
/***/ public String depthMustBeAt1;
/***/ public String depthWithUnshallow;
/***/ public String destinationIsNotAWildcard;
@@ -315,6 +315,9 @@ public static JGitText get() {
/***/ public String downloadCancelled;
/***/ public String downloadCancelledDuringIndexing;
/***/ public String duplicateAdvertisementsOf;
+ /***/ public String duplicateCacheTablesGiven;
+ /***/ public String duplicatePackExtensionsForCacheTables;
+ /***/ public String duplicatePackExtensionsSet;
/***/ public String duplicateRef;
/***/ public String duplicateRefAttribute;
/***/ public String duplicateRemoteRefUpdateIsIllegal;
@@ -490,6 +493,7 @@ public static JGitText get() {
/***/ public String invalidTimeUnitValue2;
/***/ public String invalidTimeUnitValue3;
/***/ public String invalidTreeZeroLengthName;
+ /***/ public String invalidTrustStat;
/***/ public String invalidURL;
/***/ public String invalidWildcards;
/***/ public String invalidRefSpec;
@@ -554,6 +558,8 @@ public static JGitText get() {
/***/ public String month;
/***/ public String months;
/***/ public String monthsAgo;
+ /***/ public String multiPackIndexUnexpectedSize;
+ /***/ public String multiPackIndexWritingCancelled;
/***/ public String multipleMergeBasesFor;
/***/ public String nameMustNotBeNullOrEmpty;
/***/ public String need2Arguments;
@@ -569,6 +575,8 @@ public static JGitText get() {
/***/ public String noMergeHeadSpecified;
/***/ public String nonBareLinkFilesNotSupported;
/***/ public String nonCommitToHeads;
+ /***/ public String noPackExtConfigurationGiven;
+ /***/ public String noPackExtGivenForConfiguration;
/***/ public String noPathAttributesFound;
/***/ public String noSuchRef;
/***/ public String noSuchRefKnown;
@@ -601,7 +609,6 @@ public static JGitText get() {
/***/ public String oldIdMustNotBeNull;
/***/ public String onlyOneFetchSupported;
/***/ public String onlyOneOperationCallPerConnectionIsSupported;
- /***/ public String onlyOpenPgpSupportedForSigning;
/***/ public String openFilesMustBeAtLeast1;
/***/ public String openingConnection;
/***/ public String operationCanceled;
@@ -623,6 +630,8 @@ public static JGitText get() {
/***/ public String packingCancelledDuringObjectsWriting;
/***/ public String packObjectCountMismatch;
/***/ public String packRefs;
+ /***/ public String packRefsFailed;
+ /***/ public String packRefsSuccessful;
/***/ public String packSizeNotSetYet;
/***/ public String packTooLargeForIndexVersion1;
/***/ public String packWasDeleted;
@@ -639,6 +648,7 @@ public static JGitText get() {
/***/ public String personIdentEmailNonNull;
/***/ public String personIdentNameNonNull;
/***/ public String postCommitHookFailed;
+ /***/ public String precedenceTrustConfig;
/***/ public String prefixRemote;
/***/ public String problemWithResolvingPushRefSpecsLocally;
/***/ public String progressMonUploading;
@@ -666,8 +676,6 @@ public static JGitText get() {
/***/ public String readerIsRequired;
/***/ public String readingObjectsFromLocalRepositoryFailed;
/***/ public String readLastModifiedFailed;
- /***/ public String readPipeIsNotAllowed;
- /***/ public String readPipeIsNotAllowedRequiredPermission;
/***/ public String readTimedOut;
/***/ public String receivePackObjectTooLarge1;
/***/ public String receivePackObjectTooLarge2;
@@ -747,6 +755,8 @@ public static JGitText get() {
/***/ public String shutdownCleanup;
/***/ public String shutdownCleanupFailed;
/***/ public String shutdownCleanupListenerFailed;
+ /***/ public String signatureServiceConflict;
+ /***/ public String signatureTypeUnknown;
/***/ public String signatureVerificationError;
/***/ public String signatureVerificationUnavailable;
/***/ public String signedTagMessageNoLf;
@@ -833,6 +843,7 @@ public static JGitText get() {
/***/ public String unableToCheckConnectivity;
/***/ public String unableToCreateNewObject;
/***/ public String unableToReadFullInt;
+ /***/ public String unableToReadFullArray;
/***/ public String unableToReadPackfile;
/***/ public String unableToRemovePath;
/***/ public String unableToWrite;
@@ -858,6 +869,7 @@ public static JGitText get() {
/***/ public String unknownObjectInIndex;
/***/ public String unknownObjectType;
/***/ public String unknownObjectType2;
+ /***/ public String unknownPackExtension;
/***/ public String unknownPositionEncoding;
/***/ public String unknownRefStorageFormat;
/***/ public String unknownRepositoryFormat;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphWriter.java
index 0d9815e..55539e2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/commitgraph/CommitGraphWriter.java
@@ -52,6 +52,7 @@
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.EmptyTreeIterator;
import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.filter.TreeFilter;
import org.eclipse.jgit.util.NB;
/**
@@ -71,6 +72,9 @@ public class CommitGraphWriter {
private static final int MAX_CHANGED_PATHS = 512;
+ private static final PathDiffCalculator PATH_DIFF_CALCULATOR
+ = new PathDiffCalculator();
+
private final int hashsz;
private final GraphCommits graphCommits;
@@ -374,37 +378,6 @@ private void writeCommitData(CancellableDigestOutputStream out)
return generations;
}
- private static Optional<HashSet<ByteBuffer>> computeBloomFilterPaths(
- ObjectReader or, RevCommit cmit) throws MissingObjectException,
- IncorrectObjectTypeException, CorruptObjectException, IOException {
- HashSet<ByteBuffer> paths = new HashSet<>();
- try (TreeWalk walk = new TreeWalk(null, or)) {
- walk.setRecursive(true);
- if (cmit.getParentCount() == 0) {
- walk.addTree(new EmptyTreeIterator());
- } else {
- walk.addTree(cmit.getParent(0).getTree());
- }
- walk.addTree(cmit.getTree());
- while (walk.next()) {
- if (walk.idEqual(0, 1)) {
- continue;
- }
- byte[] rawPath = walk.getRawPath();
- paths.add(ByteBuffer.wrap(rawPath));
- for (int i = 0; i < rawPath.length; i++) {
- if (rawPath[i] == '/') {
- paths.add(ByteBuffer.wrap(rawPath, 0, i));
- }
- if (paths.size() > MAX_CHANGED_PATHS) {
- return Optional.empty();
- }
- }
- }
- }
- return Optional.of(paths);
- }
-
private BloomFilterChunks computeBloomFilterChunks(ProgressMonitor monitor)
throws MissingObjectException, IncorrectObjectTypeException,
CorruptObjectException, IOException {
@@ -435,8 +408,8 @@ private BloomFilterChunks computeBloomFilterChunks(ProgressMonitor monitor)
filtersReused++;
} else {
filtersComputed++;
- Optional<HashSet<ByteBuffer>> paths = computeBloomFilterPaths(
- graphCommits.getObjectReader(), cmit);
+ Optional<HashSet<ByteBuffer>> paths = PATH_DIFF_CALCULATOR
+ .changedPaths(graphCommits.getObjectReader(), cmit);
if (paths.isEmpty()) {
cpf = ChangedPathFilter.FULL;
} else {
@@ -473,6 +446,44 @@ private void writeExtraEdges(CancellableDigestOutputStream out)
}
}
+ // Visible for testing
+ static class PathDiffCalculator {
+
+ // Walk steps in the last invocation of changedPaths
+ int stepCounter;
+
+ Optional<HashSet<ByteBuffer>> changedPaths(
+ ObjectReader or, RevCommit cmit) throws MissingObjectException,
+ IncorrectObjectTypeException, CorruptObjectException, IOException {
+ stepCounter = 0;
+ HashSet<ByteBuffer> paths = new HashSet<>();
+ try (TreeWalk walk = new TreeWalk(null, or)) {
+ walk.setRecursive(true);
+ walk.setFilter(TreeFilter.ANY_DIFF);
+ if (cmit.getParentCount() == 0) {
+ walk.addTree(new EmptyTreeIterator());
+ } else {
+ walk.addTree(cmit.getParent(0).getTree());
+ }
+ walk.addTree(cmit.getTree());
+ while (walk.next()) {
+ stepCounter += 1;
+ byte[] rawPath = walk.getRawPath();
+ paths.add(ByteBuffer.wrap(rawPath));
+ for (int i = 0; i < rawPath.length; i++) {
+ if (rawPath[i] == '/') {
+ paths.add(ByteBuffer.wrap(rawPath, 0, i));
+ }
+ if (paths.size() > MAX_CHANGED_PATHS) {
+ return Optional.empty();
+ }
+ }
+ }
+ }
+ return Optional.of(paths);
+ }
+ }
+
private static class ChunkHeader {
final int id;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/AggregatedBlockCacheStats.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/AggregatedBlockCacheStats.java
new file mode 100644
index 0000000..295b702
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/AggregatedBlockCacheStats.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2024, Google LLC and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.eclipse.jgit.internal.storage.dfs;
+
+import static org.eclipse.jgit.internal.storage.dfs.DfsBlockCacheTable.BlockCacheStats;
+
+import java.util.List;
+
+import org.eclipse.jgit.internal.storage.pack.PackExt;
+
+/**
+ * Aggregates values for all given {@link BlockCacheStats}.
+ */
+class AggregatedBlockCacheStats implements BlockCacheStats {
+ private final List<BlockCacheStats> blockCacheStats;
+
+ static BlockCacheStats fromStatsList(
+ List<BlockCacheStats> blockCacheStats) {
+ if (blockCacheStats.size() == 1) {
+ return blockCacheStats.get(0);
+ }
+ return new AggregatedBlockCacheStats(blockCacheStats);
+ }
+
+ private AggregatedBlockCacheStats(List<BlockCacheStats> blockCacheStats) {
+ this.blockCacheStats = blockCacheStats;
+ }
+
+ @Override
+ public String getName() {
+ return AggregatedBlockCacheStats.class.getName();
+ }
+
+ @Override
+ public long[] getCurrentSize() {
+ long[] sums = emptyPackStats();
+ for (BlockCacheStats blockCacheStatsEntry : blockCacheStats) {
+ sums = add(sums, blockCacheStatsEntry.getCurrentSize());
+ }
+ return sums;
+ }
+
+ @Override
+ public long[] getHitCount() {
+ long[] sums = emptyPackStats();
+ for (BlockCacheStats blockCacheStatsEntry : blockCacheStats) {
+ sums = add(sums, blockCacheStatsEntry.getHitCount());
+ }
+ return sums;
+ }
+
+ @Override
+ public long[] getMissCount() {
+ long[] sums = emptyPackStats();
+ for (BlockCacheStats blockCacheStatsEntry : blockCacheStats) {
+ sums = add(sums, blockCacheStatsEntry.getMissCount());
+ }
+ return sums;
+ }
+
+ @Override
+ public long[] getTotalRequestCount() {
+ long[] sums = emptyPackStats();
+ for (BlockCacheStats blockCacheStatsEntry : blockCacheStats) {
+ sums = add(sums, blockCacheStatsEntry.getTotalRequestCount());
+ }
+ return sums;
+ }
+
+ @Override
+ public long[] getHitRatio() {
+ long[] hit = getHitCount();
+ long[] miss = getMissCount();
+ long[] ratio = new long[Math.max(hit.length, miss.length)];
+ for (int i = 0; i < ratio.length; i++) {
+ if (i >= hit.length) {
+ ratio[i] = 0;
+ } else if (i >= miss.length) {
+ ratio[i] = 100;
+ } else {
+ long total = hit[i] + miss[i];
+ ratio[i] = total == 0 ? 0 : hit[i] * 100 / total;
+ }
+ }
+ return ratio;
+ }
+
+ @Override
+ public long[] getEvictions() {
+ long[] sums = emptyPackStats();
+ for (BlockCacheStats blockCacheStatsEntry : blockCacheStats) {
+ sums = add(sums, blockCacheStatsEntry.getEvictions());
+ }
+ return sums;
+ }
+
+ private static long[] emptyPackStats() {
+ return new long[PackExt.values().length];
+ }
+
+ private static long[] add(long[] first, long[] second) {
+ long[] sums = new long[Integer.max(first.length, second.length)];
+ int i;
+ for (i = 0; i < Integer.min(first.length, second.length); i++) {
+ sums[i] = first[i] + second[i];
+ }
+ for (int j = i; j < first.length; j++) {
+ sums[j] = first[i];
+ }
+ for (int j = i; j < second.length; j++) {
+ sums[j] = second[i];
+ }
+ return sums;
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/ClockBlockCacheTable.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/ClockBlockCacheTable.java
index d0907bc..587d482 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/ClockBlockCacheTable.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/ClockBlockCacheTable.java
@@ -12,6 +12,7 @@
import java.io.IOException;
import java.time.Duration;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReferenceArray;
@@ -47,6 +48,11 @@
* invocations is also fixed in size.
*/
final class ClockBlockCacheTable implements DfsBlockCacheTable {
+ /**
+ * Table name.
+ */
+ private final String name;
+
/** Number of entries in {@link #table}. */
private final int tableSize;
@@ -129,14 +135,20 @@ final class ClockBlockCacheTable implements DfsBlockCacheTable {
-1, 0, null);
clockHand.next = clockHand;
- this.dfsBlockCacheStats = new DfsBlockCacheStats();
+ this.name = cfg.getName();
+ this.dfsBlockCacheStats = new DfsBlockCacheStats(this.name);
this.refLockWaitTime = cfg.getRefLockWaitTimeConsumer();
this.indexEventConsumer = cfg.getIndexEventConsumer();
}
@Override
- public DfsBlockCacheStats getDfsBlockCacheStats() {
- return dfsBlockCacheStats;
+ public List<BlockCacheStats> getBlockCacheStats() {
+ return List.of(dfsBlockCacheStats);
+ }
+
+ @Override
+ public String getName() {
+ return name;
}
@Override
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java
index 56719cf..f8e0831 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java
@@ -11,7 +11,10 @@
package org.eclipse.jgit.internal.storage.dfs;
+import static org.eclipse.jgit.internal.storage.dfs.DfsBlockCacheTable.BlockCacheStats;
+
import java.io.IOException;
+import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.LongStream;
@@ -97,7 +100,12 @@ private DfsBlockCache(DfsBlockCacheConfig cfg) {
double streamRatio = cfg.getStreamRatio();
maxStreamThroughCache = (long) (maxBytes * streamRatio);
- dfsBlockCacheTable = new ClockBlockCacheTable(cfg);
+ if (!cfg.getPackExtCacheConfigurations().isEmpty()) {
+ dfsBlockCacheTable = PackExtBlockCacheTable
+ .fromBlockCacheConfigs(cfg);
+ } else {
+ dfsBlockCacheTable = new ClockBlockCacheTable(cfg);
+ }
for (int i = 0; i < PackExt.values().length; ++i) {
Integer limit = cfg.getCacheHotMap().get(PackExt.values()[i]);
@@ -119,7 +127,7 @@ boolean shouldCopyThroughCache(long length) {
* @return total number of bytes in the cache, per pack file extension.
*/
public long[] getCurrentSize() {
- return dfsBlockCacheTable.getDfsBlockCacheStats().getCurrentSize();
+ return getAggregatedBlockCacheStats().getCurrentSize();
}
/**
@@ -138,7 +146,7 @@ public long getFillPercentage() {
* extension.
*/
public long[] getHitCount() {
- return dfsBlockCacheTable.getDfsBlockCacheStats().getHitCount();
+ return getAggregatedBlockCacheStats().getHitCount();
}
/**
@@ -149,7 +157,7 @@ public long getFillPercentage() {
* extension.
*/
public long[] getMissCount() {
- return dfsBlockCacheTable.getDfsBlockCacheStats().getMissCount();
+ return getAggregatedBlockCacheStats().getMissCount();
}
/**
@@ -158,8 +166,7 @@ public long getFillPercentage() {
* @return total number of requests (hit + miss), per pack file extension.
*/
public long[] getTotalRequestCount() {
- return dfsBlockCacheTable.getDfsBlockCacheStats()
- .getTotalRequestCount();
+ return getAggregatedBlockCacheStats().getTotalRequestCount();
}
/**
@@ -168,7 +175,7 @@ public long getFillPercentage() {
* @return hit ratios
*/
public long[] getHitRatio() {
- return dfsBlockCacheTable.getDfsBlockCacheStats().getHitRatio();
+ return getAggregatedBlockCacheStats().getHitRatio();
}
/**
@@ -179,7 +186,18 @@ public long getFillPercentage() {
* file extension.
*/
public long[] getEvictions() {
- return dfsBlockCacheTable.getDfsBlockCacheStats().getEvictions();
+ return getAggregatedBlockCacheStats().getEvictions();
+ }
+
+ /**
+ * Get the list of {@link BlockCacheStats} for all underlying caches.
+ * <p>
+ * Useful in monitoring caches with breakdown.
+ *
+ * @return the list of {@link BlockCacheStats} for all underlying caches.
+ */
+ public List<BlockCacheStats> getAllBlockCacheStats() {
+ return dfsBlockCacheTable.getBlockCacheStats();
}
/**
@@ -259,6 +277,11 @@ <T> T get(DfsStreamKey key, long position) {
return dfsBlockCacheTable.get(key, position);
}
+ private BlockCacheStats getAggregatedBlockCacheStats() {
+ return AggregatedBlockCacheStats
+ .fromStatsList(dfsBlockCacheTable.getBlockCacheStats());
+ }
+
static final class Ref<T> {
final DfsStreamKey key;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java
index 77273ce..17bf518 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java
@@ -11,17 +11,27 @@
package org.eclipse.jgit.internal.storage.dfs;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_CORE_SECTION;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_DFS_CACHE_PREFIX;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_DFS_SECTION;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_BLOCK_LIMIT;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_BLOCK_SIZE;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_CONCURRENCY_LEVEL;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_PACK_EXTENSIONS;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_STREAM_RATIO;
+import java.io.PrintWriter;
import java.text.MessageFormat;
import java.time.Duration;
+import java.util.ArrayList;
import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.stream.Collectors;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.pack.PackExt;
@@ -41,25 +51,103 @@ public class DfsBlockCacheConfig {
/** Default number of max cache hits. */
public static final int DEFAULT_CACHE_HOT_MAX = 1;
+ static final String DEFAULT_NAME = "<default>"; //$NON-NLS-1$
+
+ private String name;
+
private long blockLimit;
+
private int blockSize;
+
private double streamRatio;
+
private int concurrencyLevel;
private Consumer<Long> refLock;
+
private Map<PackExt, Integer> cacheHotMap;
private IndexEventConsumer indexEventConsumer;
+ private List<DfsBlockCachePackExtConfig> packExtCacheConfigurations;
+
/**
* Create a default configuration.
*/
public DfsBlockCacheConfig() {
+ name = DEFAULT_NAME;
setBlockLimit(32 * MB);
setBlockSize(64 * KB);
setStreamRatio(0.30);
setConcurrencyLevel(32);
cacheHotMap = Collections.emptyMap();
+ packExtCacheConfigurations = Collections.emptyList();
+ }
+
+ /**
+ * Print the current cache configuration to the given {@link PrintWriter}.
+ *
+ * @param writer
+ * {@link PrintWriter} to write the cache's configuration to.
+ */
+ public void print(PrintWriter writer) {
+ print(/* linePrefix= */ "", /* pad= */ " ", writer); //$NON-NLS-1$//$NON-NLS-2$
+ }
+
+ /**
+ * Print the current cache configuration to the given {@link PrintWriter}.
+ *
+ * @param linePrefix
+ * prefix to prepend all writen lines with. Ex a string of 0 or
+ * more " " entries.
+ * @param pad
+ * filler used to extend linePrefix. Ex a multiple of " ".
+ * @param writer
+ * {@link PrintWriter} to write the cache's configuration to.
+ */
+ @SuppressWarnings("nls")
+ private void print(String linePrefix, String pad, PrintWriter writer) {
+ String currentPrefixLevel = linePrefix;
+ if (!name.isEmpty() || !packExtCacheConfigurations.isEmpty()) {
+ writer.println(linePrefix + "Name: "
+ + (name.isEmpty() ? DEFAULT_NAME : this.name));
+ currentPrefixLevel += pad;
+ }
+ writer.println(currentPrefixLevel + "BlockLimit: " + blockLimit);
+ writer.println(currentPrefixLevel + "BlockSize: " + blockSize);
+ writer.println(currentPrefixLevel + "StreamRatio: " + streamRatio);
+ writer.println(
+ currentPrefixLevel + "ConcurrencyLevel: " + concurrencyLevel);
+ for (Map.Entry<PackExt, Integer> entry : cacheHotMap.entrySet()) {
+ writer.println(currentPrefixLevel + "CacheHotMapEntry: "
+ + entry.getKey() + " : " + entry.getValue());
+ }
+ for (DfsBlockCachePackExtConfig extConfig : packExtCacheConfigurations) {
+ extConfig.print(currentPrefixLevel, pad, writer);
+ }
+ }
+
+ /**
+ * Get the name for the block cache configured by this cache config.
+ *
+ * @return the name for the block cache configured by this cache config.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Set the name for the block cache configured by this cache config.
+ * <p>
+ * Made visible for testing.
+ *
+ * @param name
+ * the name for the block cache configured by this cache config.
+ * @return {@code this}
+ */
+ DfsBlockCacheConfig setName(String name) {
+ this.name = name;
+ return this;
}
/**
@@ -77,10 +165,10 @@ public long getBlockLimit() {
* Set maximum number bytes of heap memory to dedicate to caching pack file
* data.
* <p>
- * It is strongly recommended to set the block limit to be an integer multiple
- * of the block size. This constraint is not enforced by this method (since
- * it may be called before {@link #setBlockSize(int)}), but it is enforced by
- * {@link #fromConfig(Config)}.
+ * It is strongly recommended to set the block limit to be an integer
+ * multiple of the block size. This constraint is not enforced by this
+ * method (since it may be called before {@link #setBlockSize(int)}), but it
+ * is enforced by {@link #fromConfig(Config)}.
*
* @param newLimit
* maximum number bytes of heap memory to dedicate to caching
@@ -89,9 +177,9 @@ public long getBlockLimit() {
*/
public DfsBlockCacheConfig setBlockLimit(long newLimit) {
if (newLimit <= 0) {
- throw new IllegalArgumentException(MessageFormat.format(
- JGitText.get().blockLimitNotPositive,
- Long.valueOf(newLimit)));
+ throw new IllegalArgumentException(
+ MessageFormat.format(JGitText.get().blockLimitNotPositive,
+ Long.valueOf(newLimit)));
}
blockLimit = newLimit;
return this;
@@ -211,12 +299,24 @@ public Map<PackExt, Integer> getCacheHotMap() {
* map of hot count per pack extension for {@code DfsBlockCache}.
* @return {@code this}
*/
+ /*
+ * TODO The cache HotMap configuration should be set as a config option and
+ * not passed in through a setter.
+ */
public DfsBlockCacheConfig setCacheHotMap(
Map<PackExt, Integer> cacheHotMap) {
this.cacheHotMap = Collections.unmodifiableMap(cacheHotMap);
+ setCacheHotMapToPackExtConfigs(this.cacheHotMap);
return this;
}
+ private void setCacheHotMapToPackExtConfigs(
+ Map<PackExt, Integer> cacheHotMap) {
+ for (DfsBlockCachePackExtConfig packExtConfig : packExtCacheConfigurations) {
+ packExtConfig.setCacheHotMap(cacheHotMap);
+ }
+ }
+
/**
* Get the consumer of cache index events.
*
@@ -240,61 +340,121 @@ public DfsBlockCacheConfig setIndexEventConsumer(
}
/**
+ * Get the list of pack ext cache configs.
+ *
+ * @return the list of pack ext cache configs.
+ */
+ List<DfsBlockCachePackExtConfig> getPackExtCacheConfigurations() {
+ return packExtCacheConfigurations;
+ }
+
+ /**
+ * Set the list of pack ext cache configs.
+ *
+ * Made visible for testing.
+ *
+ * @param packExtCacheConfigurations
+ * the list of pack ext cache configs to set.
+ * @return {@code this}
+ */
+ DfsBlockCacheConfig setPackExtCacheConfigurations(
+ List<DfsBlockCachePackExtConfig> packExtCacheConfigurations) {
+ this.packExtCacheConfigurations = packExtCacheConfigurations;
+ return this;
+ }
+
+ /**
* Update properties by setting fields from the configuration.
* <p>
* If a property is not defined in the configuration, then it is left
* unmodified.
* <p>
- * Enforces certain constraints on the combination of settings in the config,
- * for example that the block limit is a multiple of the block size.
+ * Enforces certain constraints on the combination of settings in the
+ * config, for example that the block limit is a multiple of the block size.
*
* @param rc
* configuration to read properties from.
* @return {@code this}
*/
public DfsBlockCacheConfig fromConfig(Config rc) {
- long cfgBlockLimit = rc.getLong(
- CONFIG_CORE_SECTION,
- CONFIG_DFS_SECTION,
- CONFIG_KEY_BLOCK_LIMIT,
- getBlockLimit());
- int cfgBlockSize = rc.getInt(
- CONFIG_CORE_SECTION,
- CONFIG_DFS_SECTION,
- CONFIG_KEY_BLOCK_SIZE,
+ fromConfig(CONFIG_CORE_SECTION, CONFIG_DFS_SECTION, rc);
+ loadPackExtConfigs(rc);
+ return this;
+ }
+
+ private void fromConfig(String section, String subSection, Config rc) {
+ long cfgBlockLimit = rc.getLong(section, subSection,
+ CONFIG_KEY_BLOCK_LIMIT, getBlockLimit());
+ int cfgBlockSize = rc.getInt(section, subSection, CONFIG_KEY_BLOCK_SIZE,
getBlockSize());
if (cfgBlockLimit % cfgBlockSize != 0) {
throw new IllegalArgumentException(MessageFormat.format(
JGitText.get().blockLimitNotMultipleOfBlockSize,
- Long.valueOf(cfgBlockLimit),
- Long.valueOf(cfgBlockSize)));
+ Long.valueOf(cfgBlockLimit), Long.valueOf(cfgBlockSize)));
}
+ // Set name only if `core dfs` is configured, otherwise fall back to the
+ // default.
+ if (rc.getSubsections(section).contains(subSection)) {
+ this.name = subSection;
+ }
setBlockLimit(cfgBlockLimit);
setBlockSize(cfgBlockSize);
- setConcurrencyLevel(rc.getInt(
- CONFIG_CORE_SECTION,
- CONFIG_DFS_SECTION,
- CONFIG_KEY_CONCURRENCY_LEVEL,
- getConcurrencyLevel()));
+ setConcurrencyLevel(rc.getInt(section, subSection,
+ CONFIG_KEY_CONCURRENCY_LEVEL, getConcurrencyLevel()));
- String v = rc.getString(
- CONFIG_CORE_SECTION,
- CONFIG_DFS_SECTION,
- CONFIG_KEY_STREAM_RATIO);
+ String v = rc.getString(section, subSection, CONFIG_KEY_STREAM_RATIO);
if (v != null) {
try {
setStreamRatio(Double.parseDouble(v));
} catch (NumberFormatException e) {
throw new IllegalArgumentException(MessageFormat.format(
- JGitText.get().enumValueNotSupported3,
- CONFIG_CORE_SECTION,
- CONFIG_DFS_SECTION,
- CONFIG_KEY_STREAM_RATIO, v), e);
+ JGitText.get().enumValueNotSupported3, section,
+ subSection, CONFIG_KEY_STREAM_RATIO, v), e);
}
}
- return this;
+ }
+
+ private void loadPackExtConfigs(Config config) {
+ List<String> subSections = config.getSubsections(CONFIG_CORE_SECTION)
+ .stream()
+ .filter(section -> section.startsWith(CONFIG_DFS_CACHE_PREFIX))
+ .collect(Collectors.toList());
+ if (subSections.size() == 0) {
+ return;
+ }
+ ArrayList<DfsBlockCachePackExtConfig> cacheConfigs = new ArrayList<>();
+ Set<PackExt> extensionsSeen = new HashSet<>();
+ for (String subSection : subSections) {
+ var cacheConfig = DfsBlockCachePackExtConfig.fromConfig(config,
+ CONFIG_CORE_SECTION, subSection);
+ Set<PackExt> packExtsDuplicates = intersection(extensionsSeen,
+ cacheConfig.packExts);
+ if (packExtsDuplicates.size() > 0) {
+ String duplicatePackExts = packExtsDuplicates.stream()
+ .map(PackExt::toString)
+ .collect(Collectors.joining(",")); //$NON-NLS-1$
+ throw new IllegalArgumentException(MessageFormat.format(
+ JGitText.get().duplicatePackExtensionsSet,
+ CONFIG_CORE_SECTION, subSection,
+ CONFIG_KEY_PACK_EXTENSIONS, duplicatePackExts));
+ }
+ extensionsSeen.addAll(cacheConfig.packExts);
+ cacheConfigs.add(cacheConfig);
+ }
+ packExtCacheConfigurations = cacheConfigs;
+ setCacheHotMapToPackExtConfigs(this.cacheHotMap);
+ }
+
+ private static <T> Set<T> intersection(Set<T> first, Set<T> second) {
+ Set<T> ret = new HashSet<>();
+ for (T entry : second) {
+ if (first.contains(entry)) {
+ ret.add(entry);
+ }
+ }
+ return ret;
}
/** Consumer of DfsBlockCache loading and eviction events for indexes. */
@@ -346,4 +506,102 @@ default boolean shouldReportEvictedEvent() {
return false;
}
}
-}
\ No newline at end of file
+
+ /**
+ * A configuration for a single cache table storing 1 or more Pack
+ * extensions.
+ * <p>
+ * The current pack ext cache tables implementation supports the same
+ * parameters the ClockBlockCacheTable (current default implementation).
+ * <p>
+ * Configuration falls back to the defaults coded values defined in the
+ * {@link DfsBlockCacheConfig} when not set on each cache table
+ * configuration and NOT the values of the basic dfs section.
+ * <p>
+ * <code>
+ *
+ * Format:
+ * [core "dfs.packCache"]
+ * packExtensions = "PACK"
+ * blockSize = 512
+ * blockLimit = 100
+ * concurrencyLevel = 5
+ *
+ * [core "dfs.multipleExtensionCache"]
+ * packExtensions = "INDEX REFTABLE BITMAP_INDEX"
+ * blockSize = 512
+ * blockLimit = 100
+ * concurrencyLevel = 5
+ * </code>
+ */
+ static class DfsBlockCachePackExtConfig {
+ // Set of pack extensions that will map to the cache instance.
+ private final EnumSet<PackExt> packExts;
+
+ // Configuration for the cache instance.
+ private final DfsBlockCacheConfig packExtCacheConfiguration;
+
+ /**
+ * Made visible for testing.
+ *
+ * @param packExts
+ * Set of {@link PackExt}s associated to this cache config.
+ * @param packExtCacheConfiguration
+ * {@link DfsBlockCacheConfig} for this cache config.
+ */
+ DfsBlockCachePackExtConfig(EnumSet<PackExt> packExts,
+ DfsBlockCacheConfig packExtCacheConfiguration) {
+ this.packExts = packExts;
+ this.packExtCacheConfiguration = packExtCacheConfiguration;
+ }
+
+ Set<PackExt> getPackExts() {
+ return packExts;
+ }
+
+ DfsBlockCacheConfig getPackExtCacheConfiguration() {
+ return packExtCacheConfiguration;
+ }
+
+ void setCacheHotMap(Map<PackExt, Integer> cacheHotMap) {
+ Map<PackExt, Integer> packExtHotMap = packExts.stream()
+ .filter(cacheHotMap::containsKey)
+ .collect(Collectors.toUnmodifiableMap(Function.identity(),
+ cacheHotMap::get));
+ packExtCacheConfiguration.setCacheHotMap(packExtHotMap);
+ }
+
+ private static DfsBlockCachePackExtConfig fromConfig(Config config,
+ String section, String subSection) {
+ String packExtensions = config.getString(section, subSection,
+ CONFIG_KEY_PACK_EXTENSIONS);
+ if (packExtensions == null) {
+ throw new IllegalArgumentException(
+ JGitText.get().noPackExtGivenForConfiguration);
+ }
+ String[] extensions = packExtensions.split(" ", -1); //$NON-NLS-1$
+ Set<PackExt> packExts = new HashSet<>(extensions.length);
+ for (String extension : extensions) {
+ try {
+ packExts.add(PackExt.valueOf(extension));
+ } catch (IllegalArgumentException e) {
+ throw new IllegalArgumentException(MessageFormat.format(
+ JGitText.get().unknownPackExtension, section,
+ subSection, CONFIG_KEY_PACK_EXTENSIONS, extension),
+ e);
+ }
+ }
+
+ DfsBlockCacheConfig dfsBlockCacheConfig = new DfsBlockCacheConfig();
+ dfsBlockCacheConfig.fromConfig(section, subSection, config);
+ return new DfsBlockCachePackExtConfig(EnumSet.copyOf(packExts),
+ dfsBlockCacheConfig);
+ }
+
+ void print(String linePrefix, String pad, PrintWriter writer) {
+ packExtCacheConfiguration.print(linePrefix, pad, writer);
+ writer.println(linePrefix + pad + "PackExts: " //$NON-NLS-1$
+ + packExts.stream().sorted().collect(Collectors.toList()));
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheStats.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheStats.java
new file mode 100644
index 0000000..436f574
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheStats.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2024, Google LLC and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.eclipse.jgit.internal.storage.dfs;
+
+import static org.eclipse.jgit.internal.storage.dfs.DfsBlockCacheTable.BlockCacheStats;
+
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.jgit.internal.storage.pack.PackExt;
+
+/**
+ * Keeps track of stats for a Block Cache table.
+ */
+class DfsBlockCacheStats implements BlockCacheStats {
+ private final String name;
+
+ /**
+ * Number of times a block was found in the cache, per pack file extension.
+ */
+ private final AtomicReference<AtomicLong[]> statHit;
+
+ /**
+ * Number of times a block was not found, and had to be loaded, per pack
+ * file extension.
+ */
+ private final AtomicReference<AtomicLong[]> statMiss;
+
+ /**
+ * Number of blocks evicted due to cache being full, per pack file
+ * extension.
+ */
+ private final AtomicReference<AtomicLong[]> statEvict;
+
+ /**
+ * Number of bytes currently loaded in the cache, per pack file extension.
+ */
+ private final AtomicReference<AtomicLong[]> liveBytes;
+
+ DfsBlockCacheStats() {
+ this(""); //$NON-NLS-1$
+ }
+
+ DfsBlockCacheStats(String name) {
+ this.name = name;
+ statHit = new AtomicReference<>(newCounters());
+ statMiss = new AtomicReference<>(newCounters());
+ statEvict = new AtomicReference<>(newCounters());
+ liveBytes = new AtomicReference<>(newCounters());
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Increment the {@code statHit} count.
+ *
+ * @param key
+ * key identifying which liveBytes entry to update.
+ */
+ void incrementHit(DfsStreamKey key) {
+ getStat(statHit, key).incrementAndGet();
+ }
+
+ /**
+ * Increment the {@code statMiss} count.
+ *
+ * @param key
+ * key identifying which liveBytes entry to update.
+ */
+ void incrementMiss(DfsStreamKey key) {
+ getStat(statMiss, key).incrementAndGet();
+ }
+
+ /**
+ * Increment the {@code statEvict} count.
+ *
+ * @param key
+ * key identifying which liveBytes entry to update.
+ */
+ void incrementEvict(DfsStreamKey key) {
+ getStat(statEvict, key).incrementAndGet();
+ }
+
+ /**
+ * Add {@code size} to the {@code liveBytes} count.
+ *
+ * @param key
+ * key identifying which liveBytes entry to update.
+ * @param size
+ * amount to increment the count by.
+ */
+ void addToLiveBytes(DfsStreamKey key, long size) {
+ getStat(liveBytes, key).addAndGet(size);
+ }
+
+ @Override
+ public long[] getCurrentSize() {
+ return getStatVals(liveBytes);
+ }
+
+ @Override
+ public long[] getHitCount() {
+ return getStatVals(statHit);
+ }
+
+ @Override
+ public long[] getMissCount() {
+ return getStatVals(statMiss);
+ }
+
+ @Override
+ public long[] getTotalRequestCount() {
+ AtomicLong[] hit = statHit.get();
+ AtomicLong[] miss = statMiss.get();
+ long[] cnt = new long[Math.max(hit.length, miss.length)];
+ for (int i = 0; i < hit.length; i++) {
+ cnt[i] += hit[i].get();
+ }
+ for (int i = 0; i < miss.length; i++) {
+ cnt[i] += miss[i].get();
+ }
+ return cnt;
+ }
+
+ @Override
+ public long[] getHitRatio() {
+ AtomicLong[] hit = statHit.get();
+ AtomicLong[] miss = statMiss.get();
+ long[] ratio = new long[Math.max(hit.length, miss.length)];
+ for (int i = 0; i < ratio.length; i++) {
+ if (i >= hit.length) {
+ ratio[i] = 0;
+ } else if (i >= miss.length) {
+ ratio[i] = 100;
+ } else {
+ long hitVal = hit[i].get();
+ long missVal = miss[i].get();
+ long total = hitVal + missVal;
+ ratio[i] = total == 0 ? 0 : hitVal * 100 / total;
+ }
+ }
+ return ratio;
+ }
+
+ @Override
+ public long[] getEvictions() {
+ return getStatVals(statEvict);
+ }
+
+ private static AtomicLong[] newCounters() {
+ AtomicLong[] ret = new AtomicLong[PackExt.values().length];
+ for (int i = 0; i < ret.length; i++) {
+ ret[i] = new AtomicLong();
+ }
+ return ret;
+ }
+
+ private static long[] getStatVals(AtomicReference<AtomicLong[]> stat) {
+ AtomicLong[] stats = stat.get();
+ long[] cnt = new long[stats.length];
+ for (int i = 0; i < stats.length; i++) {
+ cnt[i] = stats[i].get();
+ }
+ return cnt;
+ }
+
+ private static AtomicLong getStat(AtomicReference<AtomicLong[]> stats,
+ DfsStreamKey key) {
+ int pos = key.packExtPos;
+ while (true) {
+ AtomicLong[] vals = stats.get();
+ if (pos < vals.length) {
+ return vals[pos];
+ }
+ AtomicLong[] expect = vals;
+ vals = new AtomicLong[Math.max(pos + 1, PackExt.values().length)];
+ System.arraycopy(expect, 0, vals, 0, expect.length);
+ for (int i = expect.length; i < vals.length; i++) {
+ vals[i] = new AtomicLong();
+ }
+ if (stats.compareAndSet(expect, vals)) {
+ return vals[pos];
+ }
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheTable.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheTable.java
index 701d1fd..c3fd07b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheTable.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheTable.java
@@ -11,10 +11,7 @@
package org.eclipse.jgit.internal.storage.dfs;
import java.io.IOException;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.eclipse.jgit.internal.storage.pack.PackExt;
+import java.util.List;
/**
* Block cache table.
@@ -129,99 +126,43 @@ <T> DfsBlockCache.Ref<T> getOrLoadRef(DfsStreamKey key, long position,
<T> T get(DfsStreamKey key, long position);
/**
- * Get the DfsBlockCacheStats object for this block cache table's
- * statistics.
+ * Get the list of {@link BlockCacheStats} held by this cache.
+ * <p>
+ * The returned list has a {@link BlockCacheStats} per configured cache
+ * table, with a minimum of 1 {@link BlockCacheStats} object returned.
*
- * @return the DfsBlockCacheStats tracking this block cache table's
- * statistics.
+ * Use {@link AggregatedBlockCacheStats} to combine the results of the stats
+ * in the list for an aggregated view of the cache's stats.
+ *
+ * @return the list of {@link BlockCacheStats} held by this cache.
*/
- DfsBlockCacheStats getDfsBlockCacheStats();
+ List<BlockCacheStats> getBlockCacheStats();
/**
- * Keeps track of stats for a Block Cache table.
+ * Get the name of the table.
+ *
+ * @return this table's name.
*/
- class DfsBlockCacheStats {
- /**
- * Number of times a block was found in the cache, per pack file
- * extension.
- */
- private final AtomicReference<AtomicLong[]> statHit;
+ String getName();
+
+ /**
+ * Provides methods used with Block Cache statistics.
+ */
+ interface BlockCacheStats {
/**
- * Number of times a block was not found, and had to be loaded, per pack
- * file extension.
- */
- private final AtomicReference<AtomicLong[]> statMiss;
-
- /**
- * Number of blocks evicted due to cache being full, per pack file
- * extension.
- */
- private final AtomicReference<AtomicLong[]> statEvict;
-
- /**
- * Number of bytes currently loaded in the cache, per pack file
- * extension.
- */
- private final AtomicReference<AtomicLong[]> liveBytes;
-
- DfsBlockCacheStats() {
- statHit = new AtomicReference<>(newCounters());
- statMiss = new AtomicReference<>(newCounters());
- statEvict = new AtomicReference<>(newCounters());
- liveBytes = new AtomicReference<>(newCounters());
- }
-
- /**
- * Increment the {@code statHit} count.
+ * Get the name of the block cache generating this instance.
*
- * @param key
- * key identifying which liveBytes entry to update.
+ * @return this cache's name.
*/
- void incrementHit(DfsStreamKey key) {
- getStat(statHit, key).incrementAndGet();
- }
-
- /**
- * Increment the {@code statMiss} count.
- *
- * @param key
- * key identifying which liveBytes entry to update.
- */
- void incrementMiss(DfsStreamKey key) {
- getStat(statMiss, key).incrementAndGet();
- }
-
- /**
- * Increment the {@code statEvict} count.
- *
- * @param key
- * key identifying which liveBytes entry to update.
- */
- void incrementEvict(DfsStreamKey key) {
- getStat(statEvict, key).incrementAndGet();
- }
-
- /**
- * Add {@code size} to the {@code liveBytes} count.
- *
- * @param key
- * key identifying which liveBytes entry to update.
- * @param size
- * amount to increment the count by.
- */
- void addToLiveBytes(DfsStreamKey key, long size) {
- getStat(liveBytes, key).addAndGet(size);
- }
+ String getName();
/**
* Get total number of bytes in the cache, per pack file extension.
*
* @return total number of bytes in the cache, per pack file extension.
*/
- long[] getCurrentSize() {
- return getStatVals(liveBytes);
- }
+ long[] getCurrentSize();
/**
* Get number of requests for items in the cache, per pack file
@@ -230,9 +171,7 @@ void addToLiveBytes(DfsStreamKey key, long size) {
* @return the number of requests for items in the cache, per pack file
* extension.
*/
- long[] getHitCount() {
- return getStatVals(statHit);
- }
+ long[] getHitCount();
/**
* Get number of requests for items not in the cache, per pack file
@@ -241,9 +180,7 @@ void addToLiveBytes(DfsStreamKey key, long size) {
* @return the number of requests for items not in the cache, per pack
* file extension.
*/
- long[] getMissCount() {
- return getStatVals(statMiss);
- }
+ long[] getMissCount();
/**
* Get total number of requests (hit + miss), per pack file extension.
@@ -251,42 +188,14 @@ void addToLiveBytes(DfsStreamKey key, long size) {
* @return total number of requests (hit + miss), per pack file
* extension.
*/
- long[] getTotalRequestCount() {
- AtomicLong[] hit = statHit.get();
- AtomicLong[] miss = statMiss.get();
- long[] cnt = new long[Math.max(hit.length, miss.length)];
- for (int i = 0; i < hit.length; i++) {
- cnt[i] += hit[i].get();
- }
- for (int i = 0; i < miss.length; i++) {
- cnt[i] += miss[i].get();
- }
- return cnt;
- }
+ long[] getTotalRequestCount();
/**
* Get hit ratios.
*
* @return hit ratios.
*/
- long[] getHitRatio() {
- AtomicLong[] hit = statHit.get();
- AtomicLong[] miss = statMiss.get();
- long[] ratio = new long[Math.max(hit.length, miss.length)];
- for (int i = 0; i < ratio.length; i++) {
- if (i >= hit.length) {
- ratio[i] = 0;
- } else if (i >= miss.length) {
- ratio[i] = 100;
- } else {
- long hitVal = hit[i].get();
- long missVal = miss[i].get();
- long total = hitVal + missVal;
- ratio[i] = total == 0 ? 0 : hitVal * 100 / total;
- }
- }
- return ratio;
- }
+ long[] getHitRatio();
/**
* Get number of evictions performed due to cache being full, per pack
@@ -295,46 +204,6 @@ void addToLiveBytes(DfsStreamKey key, long size) {
* @return the number of evictions performed due to cache being full,
* per pack file extension.
*/
- long[] getEvictions() {
- return getStatVals(statEvict);
- }
-
- private static AtomicLong[] newCounters() {
- AtomicLong[] ret = new AtomicLong[PackExt.values().length];
- for (int i = 0; i < ret.length; i++) {
- ret[i] = new AtomicLong();
- }
- return ret;
- }
-
- private static long[] getStatVals(AtomicReference<AtomicLong[]> stat) {
- AtomicLong[] stats = stat.get();
- long[] cnt = new long[stats.length];
- for (int i = 0; i < stats.length; i++) {
- cnt[i] = stats[i].get();
- }
- return cnt;
- }
-
- private static AtomicLong getStat(AtomicReference<AtomicLong[]> stats,
- DfsStreamKey key) {
- int pos = key.packExtPos;
- while (true) {
- AtomicLong[] vals = stats.get();
- if (pos < vals.length) {
- return vals[pos];
- }
- AtomicLong[] expect = vals;
- vals = new AtomicLong[Math.max(pos + 1,
- PackExt.values().length)];
- System.arraycopy(expect, 0, vals, 0, expect.length);
- for (int i = expect.length; i < vals.length; i++) {
- vals[i] = new AtomicLong();
- }
- if (stats.compareAndSet(expect, vals)) {
- return vals[pos];
- }
- }
- }
+ long[] getEvictions();
}
-}
\ No newline at end of file
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java
index a177669..199481c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java
@@ -18,13 +18,13 @@
import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.UNREACHABLE_GARBAGE;
import static org.eclipse.jgit.internal.storage.dfs.DfsPackCompactor.configureReftable;
import static org.eclipse.jgit.internal.storage.pack.PackExt.COMMIT_GRAPH;
-import static org.eclipse.jgit.internal.storage.pack.PackExt.INDEX;
import static org.eclipse.jgit.internal.storage.pack.PackExt.OBJECT_SIZE_INDEX;
import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
import static org.eclipse.jgit.internal.storage.pack.PackExt.REFTABLE;
import static org.eclipse.jgit.internal.storage.pack.PackWriter.NONE;
import java.io.IOException;
+import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
@@ -90,7 +90,7 @@ public class DfsGarbageCollector {
private long coalesceGarbageLimit = 50 << 20;
private long garbageTtlMillis = TimeUnit.DAYS.toMillis(1);
- private long startTimeMillis;
+ private Instant startTime;
private List<DfsPackFile> packsBefore;
private List<DfsReftable> reftablesBefore;
private List<DfsPackFile> expiredGarbagePacks;
@@ -100,6 +100,7 @@ public class DfsGarbageCollector {
private Set<ObjectId> allTags;
private Set<ObjectId> nonHeads;
private Set<ObjectId> tagTargets;
+ private Instant refLogExpire;
/**
* Initialize a garbage collector.
@@ -200,6 +201,22 @@ public DfsGarbageCollector setReftableInitialMinUpdateIndex(long u) {
return this;
}
+
+ /**
+ * Set time limit to the reflog history.
+ * <p>
+ * Garbage Collector prunes entries from reflog history older than {@code refLogExpire}
+ * <p>
+ *
+ * @param refLogExpire
+ * instant in time which defines refLog expiration
+ * @return {@code this}
+ */
+ public DfsGarbageCollector setRefLogExpire(Instant refLogExpire) {
+ this.refLogExpire = refLogExpire;
+ return this;
+ }
+
/**
* Set maxUpdateIndex for the initial reftable created during conversion.
*
@@ -335,7 +352,7 @@ public boolean pack(ProgressMonitor pm) throws IOException {
throw new IllegalStateException(
JGitText.get().supportOnlyPackIndexVersion2);
- startTimeMillis = SystemReader.getInstance().getCurrentTime();
+ startTime = SystemReader.getInstance().now();
ctx = objdb.newReader();
try {
refdb.refresh();
@@ -418,7 +435,7 @@ private void readPacksBefore() throws IOException {
packsBefore = new ArrayList<>(packs.length);
expiredGarbagePacks = new ArrayList<>(packs.length);
- long now = SystemReader.getInstance().getCurrentTime();
+ long now = SystemReader.getInstance().now().toEpochMilli();
for (DfsPackFile p : packs) {
DfsPackDescription d = p.getPackDescription();
if (d.getPackSource() != UNREACHABLE_GARBAGE) {
@@ -687,14 +704,7 @@ private DfsPackDescription writePack(PackSource source, PackWriter pw,
pack.setBlockSize(PACK, out.blockSize());
}
- try (DfsOutputStream out = objdb.writeFile(pack, INDEX)) {
- CountingOutputStream cnt = new CountingOutputStream(out);
- pw.writeIndex(cnt);
- pack.addFileExt(INDEX);
- pack.setFileSize(INDEX, cnt.getCount());
- pack.setBlockSize(INDEX, out.blockSize());
- pack.setIndexVersion(pw.getIndexVersion());
- }
+ pw.writeIndex(objdb.getPackIndexWriter(pack, pw.getIndexVersion()));
if (source != UNREACHABLE_GARBAGE && packConfig.getMinBytesForObjSizeIndex() >= 0) {
try (DfsOutputStream out = objdb.writeFile(pack,
@@ -713,7 +723,7 @@ private DfsPackDescription writePack(PackSource source, PackWriter pw,
PackStatistics stats = pw.getStatistics();
pack.setPackStats(stats);
- pack.setLastModified(startTimeMillis);
+ pack.setLastModified(startTime.toEpochMilli());
newPackDesc.add(pack);
newPackStats.add(stats);
newPackObj.add(pw.getObjectSet());
@@ -741,6 +751,10 @@ private void writeReftable(DfsPackDescription pack) throws IOException {
compact.addAll(stack.readers());
compact.setIncludeDeletes(includeDeletes);
compact.setConfig(configureReftable(reftableConfig, out));
+ if(refLogExpire != null ){
+ compact.setReflogExpireOldestReflogTimeMillis(
+ refLogExpire.toEpochMilli());
+ }
compact.compact();
pack.addFileExt(REFTABLE);
pack.setReftableStats(compact.getStats());
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsInserter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsInserter.java
index a07d841..16315bf 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsInserter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsInserter.java
@@ -41,12 +41,13 @@
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.LargeObjectException;
import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.file.BasePackIndexWriter;
import org.eclipse.jgit.internal.storage.file.PackIndex;
-import org.eclipse.jgit.internal.storage.file.PackIndexWriter;
import org.eclipse.jgit.internal.storage.file.PackObjectSizeIndexWriter;
import org.eclipse.jgit.internal.storage.pack.PackExt;
import org.eclipse.jgit.lib.AbbreviatedObjectId;
import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdOwnerMap;
@@ -54,7 +55,6 @@
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.ObjectStream;
-import org.eclipse.jgit.storage.pack.PackConfig;
import org.eclipse.jgit.transport.PackedObjectInfo;
import org.eclipse.jgit.util.BlockList;
import org.eclipse.jgit.util.IO;
@@ -71,6 +71,8 @@ public class DfsInserter extends ObjectInserter {
private static final int INDEX_VERSION = 2;
final DfsObjDatabase db;
+
+ private final int minBytesForObjectSizeIndex;
int compression = Deflater.BEST_COMPRESSION;
List<PackedObjectInfo> objectList;
@@ -83,8 +85,6 @@ public class DfsInserter extends ObjectInserter {
private boolean rollback;
private boolean checkExisting = true;
- private int minBytesForObjectSizeIndex = -1;
-
/**
* Initialize a new inserter.
*
@@ -93,8 +93,9 @@ public class DfsInserter extends ObjectInserter {
*/
protected DfsInserter(DfsObjDatabase db) {
this.db = db;
- PackConfig pc = new PackConfig(db.getRepository().getConfig());
- this.minBytesForObjectSizeIndex = pc.getMinBytesForObjSizeIndex();
+ this.minBytesForObjectSizeIndex = db.getRepository().getConfig().getInt(
+ ConfigConstants.CONFIG_PACK_SECTION,
+ ConfigConstants.CONFIG_KEY_MIN_BYTES_OBJ_SIZE_INDEX, -1);
}
/**
@@ -112,21 +113,6 @@ public void checkExisting(boolean check) {
void setCompressionLevel(int compression) {
this.compression = compression;
}
-
- /**
- * Set minimum size for an object to be included in the object size index.
- *
- * <p>
- * Use 0 for all and -1 for nothing (the pack won't have object size index).
- *
- * @param minBytes
- * only objects with size bigger or equal to this are included in
- * the index.
- */
- protected void setMinBytesForObjectSizeIndex(int minBytes) {
- this.minBytesForObjectSizeIndex = minBytes;
- }
-
@Override
public DfsPackParser newPackParser(InputStream in) throws IOException {
return new DfsPackParser(db, this, in);
@@ -333,7 +319,7 @@ PackIndex writePackIndex(DfsPackDescription pack, byte[] packHash,
private static void index(OutputStream out, byte[] packHash,
List<PackedObjectInfo> list) throws IOException {
- PackIndexWriter.createVersion(out, INDEX_VERSION).write(list, packHash);
+ BasePackIndexWriter.createVersion(out, INDEX_VERSION).write(list, packHash);
}
void writeObjectSizeIndex(DfsPackDescription pack,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java
index 616563f..efd666f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java
@@ -12,6 +12,7 @@
import static java.util.stream.Collectors.joining;
import static org.eclipse.jgit.internal.storage.pack.PackExt.BITMAP_INDEX;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.INDEX;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -27,7 +28,9 @@
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
+import org.eclipse.jgit.internal.storage.file.BasePackIndexWriter;
import org.eclipse.jgit.internal.storage.file.PackBitmapIndexWriterV1;
+import org.eclipse.jgit.internal.storage.pack.PackIndexWriter;
import org.eclipse.jgit.internal.storage.pack.PackBitmapIndexWriter;
import org.eclipse.jgit.internal.storage.pack.PackExt;
import org.eclipse.jgit.lib.AnyObjectId;
@@ -771,4 +774,35 @@ public PackBitmapIndexWriter getPackBitmapIndexWriter(
}
};
}
+
+ /**
+ * Returns a writer to store the pack index in this object database.
+ *
+ * @param pack
+ * Pack file to which the index is associated.
+ * @param indexVersion
+ * which version of the index to write
+ * @return a writer to store the index associated with the pack
+ * @throws IOException
+ * when some I/O problem occurs while creating or writing to
+ * output stream
+ */
+ public PackIndexWriter getPackIndexWriter(
+ DfsPackDescription pack, int indexVersion)
+ throws IOException {
+ return (objectsToStore, packDataChecksum) -> {
+ try (DfsOutputStream out = writeFile(pack, INDEX);
+ CountingOutputStream cnt = new CountingOutputStream(out)) {
+ final PackIndexWriter iw = BasePackIndexWriter
+ .createVersion(cnt,
+ indexVersion);
+ iw.write(objectsToStore, packDataChecksum);
+ pack.addFileExt(INDEX);
+ pack.setFileSize(INDEX, cnt.getCount());
+ pack.setBlockSize(INDEX, out.blockSize());
+ pack.setIndexVersion(indexVersion);
+ }
+ };
+ }
+
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java
index 86144b3..f9c01b9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java
@@ -12,7 +12,7 @@
import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.COMPACT;
import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.GC;
-import static org.eclipse.jgit.internal.storage.pack.PackExt.INDEX;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.OBJECT_SIZE_INDEX;
import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
import static org.eclipse.jgit.internal.storage.pack.PackExt.REFTABLE;
import static org.eclipse.jgit.internal.storage.pack.StoredObjectRepresentation.PACK_DELTA;
@@ -249,6 +249,7 @@ private void compactPacks(DfsReader ctx, ProgressMonitor pm)
try {
writePack(objdb, outDesc, pw, pm);
writeIndex(objdb, outDesc, pw);
+ writeObjectSizeIndex(objdb, outDesc, pw);
PackStatistics stats = pw.getStatistics();
@@ -458,13 +459,20 @@ private static void writePack(DfsObjDatabase objdb,
private static void writeIndex(DfsObjDatabase objdb,
DfsPackDescription pack,
PackWriter pw) throws IOException {
- try (DfsOutputStream out = objdb.writeFile(pack, INDEX)) {
+ pw.writeIndex(objdb.getPackIndexWriter(pack, pw.getIndexVersion()));
+ }
+
+ private static void writeObjectSizeIndex(DfsObjDatabase objdb,
+ DfsPackDescription pack,
+ PackWriter pw) throws IOException {
+ try (DfsOutputStream out = objdb.writeFile(pack, OBJECT_SIZE_INDEX)) {
CountingOutputStream cnt = new CountingOutputStream(out);
- pw.writeIndex(cnt);
- pack.addFileExt(INDEX);
- pack.setFileSize(INDEX, cnt.getCount());
- pack.setBlockSize(INDEX, out.blockSize());
- pack.setIndexVersion(pw.getIndexVersion());
+ pw.writeObjectSizeIndex(cnt);
+ if (cnt.getCount() > 0) {
+ pack.addFileExt(OBJECT_SIZE_INDEX);
+ pack.setFileSize(OBJECT_SIZE_INDEX, cnt.getCount());
+ pack.setBlockSize(OBJECT_SIZE_INDEX, out.blockSize());
+ }
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java
index 5cc2a57..48ed47a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java
@@ -29,6 +29,7 @@
import java.text.MessageFormat;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
import java.util.zip.CRC32;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
@@ -106,6 +107,53 @@ public final class DfsPackFile extends BlockBasedFile {
/** Lock for {@link #corruptObjects}. */
private final Object corruptObjectsLock = new Object();
+ private final IndexFactory indexFactory;
+
+ /**
+ * Returns the indexes for this pack.
+ * <p>
+ * We define indexes in different sub interfaces to allow implementing the
+ * indexes over different combinations of backends.
+ * <p>
+ * Implementations decide if/how to cache the indexes. The calling
+ * DfsPackFile will keep the reference to the index as long as it needs it.
+ */
+ public interface IndexFactory {
+ /**
+ * Take care of loading the primary and reverse indexes for this pack.
+ */
+ interface PackIndexes {
+ /**
+ * Load the primary index for the pack.
+ *
+ * @param ctx
+ * reader to find the raw bytes
+ * @return a primary index
+ * @throws IOException
+ * a problem finding/parsing the index
+ */
+ PackIndex index(DfsReader ctx) throws IOException;
+
+ /**
+ * Load the reverse index of the pack
+ *
+ * @param ctx
+ * reader to find the raw bytes
+ * @return the reverse index of the pack
+ * @throws IOException
+ * a problem finding/parsing the reverse index
+ */
+ PackReverseIndex reverseIndex(DfsReader ctx) throws IOException;
+ }
+
+ /**
+ * Returns a provider of the primary and reverse indexes of this pack
+ *
+ * @return an implementation of the {@link PackIndexes} interface
+ */
+ PackIndexes getPackIndexes();
+ }
+
/**
* Construct a reader for an existing, packfile.
*
@@ -115,7 +163,8 @@ public final class DfsPackFile extends BlockBasedFile {
* description of the pack within the DFS.
*/
DfsPackFile(DfsBlockCache cache, DfsPackDescription desc) {
- this(cache, desc, DEFAULT_BITMAP_LOADER);
+ this(cache, desc, DEFAULT_BITMAP_LOADER,
+ new CachedStreamIndexFactory(cache, desc));
}
/**
@@ -127,9 +176,11 @@ public final class DfsPackFile extends BlockBasedFile {
* description of the pack within the DFS
* @param bitmapLoader
* loader to get the bitmaps of this pack (if any)
+ * @param indexFactory
+ * an IndexFactory to get references to the indexes of this pack
*/
public DfsPackFile(DfsBlockCache cache, DfsPackDescription desc,
- PackBitmapIndexLoader bitmapLoader) {
+ PackBitmapIndexLoader bitmapLoader, IndexFactory indexFactory) {
super(cache, desc, PACK);
int bs = desc.getBlockSize(PACK);
@@ -141,6 +192,7 @@ public DfsPackFile(DfsBlockCache cache, DfsPackDescription desc,
length = sz > 0 ? sz : -1;
this.bitmapLoader = bitmapLoader;
+ this.indexFactory = indexFactory;
}
/**
@@ -195,19 +247,10 @@ private PackIndex idx(DfsReader ctx) throws IOException {
Repository.getGlobalListenerList()
.dispatch(new BeforeDfsPackIndexLoadedEvent(this));
try {
- DfsStreamKey idxKey = desc.getStreamKey(INDEX);
- AtomicBoolean cacheHit = new AtomicBoolean(true);
- DfsBlockCache.Ref<PackIndex> idxref = cache.getOrLoadRef(idxKey,
- REF_POSITION, () -> {
- cacheHit.set(false);
- return loadPackIndex(ctx, idxKey);
- });
- if (cacheHit.get()) {
- ctx.stats.idxCacheHit++;
- }
- PackIndex idx = idxref.get();
- if (index == null && idx != null) {
- index = idx;
+ index = indexFactory.getPackIndexes().index(ctx);
+ if (index == null) {
+ throw new IOException(
+ "Couldn't get a reference to the primary index"); //$NON-NLS-1$
}
ctx.emitIndexLoad(desc, INDEX, index);
return index;
@@ -321,20 +364,10 @@ public PackReverseIndex getReverseIdx(DfsReader ctx) throws IOException {
return reverseIndex;
}
- PackIndex idx = idx(ctx);
- DfsStreamKey revKey = desc.getStreamKey(REVERSE_INDEX);
- AtomicBoolean cacheHit = new AtomicBoolean(true);
- DfsBlockCache.Ref<PackReverseIndex> revref = cache.getOrLoadRef(revKey,
- REF_POSITION, () -> {
- cacheHit.set(false);
- return loadReverseIdx(ctx, revKey, idx);
- });
- if (cacheHit.get()) {
- ctx.stats.ridxCacheHit++;
- }
- PackReverseIndex revidx = revref.get();
- if (reverseIndex == null && revidx != null) {
- reverseIndex = revidx;
+ reverseIndex = indexFactory.getPackIndexes().reverseIndex(ctx);
+ if (reverseIndex == null) {
+ throw new IOException(
+ "Couldn't get a reference to the reverse index"); //$NON-NLS-1$
}
ctx.emitIndexLoad(desc, REVERSE_INDEX, reverseIndex);
return reverseIndex;
@@ -347,6 +380,7 @@ private PackObjectSizeIndex getObjectSizeIndex(DfsReader ctx)
}
if (objectSizeIndexLoadAttempted
+ || !ctx.getOptions().shouldUseObjectSizeIndex()
|| !desc.hasFileExt(OBJECT_SIZE_INDEX)) {
// Pack doesn't have object size index
return null;
@@ -1210,48 +1244,6 @@ private void setCorrupt(long offset) {
}
}
- private DfsBlockCache.Ref<PackIndex> loadPackIndex(
- DfsReader ctx, DfsStreamKey idxKey) throws IOException {
- try {
- ctx.stats.readIdx++;
- long start = System.nanoTime();
- try (ReadableChannel rc = ctx.db.openFile(desc, INDEX)) {
- PackIndex idx = PackIndex.read(alignTo8kBlocks(rc));
- ctx.stats.readIdxBytes += rc.position();
- index = idx;
- return new DfsBlockCache.Ref<>(
- idxKey,
- REF_POSITION,
- idx.getObjectCount() * REC_SIZE,
- idx);
- } finally {
- ctx.stats.readIdxMicros += elapsedMicros(start);
- }
- } catch (EOFException e) {
- throw new IOException(MessageFormat.format(
- DfsText.get().shortReadOfIndex,
- desc.getFileName(INDEX)), e);
- } catch (IOException e) {
- throw new IOException(MessageFormat.format(
- DfsText.get().cannotReadIndex,
- desc.getFileName(INDEX)), e);
- }
- }
-
- private DfsBlockCache.Ref<PackReverseIndex> loadReverseIdx(
- DfsReader ctx, DfsStreamKey revKey, PackIndex idx) {
- ctx.stats.readReverseIdx++;
- long start = System.nanoTime();
- PackReverseIndex revidx = PackReverseIndexFactory.computeFromIndex(idx);
- reverseIndex = revidx;
- ctx.stats.readReverseIdxMicros += elapsedMicros(start);
- return new DfsBlockCache.Ref<>(
- revKey,
- REF_POSITION,
- idx.getObjectCount() * 8,
- revidx);
- }
-
private DfsBlockCache.Ref<PackObjectSizeIndex> loadObjectSizeIndex(
DfsReader ctx, DfsStreamKey objectSizeIndexKey) throws IOException {
ctx.stats.readObjectSizeIndex++;
@@ -1288,9 +1280,12 @@ private DfsBlockCache.Ref<PackObjectSizeIndex> loadObjectSizeIndex(
private DfsBlockCache.Ref<PackBitmapIndex> loadBitmapIndex(DfsReader ctx,
DfsStreamKey bitmapKey) throws IOException {
ctx.stats.readBitmap++;
+ long start = System.nanoTime();
PackBitmapIndexLoader.LoadResult result = bitmapLoader
.loadPackBitmapIndex(ctx, this);
bitmapIndex = result.bitmapIndex;
+ ctx.stats.readBitmapIdxBytes += result.bytesRead;
+ ctx.stats.readBitmapIdxMicros += elapsedMicros(start);
return new DfsBlockCache.Ref<>(bitmapKey, REF_POSITION,
result.bytesRead, result.bitmapIndex);
}
@@ -1449,4 +1444,141 @@ public LoadResult loadPackBitmapIndex(DfsReader ctx, DfsPackFile pack)
}
}
}
+
+ /**
+ * An index factory backed by Dfs streams and references cached in
+ * DfsBlockCache
+ */
+ public static final class CachedStreamIndexFactory implements IndexFactory {
+ private final CachedStreamPackIndexes indexes;
+
+ /**
+ * An index factory
+ *
+ * @param cache
+ * DFS block cache to use for the references
+ * @param desc
+ * This factory loads indexes for this package
+ */
+ public CachedStreamIndexFactory(DfsBlockCache cache,
+ DfsPackDescription desc) {
+ this.indexes = new CachedStreamPackIndexes(cache, desc);
+ }
+
+ @Override
+ public PackIndexes getPackIndexes() {
+ return indexes;
+ }
+ }
+
+ /**
+ * Load primary and reverse index from Dfs streams and cache the references
+ * in DfsBlockCache.
+ */
+ public static final class CachedStreamPackIndexes implements IndexFactory.PackIndexes {
+ private final DfsBlockCache cache;
+
+ private final DfsPackDescription desc;
+
+ /**
+ * An index factory
+ *
+ * @param cache
+ * DFS block cache to use for the references
+ * @param desc This factory loads indexes for this package
+ */
+ public CachedStreamPackIndexes(DfsBlockCache cache,
+ DfsPackDescription desc) {
+ this.cache = cache;
+ this.desc = desc;
+ }
+
+ @Override
+ public PackIndex index(DfsReader ctx) throws IOException {
+ DfsStreamKey idxKey = desc.getStreamKey(INDEX);
+ // Keep the value parsed in the loader, in case the Ref<> is
+ // nullified in ClockBlockCacheTable#reserveSpace
+ // before we read its value.
+ AtomicReference<PackIndex> loadedRef = new AtomicReference<>(null);
+ DfsBlockCache.Ref<PackIndex> cachedRef = cache.getOrLoadRef(idxKey,
+ REF_POSITION, () -> {
+ RefWithSize<PackIndex> idx = loadPackIndex(ctx, desc);
+ loadedRef.set(idx.ref);
+ return new DfsBlockCache.Ref<>(idxKey, REF_POSITION,
+ idx.size, idx.ref);
+ });
+ if (loadedRef.get() == null) {
+ ctx.stats.idxCacheHit++;
+ }
+ return cachedRef.get() != null ? cachedRef.get() : loadedRef.get();
+ }
+
+ private static RefWithSize<PackIndex> loadPackIndex(DfsReader ctx,
+ DfsPackDescription desc) throws IOException {
+ try {
+ ctx.stats.readIdx++;
+ long start = System.nanoTime();
+ try (ReadableChannel rc = ctx.db.openFile(desc, INDEX)) {
+ PackIndex idx = PackIndex.read(alignTo8kBlocks(rc));
+ ctx.stats.readIdxBytes += rc.position();
+ return new RefWithSize<>(idx,
+ idx.getObjectCount() * REC_SIZE);
+ } finally {
+ ctx.stats.readIdxMicros += elapsedMicros(start);
+ }
+ } catch (EOFException e) {
+ throw new IOException(
+ MessageFormat.format(DfsText.get().shortReadOfIndex,
+ desc.getFileName(INDEX)),
+ e);
+ } catch (IOException e) {
+ throw new IOException(
+ MessageFormat.format(DfsText.get().cannotReadIndex,
+ desc.getFileName(INDEX)),
+ e);
+ }
+ }
+
+ @Override
+ public PackReverseIndex reverseIndex(DfsReader ctx) throws IOException {
+ PackIndex idx = index(ctx);
+ DfsStreamKey revKey = desc.getStreamKey(REVERSE_INDEX);
+ // Keep the value parsed in the loader, in case the Ref<> is
+ // nullified in ClockBlockCacheTable#reserveSpace
+ // before we read its value.
+ AtomicReference<PackReverseIndex> loadedRef = new AtomicReference<>(
+ null);
+ DfsBlockCache.Ref<PackReverseIndex> cachedRef = cache
+ .getOrLoadRef(revKey, REF_POSITION, () -> {
+ RefWithSize<PackReverseIndex> ridx = loadReverseIdx(ctx,
+ idx);
+ loadedRef.set(ridx.ref);
+ return new DfsBlockCache.Ref<>(revKey, REF_POSITION,
+ ridx.size, ridx.ref);
+ });
+ if (loadedRef.get() == null) {
+ ctx.stats.ridxCacheHit++;
+ }
+ return cachedRef.get() != null ? cachedRef.get() : loadedRef.get();
+ }
+
+ private static RefWithSize<PackReverseIndex> loadReverseIdx(
+ DfsReader ctx, PackIndex idx) {
+ ctx.stats.readReverseIdx++;
+ long start = System.nanoTime();
+ PackReverseIndex revidx = PackReverseIndexFactory
+ .computeFromIndex(idx);
+ ctx.stats.readReverseIdxMicros += elapsedMicros(start);
+ return new RefWithSize<>(revidx, idx.getObjectCount() * 8);
+ }
+ }
+
+ private static final class RefWithSize<V> {
+ final V ref;
+ final long size;
+ RefWithSize(V ref, long size) {
+ this.ref = ref;
+ this.size = size;
+ }
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java
index c939114..62f6753 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java
@@ -307,7 +307,7 @@ private static class FoundObject<T extends ObjectId> {
private <T extends ObjectId> Iterable<FoundObject<T>> findAll(
Iterable<T> objectIds) throws IOException {
- Collection<T> pending = new ArrayList<>();
+ HashSet<T> pending = new HashSet<>();
for (T id : objectIds) {
pending.add(id);
}
@@ -327,22 +327,21 @@ private <T extends ObjectId> Iterable<FoundObject<T>> findAll(
}
private <T extends ObjectId> void findAllImpl(PackList packList,
- Collection<T> pending, List<FoundObject<T>> r) {
+ HashSet<T> pending, List<FoundObject<T>> r) {
DfsPackFile[] packs = packList.packs;
if (packs.length == 0) {
return;
}
int lastIdx = 0;
DfsPackFile lastPack = packs[lastIdx];
-
- OBJECT_SCAN: for (Iterator<T> it = pending.iterator(); it.hasNext();) {
- T t = it.next();
+ HashSet<T> toRemove = new HashSet<>();
+ OBJECT_SCAN: for (T t : pending) {
if (!skipGarbagePack(lastPack)) {
try {
long p = lastPack.findOffset(this, t);
if (0 < p) {
r.add(new FoundObject<>(t, lastIdx, lastPack, p));
- it.remove();
+ toRemove.add(t);
continue;
}
} catch (IOException e) {
@@ -360,7 +359,7 @@ private <T extends ObjectId> void findAllImpl(PackList packList,
long p = pack.findOffset(this, t);
if (0 < p) {
r.add(new FoundObject<>(t, i, pack, p));
- it.remove();
+ toRemove.add(t);
lastIdx = i;
lastPack = pack;
continue OBJECT_SCAN;
@@ -370,6 +369,7 @@ private <T extends ObjectId> void findAllImpl(PackList packList,
}
}
}
+ pending.removeAll(toRemove);
last = lastPack;
}
@@ -511,18 +511,15 @@ public long getObjectSize(AnyObjectId objectId, int typeHint)
throw new MissingObjectException(objectId.copy(), typeHint);
}
- if (typeHint != Constants.OBJ_BLOB || !pack.hasObjectSizeIndex(this)) {
+ if (typeHint != Constants.OBJ_BLOB || !safeHasObjectSizeIndex(pack)) {
return pack.getObjectSize(this, objectId);
}
- long sz = pack.getIndexedObjectSize(this, objectId);
+ Optional<Long> maybeSz = safeGetIndexedObjectSize(pack, objectId);
+ long sz = maybeSz.orElse(-1L);
if (sz >= 0) {
- stats.objectSizeIndexHit += 1;
return sz;
}
-
- // Object wasn't in the index
- stats.objectSizeIndexMiss += 1;
return pack.getObjectSize(this, objectId);
}
@@ -541,23 +538,61 @@ public boolean isNotLargerThan(AnyObjectId objectId, int typeHint,
}
stats.isNotLargerThanCallCount += 1;
- if (typeHint != Constants.OBJ_BLOB || !pack.hasObjectSizeIndex(this)) {
+ if (typeHint != Constants.OBJ_BLOB || !safeHasObjectSizeIndex(pack)) {
return pack.getObjectSize(this, objectId) <= limit;
}
- long sz = pack.getIndexedObjectSize(this, objectId);
+ Optional<Long> maybeSz = safeGetIndexedObjectSize(pack, objectId);
+ if (maybeSz.isEmpty()) {
+ // Exception in object size index
+ return pack.getObjectSize(this, objectId) <= limit;
+ }
+
+ long sz = maybeSz.get();
+ if (sz >= 0) {
+ return sz <= limit;
+ }
+
+ if (isLimitInsideIndexThreshold(pack, limit)) {
+ // With threshold T, not-found means object < T
+ // If limit L > T, then object < T < L
+ return true;
+ }
+
+ return pack.getObjectSize(this, objectId) <= limit;
+ }
+
+ private boolean safeHasObjectSizeIndex(DfsPackFile pack) {
+ try {
+ return pack.hasObjectSizeIndex(this);
+ } catch (IOException e) {
+ return false;
+ }
+ }
+
+ private Optional<Long> safeGetIndexedObjectSize(DfsPackFile pack,
+ AnyObjectId objectId) {
+ long sz;
+ try {
+ sz = pack.getIndexedObjectSize(this, objectId);
+ } catch (IOException e) {
+ // Do not count the exception as an index miss
+ return Optional.empty();
+ }
if (sz < 0) {
stats.objectSizeIndexMiss += 1;
} else {
stats.objectSizeIndexHit += 1;
}
+ return Optional.of(sz);
+ }
- // Got size from index or we didn't but we are sure it should be there.
- if (sz >= 0 || pack.getObjectSizeIndexThreshold(this) <= limit) {
- return sz <= limit;
+ private boolean isLimitInsideIndexThreshold(DfsPackFile pack, long limit) {
+ try {
+ return pack.getObjectSizeIndexThreshold(this) <= limit;
+ } catch (IOException e) {
+ return false;
}
-
- return pack.getObjectSize(this, objectId) <= limit;
}
private DfsPackFile findPackWithObject(AnyObjectId objectId)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderIoStats.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderIoStats.java
index adb4673..fcfa3e0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderIoStats.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderIoStats.java
@@ -40,12 +40,12 @@ public static class Accumulator {
/** Total number of complete pack indexes read into memory. */
long readIdx;
- /** Total number of complete bitmap indexes read into memory. */
- long readBitmap;
-
/** Total number of reverse indexes added into memory. */
long readReverseIdx;
+ /** Total number of complete bitmap indexes read into memory. */
+ long readBitmap;
+
/** Total number of complete commit graphs read into memory. */
long readCommitGraph;
@@ -55,6 +55,9 @@ public static class Accumulator {
/** Total number of bytes read from pack indexes. */
long readIdxBytes;
+ /** Total number of bytes read from bitmap indexes. */
+ long readBitmapIdxBytes;
+
/** Total number of bytes read from commit graphs. */
long readCommitGraphBytes;
@@ -67,18 +70,15 @@ public static class Accumulator {
/** Total microseconds spent creating reverse indexes. */
long readReverseIdxMicros;
+ /** Total microseconds spent reading bitmap indexes. */
+ long readBitmapIdxMicros;
+
/** Total microseconds spent creating commit graphs. */
long readCommitGraphMicros;
/** Total microseconds spent creating object size indexes */
long readObjectSizeIndexMicros;
- /** Total number of bytes read from bitmap indexes. */
- long readBitmapIdxBytes;
-
- /** Total microseconds spent reading bitmap indexes. */
- long readBitmapIdxMicros;
-
/** Total number of block cache hits. */
long blockCacheHit;
@@ -195,15 +195,6 @@ public long getReadReverseIndexCount() {
}
/**
- * Get total number of times the commit graph read into memory.
- *
- * @return total number of commit graph read into memory.
- */
- public long getReadCommitGraphCount() {
- return stats.readCommitGraph;
- }
-
- /**
* Get total number of complete bitmap indexes read into memory.
*
* @return total number of complete bitmap indexes read into memory.
@@ -213,6 +204,15 @@ public long getReadBitmapIndexCount() {
}
/**
+ * Get total number of times the commit graph read into memory.
+ *
+ * @return total number of commit graph read into memory.
+ */
+ public long getReadCommitGraphCount() {
+ return stats.readCommitGraph;
+ }
+
+ /**
* Get total number of complete object size indexes read into memory.
*
* @return total number of complete object size indexes read into memory.
@@ -231,6 +231,15 @@ public long getReadIndexBytes() {
}
/**
+ * Get total number of bytes read from bitmap indexes.
+ *
+ * @return total number of bytes read from bitmap indexes.
+ */
+ public long getReadBitmapIndexBytes() {
+ return stats.readBitmapIdxBytes;
+ }
+
+ /**
* Get total number of bytes read from commit graphs.
*
* @return total number of bytes read from commit graphs.
@@ -240,6 +249,15 @@ public long getCommitGraphBytes() {
}
/**
+ * Get total number of bytes read from object size indexes.
+ *
+ * @return total number of bytes read from object size indexes.
+ */
+ public long getObjectSizeIndexBytes() {
+ return stats.readObjectSizeIndexBytes;
+ }
+
+ /**
* Get total microseconds spent reading pack indexes.
*
* @return total microseconds spent reading pack indexes.
@@ -258,6 +276,15 @@ public long getReadReverseIndexMicros() {
}
/**
+ * Get total microseconds spent reading bitmap indexes.
+ *
+ * @return total microseconds spent reading bitmap indexes.
+ */
+ public long getReadBitmapIndexMicros() {
+ return stats.readBitmapIdxMicros;
+ }
+
+ /**
* Get total microseconds spent reading commit graphs.
*
* @return total microseconds spent reading commit graphs.
@@ -267,21 +294,12 @@ public long getReadCommitGraphMicros() {
}
/**
- * Get total number of bytes read from bitmap indexes.
+ * Get total microseconds spent reading object size indexes.
*
- * @return total number of bytes read from bitmap indexes.
+ * @return total microseconds spent reading object size indexes.
*/
- public long getReadBitmapIndexBytes() {
- return stats.readBitmapIdxBytes;
- }
-
- /**
- * Get total microseconds spent reading bitmap indexes.
- *
- * @return total microseconds spent reading bitmap indexes.
- */
- public long getReadBitmapIndexMicros() {
- return stats.readBitmapIdxMicros;
+ public long getReadObjectSizeIndexMicros() {
+ return stats.readObjectSizeIndexMicros;
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderOptions.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderOptions.java
index f2ac461..c397469 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderOptions.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderOptions.java
@@ -13,8 +13,10 @@
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_CORE_SECTION;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_DFS_SECTION;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_DELTA_BASE_CACHE_LIMIT;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_LOAD_REV_INDEX_IN_PARALLEL;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_STREAM_BUFFER;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_STREAM_FILE_THRESHOLD;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_USE_OBJECT_SIZE_INDEX;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.storage.pack.PackConfig;
@@ -36,6 +38,8 @@ public class DfsReaderOptions {
private boolean loadRevIndexInParallel;
+ private boolean useObjectSizeIndex;
+
/**
* Create a default reader configuration.
*/
@@ -137,6 +141,28 @@ public DfsReaderOptions setLoadRevIndexInParallel(
}
/**
+ * Use the object size index if available.
+ *
+ * @return true if the reader should try to use the object size index. if
+ * false, the reader ignores that index.
+ */
+ public boolean shouldUseObjectSizeIndex() {
+ return useObjectSizeIndex;
+ }
+
+ /**
+ * Set if the reader should try to use the object size index
+ *
+ * @param useObjectSizeIndex true to use it, false to ignore the object size index
+ *
+ * @return {@code this}
+ */
+ public DfsReaderOptions setUseObjectSizeIndex(boolean useObjectSizeIndex) {
+ this.useObjectSizeIndex = useObjectSizeIndex;
+ return this;
+ }
+
+ /**
* Update properties by setting fields from the configuration.
* <p>
* If a property is not defined in the configuration, then it is left
@@ -168,6 +194,13 @@ public DfsReaderOptions fromConfig(Config rc) {
CONFIG_DFS_SECTION,
CONFIG_KEY_STREAM_BUFFER,
getStreamPackBufferSize()));
+
+ setUseObjectSizeIndex(rc.getBoolean(CONFIG_CORE_SECTION,
+ CONFIG_DFS_SECTION, CONFIG_KEY_USE_OBJECT_SIZE_INDEX,
+ false));
+ setLoadRevIndexInParallel(
+ rc.getBoolean(CONFIG_CORE_SECTION, CONFIG_DFS_SECTION,
+ CONFIG_KEY_LOAD_REV_INDEX_IN_PARALLEL, false));
return this;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftableDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftableDatabase.java
index 3ba74b2..2751cd2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftableDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftableDatabase.java
@@ -28,6 +28,7 @@
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.ReflogReader;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.ReceiveCommand;
import org.eclipse.jgit.util.RefList;
@@ -177,6 +178,11 @@ public List<Ref> getRefsByPrefixWithExclusions(String include, Set<String> exclu
}
@Override
+ public ReflogReader getReflogReader(Ref ref) throws IOException {
+ return reftableDatabase.getReflogReader(ref.getName());
+ }
+
+ @Override
public Set<Ref> getTipsWithSha1(ObjectId id) throws IOException {
if (!getReftableConfig().isIndexObjects()) {
return super.getTipsWithSha1(id);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/PackExtBlockCacheTable.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/PackExtBlockCacheTable.java
new file mode 100644
index 0000000..bb44f93
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/PackExtBlockCacheTable.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2024, Google LLC and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.eclipse.jgit.internal.storage.dfs;
+
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.dfs.DfsBlockCache.ReadableChannelSupplier;
+import org.eclipse.jgit.internal.storage.dfs.DfsBlockCache.Ref;
+import org.eclipse.jgit.internal.storage.dfs.DfsBlockCache.RefLoader;
+import org.eclipse.jgit.internal.storage.dfs.DfsBlockCacheConfig.DfsBlockCachePackExtConfig;
+import org.eclipse.jgit.internal.storage.pack.PackExt;
+
+/**
+ * A table that holds multiple cache tables accessed by {@link PackExt} types.
+ *
+ * <p>
+ * Allows the separation of entries from different {@link PackExt} types to
+ * limit churn in cache caused by entries of differing sizes.
+ * <p>
+ * Separating these tables enables the fine-tuning of cache tables per extension
+ * type.
+ */
+class PackExtBlockCacheTable implements DfsBlockCacheTable {
+ /**
+ * Table name.
+ */
+ private final String name;
+
+ private final DfsBlockCacheTable defaultBlockCacheTable;
+
+ // Holds the unique tables backing the extBlockCacheTables values.
+ private final List<DfsBlockCacheTable> blockCacheTableList;
+
+ // Holds the mapping of PackExt to DfsBlockCacheTables.
+ // The relation between the size of extBlockCacheTables entries and
+ // blockCacheTableList entries is:
+ // blockCacheTableList.size() <= extBlockCacheTables.size()
+ private final Map<PackExt, DfsBlockCacheTable> extBlockCacheTables;
+
+ /**
+ * Builds the PackExtBlockCacheTable from a list of
+ * {@link DfsBlockCachePackExtConfig}s.
+ *
+ * @param cacheConfig
+ * {@link DfsBlockCacheConfig} containing
+ * {@link DfsBlockCachePackExtConfig}s used to configure
+ * PackExtBlockCacheTable. The {@link DfsBlockCacheConfig} holds
+ * the configuration for the default cache table.
+ * @return the cache table built from the given configs.
+ * @throws IllegalArgumentException
+ * when no {@link DfsBlockCachePackExtConfig} exists in the
+ * {@link DfsBlockCacheConfig}.
+ */
+ static PackExtBlockCacheTable fromBlockCacheConfigs(
+ DfsBlockCacheConfig cacheConfig) {
+ DfsBlockCacheTable defaultTable = new ClockBlockCacheTable(cacheConfig);
+ Map<PackExt, DfsBlockCacheTable> packExtBlockCacheTables = new HashMap<>();
+ List<DfsBlockCachePackExtConfig> packExtConfigs = cacheConfig
+ .getPackExtCacheConfigurations();
+ if (packExtConfigs == null || packExtConfigs.size() == 0) {
+ throw new IllegalArgumentException(
+ JGitText.get().noPackExtConfigurationGiven);
+ }
+ for (DfsBlockCachePackExtConfig packExtCacheConfig : packExtConfigs) {
+ DfsBlockCacheTable table = new ClockBlockCacheTable(
+ packExtCacheConfig.getPackExtCacheConfiguration());
+ for (PackExt packExt : packExtCacheConfig.getPackExts()) {
+ if (packExtBlockCacheTables.containsKey(packExt)) {
+ throw new IllegalArgumentException(MessageFormat.format(
+ JGitText.get().duplicatePackExtensionsForCacheTables,
+ packExt));
+ }
+ packExtBlockCacheTables.put(packExt, table);
+ }
+ }
+ return fromCacheTables(defaultTable, packExtBlockCacheTables);
+ }
+
+ /**
+ * Creates a new PackExtBlockCacheTable from the combination of a default
+ * {@link DfsBlockCacheTable} and a map of {@link PackExt}s to
+ * {@link DfsBlockCacheTable}s.
+ * <p>
+ * This method allows for the PackExtBlockCacheTable to handle a mapping of
+ * {@link PackExt}s to arbitrarily defined {@link DfsBlockCacheTable}
+ * implementations. This is especially useful for users wishing to implement
+ * custom cache tables.
+ * <p>
+ * This is currently made visible for testing.
+ *
+ * @param defaultBlockCacheTable
+ * the default table used when a handling a {@link PackExt} type
+ * that does not map to a {@link DfsBlockCacheTable} mapped by
+ * packExtsCacheTablePairs.
+ * @param packExtBlockCacheTables
+ * the mapping of {@link PackExt}s to
+ * {@link DfsBlockCacheTable}s. A single
+ * {@link DfsBlockCacheTable} can be defined for multiple
+ * {@link PackExt}s in a many-to-one relationship.
+ * @return the PackExtBlockCacheTable created from the
+ * defaultBlockCacheTable and packExtsCacheTablePairs mapping.
+ * @throws IllegalArgumentException
+ * when a {@link PackExt} is defined for multiple
+ * {@link DfsBlockCacheTable}s.
+ */
+ static PackExtBlockCacheTable fromCacheTables(
+ DfsBlockCacheTable defaultBlockCacheTable,
+ Map<PackExt, DfsBlockCacheTable> packExtBlockCacheTables) {
+ Set<DfsBlockCacheTable> blockCacheTables = new HashSet<>();
+ blockCacheTables.add(defaultBlockCacheTable);
+ blockCacheTables.addAll(packExtBlockCacheTables.values());
+ String name = defaultBlockCacheTable.getName() + "," //$NON-NLS-1$
+ + packExtBlockCacheTables.values().stream()
+ .map(DfsBlockCacheTable::getName).sorted()
+ .collect(Collectors.joining(",")); //$NON-NLS-1$
+ return new PackExtBlockCacheTable(name, defaultBlockCacheTable,
+ List.copyOf(blockCacheTables), packExtBlockCacheTables);
+ }
+
+ private PackExtBlockCacheTable(String name,
+ DfsBlockCacheTable defaultBlockCacheTable,
+ List<DfsBlockCacheTable> blockCacheTableList,
+ Map<PackExt, DfsBlockCacheTable> extBlockCacheTables) {
+ this.name = name;
+ this.defaultBlockCacheTable = defaultBlockCacheTable;
+ this.blockCacheTableList = blockCacheTableList;
+ this.extBlockCacheTables = extBlockCacheTables;
+ }
+
+ @Override
+ public boolean hasBlock0(DfsStreamKey key) {
+ return getTable(key).hasBlock0(key);
+ }
+
+ @Override
+ public DfsBlock getOrLoad(BlockBasedFile file, long position,
+ DfsReader dfsReader, ReadableChannelSupplier fileChannel)
+ throws IOException {
+ return getTable(file.ext).getOrLoad(file, position, dfsReader,
+ fileChannel);
+ }
+
+ @Override
+ public <T> Ref<T> getOrLoadRef(DfsStreamKey key, long position,
+ RefLoader<T> loader) throws IOException {
+ return getTable(key).getOrLoadRef(key, position, loader);
+ }
+
+ @Override
+ public void put(DfsBlock v) {
+ getTable(v.stream).put(v);
+ }
+
+ @Override
+ public <T> Ref<T> put(DfsStreamKey key, long pos, long size, T v) {
+ return getTable(key).put(key, pos, size, v);
+ }
+
+ @Override
+ public <T> Ref<T> putRef(DfsStreamKey key, long size, T v) {
+ return getTable(key).putRef(key, size, v);
+ }
+
+ @Override
+ public boolean contains(DfsStreamKey key, long position) {
+ return getTable(key).contains(key, position);
+ }
+
+ @Override
+ public <T> T get(DfsStreamKey key, long position) {
+ return getTable(key).get(key, position);
+ }
+
+ @Override
+ public List<BlockCacheStats> getBlockCacheStats() {
+ return blockCacheTableList.stream()
+ .flatMap(cacheTable -> cacheTable.getBlockCacheStats().stream())
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ private DfsBlockCacheTable getTable(PackExt packExt) {
+ return extBlockCacheTables.getOrDefault(packExt,
+ defaultBlockCacheTable);
+ }
+
+ private DfsBlockCacheTable getTable(DfsStreamKey key) {
+ return extBlockCacheTables.getOrDefault(getPackExt(key),
+ defaultBlockCacheTable);
+ }
+
+ private static PackExt getPackExt(DfsStreamKey key) {
+ return PackExt.values()[key.packExtPos];
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BasePackIndexWriter.java
similarity index 97%
rename from org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexWriter.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BasePackIndexWriter.java
index 87e0b44..b89cc1e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BasePackIndexWriter.java
@@ -19,6 +19,7 @@
import java.util.List;
import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.pack.PackIndexWriter;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.transport.PackedObjectInfo;
import org.eclipse.jgit.util.NB;
@@ -31,7 +32,7 @@
* random access to any object in the pack by associating an ObjectId to the
* byte offset within the pack where the object's data can be read.
*/
-public abstract class PackIndexWriter {
+public abstract class BasePackIndexWriter implements PackIndexWriter {
/** Magic constant indicating post-version 1 format. */
protected static final byte[] TOC = { -1, 't', 'O', 'c' };
@@ -147,7 +148,7 @@ public static PackIndexWriter createVersion(final OutputStream dst,
* the stream this instance outputs to. If not already buffered
* it will be automatically wrapped in a buffered stream.
*/
- protected PackIndexWriter(OutputStream dst) {
+ protected BasePackIndexWriter(OutputStream dst) {
out = new DigestOutputStream(dst instanceof BufferedOutputStream ? dst
: new BufferedOutputStream(dst),
Constants.newMessageDigest());
@@ -172,6 +173,7 @@ protected PackIndexWriter(OutputStream dst) {
* an error occurred while writing to the output stream, or this
* index format cannot store the object data supplied.
*/
+ @Override
public void write(final List<? extends PackedObjectInfo> toStore,
final byte[] packDataChecksum) throws IOException {
entries = toStore;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableDatabase.java
index ed2516d..e9782e2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableDatabase.java
@@ -16,6 +16,7 @@
import java.io.File;
import java.io.IOException;
+import java.io.UncheckedIOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
@@ -23,22 +24,27 @@
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.api.PackRefsCommand;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.events.RefsChangedEvent;
+import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.reftable.MergedReftable;
import org.eclipse.jgit.internal.storage.reftable.ReftableBatchRefUpdate;
import org.eclipse.jgit.internal.storage.reftable.ReftableDatabase;
import org.eclipse.jgit.internal.storage.reftable.ReftableWriter;
import org.eclipse.jgit.lib.BatchRefUpdate;
+import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdRef;
import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.lib.RefRename;
@@ -67,15 +73,20 @@ public class FileReftableDatabase extends RefDatabase {
private final FileReftableStack reftableStack;
+ private final AtomicBoolean autoRefresh;
+
FileReftableDatabase(FileRepository repo) throws IOException {
- this(repo, new File(new File(repo.getDirectory(), Constants.REFTABLE),
+ this(repo, new File(new File(repo.getCommonDirectory(), Constants.REFTABLE),
Constants.TABLES_LIST));
}
FileReftableDatabase(FileRepository repo, File refstackName) throws IOException {
this.fileRepository = repo;
+ this.autoRefresh = new AtomicBoolean(repo.getConfig().getBoolean(
+ ConfigConstants.CONFIG_REFTABLE_SECTION,
+ ConfigConstants.CONFIG_KEY_AUTOREFRESH, false));
this.reftableStack = new FileReftableStack(refstackName,
- new File(fileRepository.getDirectory(), Constants.REFTABLE),
+ new File(fileRepository.getCommonDirectory(), Constants.REFTABLE),
() -> fileRepository.fireEvent(new RefsChangedEvent()),
() -> fileRepository.getConfig());
this.reftableDatabase = new ReftableDatabase() {
@@ -87,7 +98,13 @@ public MergedReftable openMergedReftable() throws IOException {
};
}
- ReflogReader getReflogReader(String refname) throws IOException {
+ @Override
+ public ReflogReader getReflogReader(Ref ref) throws IOException {
+ return reftableDatabase.getReflogReader(ref.getName());
+ }
+
+ @Override
+ public ReflogReader getReflogReader(String refname) throws IOException {
return reftableDatabase.getReflogReader(refname);
}
@@ -108,6 +125,22 @@ public boolean hasFastTipsWithSha1() throws IOException {
}
/**
+ * {@inheritDoc}
+ *
+ * For Reftable, all the data is compacted into a single table.
+ */
+ @Override
+ public void packRefs(ProgressMonitor pm, PackRefsCommand packRefs)
+ throws IOException {
+ pm.beginTask(JGitText.get().packRefs, 1);
+ try {
+ compactFully();
+ } finally {
+ pm.endTask();
+ }
+ }
+
+ /**
* Runs a full compaction for GC purposes.
* @throws IOException on I/O errors
*/
@@ -158,6 +191,7 @@ public RefUpdate newUpdate(String refName, boolean detach)
@Override
public Ref exactRef(String name) throws IOException {
+ autoRefresh();
return reftableDatabase.exactRef(name);
}
@@ -168,6 +202,7 @@ public List<Ref> getRefs() throws IOException {
@Override
public Map<String, Ref> getRefs(String prefix) throws IOException {
+ autoRefresh();
List<Ref> refs = reftableDatabase.getRefsByPrefix(prefix);
RefList.Builder<Ref> builder = new RefList.Builder<>(refs.size());
for (Ref r : refs) {
@@ -180,6 +215,7 @@ public Map<String, Ref> getRefs(String prefix) throws IOException {
@Override
public List<Ref> getRefsByPrefixWithExclusions(String include, Set<String> excludes)
throws IOException {
+ autoRefresh();
return reftableDatabase.getRefsByPrefixWithExclusions(include, excludes);
}
@@ -198,6 +234,50 @@ public Ref peel(Ref ref) throws IOException {
}
+ /**
+ * Whether to auto-refresh the reftable stack if it is out of date.
+ *
+ * @param autoRefresh
+ * whether to auto-refresh the reftable stack if it is out of
+ * date.
+ */
+ public void setAutoRefresh(boolean autoRefresh) {
+ this.autoRefresh.set(autoRefresh);
+ }
+
+ /**
+ * Whether the reftable stack is auto-refreshed if it is out of date.
+ *
+ * @return whether the reftable stack is auto-refreshed if it is out of
+ * date.
+ */
+ public boolean isAutoRefresh() {
+ return autoRefresh.get();
+ }
+
+ private void autoRefresh() {
+ if (autoRefresh.get()) {
+ refresh();
+ }
+ }
+
+ /**
+ * Check if the reftable stack is up to date, and if not, reload it.
+ * <p>
+ * {@inheritDoc}
+ */
+ @Override
+ public void refresh() {
+ try {
+ if (!reftableStack.isUpToDate()) {
+ reftableDatabase.clearCache();
+ reftableStack.reload();
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
private Ref doPeel(Ref leaf) throws IOException {
try (RevWalk rw = new RevWalk(fileRepository)) {
RevObject obj = rw.parseAny(leaf.getObjectId());
@@ -318,7 +398,7 @@ public void close() {
@Override
public void create() throws IOException {
FileUtils.mkdir(
- new File(fileRepository.getDirectory(), Constants.REFTABLE),
+ new File(fileRepository.getCommonDirectory(), Constants.REFTABLE),
true);
}
@@ -538,9 +618,10 @@ private static void writeConvertTable(Repository repo, ReftableWriter w,
boolean writeLogs) throws IOException {
int size = 0;
List<Ref> refs = repo.getRefDatabase().getRefs();
+ RefDatabase refDb = repo.getRefDatabase();
if (writeLogs) {
for (Ref r : refs) {
- ReflogReader rlr = repo.getReflogReader(r.getName());
+ ReflogReader rlr = refDb.getReflogReader(r);
if (rlr != null) {
size = Math.max(rlr.getReverseEntries().size(), size);
}
@@ -563,10 +644,7 @@ private static void writeConvertTable(Repository repo, ReftableWriter w,
if (writeLogs) {
for (Ref r : refs) {
long idx = size;
- ReflogReader reader = repo.getReflogReader(r.getName());
- if (reader == null) {
- continue;
- }
+ ReflogReader reader = refDb.getReflogReader(r);
for (ReflogEntry e : reader.getReverseEntries()) {
w.writeLog(r.getName(), idx, e.getWho(), e.getOldId(),
e.getNewId(), e.getComment());
@@ -615,7 +693,7 @@ public static FileReftableDatabase convertFrom(FileRepository repo,
FileReftableDatabase newDb = null;
File reftableList = null;
try {
- File reftableDir = new File(repo.getDirectory(),
+ File reftableDir = new File(repo.getCommonDirectory(),
Constants.REFTABLE);
reftableList = new File(reftableDir, Constants.TABLES_LIST);
if (!reftableDir.isDirectory()) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableStack.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableStack.java
index 0f5ff0f..b2c8892 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableStack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableStack.java
@@ -18,8 +18,10 @@
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
import java.nio.file.StandardCopyOption;
import java.security.SecureRandom;
import java.util.ArrayList;
@@ -27,6 +29,7 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import java.util.stream.Collectors;
@@ -39,6 +42,8 @@
import org.eclipse.jgit.internal.storage.reftable.ReftableReader;
import org.eclipse.jgit.internal.storage.reftable.ReftableWriter;
import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.CoreConfig;
+import org.eclipse.jgit.lib.CoreConfig.TrustStat;
import org.eclipse.jgit.util.FileUtils;
import org.eclipse.jgit.util.SystemReader;
@@ -59,6 +64,9 @@ private static class StackEntry {
private List<StackEntry> stack;
+ private AtomicReference<FileSnapshot> snapshot = new AtomicReference<>(
+ FileSnapshot.DIRTY);
+
private long lastNextUpdateIndex;
private final File stackPath;
@@ -98,6 +106,8 @@ static class CompactionStats {
private final CompactionStats stats;
+ private final TrustStat trustTablesListStat;
+
/**
* Creates a stack corresponding to the list of reftables in the argument
*
@@ -126,6 +136,8 @@ public FileReftableStack(File stackPath, File reftableDir,
reload();
stats = new CompactionStats();
+ trustTablesListStat = configSupplier.get().get(CoreConfig.KEY)
+ .getTrustTablesListStat();
}
CompactionStats getStats() {
@@ -272,8 +284,9 @@ public interface Writer {
}
private List<String> readTableNames() throws IOException {
+ FileSnapshot old;
List<String> names = new ArrayList<>(stack.size() + 1);
-
+ old = snapshot.get();
try (BufferedReader br = new BufferedReader(
new InputStreamReader(new FileInputStream(stackPath), UTF_8))) {
String line;
@@ -282,8 +295,10 @@ private List<String> readTableNames() throws IOException {
names.add(line);
}
}
+ snapshot.compareAndSet(old, FileSnapshot.save(stackPath));
} catch (FileNotFoundException e) {
// file isn't there: empty repository.
+ snapshot.compareAndSet(old, FileSnapshot.MISSING_FILE);
}
return names;
}
@@ -294,9 +309,28 @@ private List<String> readTableNames() throws IOException {
* on IO problem
*/
boolean isUpToDate() throws IOException {
- // We could use FileSnapshot to avoid reading the file, but the file is
- // small so it's probably a minor optimization.
try {
+ switch (trustTablesListStat) {
+ case NEVER:
+ break;
+ case AFTER_OPEN:
+ try (InputStream stream = Files
+ .newInputStream(stackPath.toPath())) {
+ // open the tables.list file to refresh attributes (on some
+ // NFS clients)
+ } catch (FileNotFoundException | NoSuchFileException e) {
+ // ignore
+ }
+ //$FALL-THROUGH$
+ case ALWAYS:
+ if (!snapshot.get().isModified(stackPath)) {
+ return true;
+ }
+ break;
+ case INHERIT:
+ // only used in CoreConfig internally
+ throw new IllegalStateException();
+ }
List<String> names = readTableNames();
if (names.size() != stack.size()) {
return false;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java
index e5a00d3..bcf9f1e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java
@@ -2,7 +2,7 @@
* Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
* Copyright (C) 2008-2010, Google Inc.
* Copyright (C) 2006-2010, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org> and others
+ * Copyright (C) 2006-2024, Shawn O. Pearce <spearce@spearce.org> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -31,8 +31,8 @@
import java.util.Objects;
import java.util.Set;
-import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.attributes.AttributesNode;
import org.eclipse.jgit.attributes.AttributesNodeProvider;
@@ -60,7 +60,6 @@
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
-import org.eclipse.jgit.storage.pack.PackConfig;
import org.eclipse.jgit.transport.ReceiveCommand;
import org.eclipse.jgit.util.FileUtils;
import org.eclipse.jgit.util.IO;
@@ -165,7 +164,7 @@ public FileRepository(BaseRepositoryBuilder options) throws IOException {
throw new IOException(e.getMessage(), e);
}
repoConfig = new FileBasedConfig(userConfig, getFS().resolve(
- getDirectory(), Constants.CONFIG),
+ getCommonDirectory(), Constants.CONFIG),
getFS());
loadRepoConfig();
@@ -193,7 +192,7 @@ public FileRepository(BaseRepositoryBuilder options) throws IOException {
options.getObjectDirectory(), //
options.getAlternateObjectDirectories(), //
getFS(), //
- new File(getDirectory(), Constants.SHALLOW));
+ new File(getCommonDirectory(), Constants.SHALLOW));
if (objectDatabase.exists()) {
if (repositoryFormatVersion > 1)
@@ -215,6 +214,16 @@ private void loadRepoConfig() throws IOException {
}
}
+ private String getRelativeDir(File base, File other) {
+ File relPath;
+ try {
+ relPath = base.toPath().relativize(other.toPath()).toFile();
+ } catch (IllegalArgumentException e) {
+ relPath = other;
+ }
+ return FileUtils.pathToString(relPath);
+ }
+
/**
* {@inheritDoc}
* <p>
@@ -223,6 +232,22 @@ private void loadRepoConfig() throws IOException {
*/
@Override
public void create(boolean bare) throws IOException {
+ create(bare, false);
+ }
+
+ /**
+ * Create a new Git repository initializing the necessary files and
+ * directories.
+ *
+ * @param bare
+ * if true, a bare repository (a repository without a working
+ * directory) is created.
+ * @param relativePaths
+ * if true, relative paths are used for GIT_DIR and GIT_WORK_TREE
+ * @throws IOException
+ * in case of IO problem
+ */
+ public void create(boolean bare, boolean relativePaths) throws IOException {
final FileBasedConfig cfg = getConfig();
if (cfg.getFile().exists()) {
throw new IllegalStateException(MessageFormat.format(
@@ -293,15 +318,25 @@ && getDirectory().getName().startsWith(".")) //$NON-NLS-1$
if (!bare) {
File workTree = getWorkTree();
if (!getDirectory().getParentFile().equals(workTree)) {
+ String workTreePath;
+ String gitDirPath;
+ if (relativePaths) {
+ File canonGitDir = getDirectory().getCanonicalFile();
+ File canonWorkTree = getWorkTree().getCanonicalFile();
+ workTreePath = getRelativeDir(canonGitDir, canonWorkTree);
+ gitDirPath = getRelativeDir(canonWorkTree, canonGitDir);
+ } else {
+ workTreePath = getWorkTree().getAbsolutePath();
+ gitDirPath = getDirectory().getAbsolutePath();
+ }
cfg.setString(ConfigConstants.CONFIG_CORE_SECTION, null,
- ConfigConstants.CONFIG_KEY_WORKTREE, getWorkTree()
- .getAbsolutePath());
+ ConfigConstants.CONFIG_KEY_WORKTREE, workTreePath);
LockFile dotGitLockFile = new LockFile(new File(workTree,
Constants.DOT_GIT));
try {
if (dotGitLockFile.lock()) {
dotGitLockFile.write(Constants.encode(Constants.GITDIR
- + getDirectory().getAbsolutePath()));
+ + gitDirPath));
dotGitLockFile.commit();
}
} finally {
@@ -507,29 +542,6 @@ public void notifyIndexChanged(boolean internal) {
}
@Override
- public ReflogReader getReflogReader(String refName) throws IOException {
- if (refs instanceof FileReftableDatabase) {
- // Cannot use findRef: reftable stores log data for deleted or renamed
- // branches.
- return ((FileReftableDatabase)refs).getReflogReader(refName);
- }
-
- // TODO: use exactRef here, which offers more predictable and therefore preferable
- // behavior.
- Ref ref = findRef(refName);
- if (ref == null) {
- return null;
- }
- return new ReflogReaderImpl(this, ref.getName());
- }
-
- @Override
- public @NonNull ReflogReader getReflogReader(@NonNull Ref ref)
- throws IOException {
- return new ReflogReaderImpl(this, ref.getName());
- }
-
- @Override
public AttributesNodeProvider createAttributesNodeProvider() {
return new AttributesNodeProviderImpl(this);
}
@@ -595,13 +607,12 @@ private boolean shouldAutoDetach() {
@Override
public void autoGC(ProgressMonitor monitor) {
GC gc = new GC(this);
- gc.setPackConfig(new PackConfig(this));
gc.setProgressMonitor(monitor);
gc.setAuto(true);
gc.setBackground(shouldAutoDetach());
try {
gc.gc();
- } catch (ParseException | IOException e) {
+ } catch (ParseException | IOException | GitAPIException e) {
throw new JGitInternalException(JGitText.get().gcFailed, e);
}
}
@@ -622,16 +633,17 @@ public void autoGC(ProgressMonitor monitor) {
* on IO problem
*/
void convertToPackedRefs(boolean writeLogs, boolean backup) throws IOException {
+ File commonDirectory = getCommonDirectory();
List<Ref> all = refs.getRefs();
- File packedRefs = new File(getDirectory(), Constants.PACKED_REFS);
+ File packedRefs = new File(commonDirectory, Constants.PACKED_REFS);
if (packedRefs.exists()) {
throw new IOException(MessageFormat.format(JGitText.get().fileAlreadyExists,
packedRefs.getName()));
}
- File refsFile = new File(getDirectory(), "refs"); //$NON-NLS-1$
+ File refsFile = new File(commonDirectory, "refs"); //$NON-NLS-1$
File refsHeadsFile = new File(refsFile, "heads");//$NON-NLS-1$
- File headFile = new File(getDirectory(), Constants.HEAD);
+ File headFile = new File(commonDirectory, Constants.HEAD);
FileReftableDatabase oldDb = (FileReftableDatabase) refs;
// Remove the dummy files that ensure compatibility with older git
@@ -661,8 +673,8 @@ void convertToPackedRefs(boolean writeLogs, boolean backup) throws IOException {
}
if (writeLogs) {
- List<ReflogEntry> logs = oldDb.getReflogReader(r.getName())
- .getReverseEntries();
+ ReflogReader reflogReader = oldDb.getReflogReader(r);
+ List<ReflogEntry> logs = reflogReader.getReverseEntries();
Collections.reverse(logs);
for (ReflogEntry e : logs) {
logWriter.log(r.getName(), e);
@@ -701,12 +713,14 @@ void convertToPackedRefs(boolean writeLogs, boolean backup) throws IOException {
}
if (!backup) {
- File reftableDir = new File(getDirectory(), Constants.REFTABLE);
+ File reftableDir = new File(commonDirectory, Constants.REFTABLE);
FileUtils.delete(reftableDir,
FileUtils.RECURSIVE | FileUtils.IGNORE_ERRORS);
}
repoConfig.unset(ConfigConstants.CONFIG_EXTENSIONS_SECTION, null,
ConfigConstants.CONFIG_KEY_REF_STORAGE);
+ repoConfig.setLong(ConfigConstants.CONFIG_CORE_SECTION, null,
+ ConfigConstants.CONFIG_KEY_REPO_FORMAT_VERSION, 0);
repoConfig.save();
}
@@ -730,8 +744,10 @@ void convertToPackedRefs(boolean writeLogs, boolean backup) throws IOException {
@SuppressWarnings("nls")
void convertToReftable(boolean writeLogs, boolean backup)
throws IOException {
- File reftableDir = new File(getDirectory(), Constants.REFTABLE);
- File headFile = new File(getDirectory(), Constants.HEAD);
+ File commonDirectory = getCommonDirectory();
+ File directory = getDirectory();
+ File reftableDir = new File(commonDirectory, Constants.REFTABLE);
+ File headFile = new File(directory, Constants.HEAD);
if (reftableDir.exists() && FileUtils.hasFiles(reftableDir.toPath())) {
throw new IOException(JGitText.get().reftableDirExists);
}
@@ -739,28 +755,28 @@ void convertToReftable(boolean writeLogs, boolean backup)
// Ignore return value, as it is tied to temporary newRefs file.
FileReftableDatabase.convertFrom(this, writeLogs);
- File refsFile = new File(getDirectory(), "refs");
+ File refsFile = new File(commonDirectory, "refs");
// non-atomic: remove old data.
- File packedRefs = new File(getDirectory(), Constants.PACKED_REFS);
- File logsDir = new File(getDirectory(), Constants.LOGS);
+ File packedRefs = new File(commonDirectory, Constants.PACKED_REFS);
+ File logsDir = new File(commonDirectory, Constants.LOGS);
List<String> additional = getRefDatabase().getAdditionalRefs().stream()
.map(Ref::getName).collect(toList());
additional.add(Constants.HEAD);
if (backup) {
- FileUtils.rename(refsFile, new File(getDirectory(), "refs.old"));
+ FileUtils.rename(refsFile, new File(commonDirectory, "refs.old"));
if (packedRefs.exists()) {
- FileUtils.rename(packedRefs, new File(getDirectory(),
+ FileUtils.rename(packedRefs, new File(commonDirectory,
Constants.PACKED_REFS + ".old"));
}
if (logsDir.exists()) {
FileUtils.rename(logsDir,
- new File(getDirectory(), Constants.LOGS + ".old"));
+ new File(commonDirectory, Constants.LOGS + ".old"));
}
for (String r : additional) {
- FileUtils.rename(new File(getDirectory(), r),
- new File(getDirectory(), r + ".old"));
+ FileUtils.rename(new File(commonDirectory, r),
+ new File(commonDirectory, r + ".old"));
}
} else {
FileUtils.delete(packedRefs, FileUtils.SKIP_MISSING);
@@ -770,7 +786,7 @@ void convertToReftable(boolean writeLogs, boolean backup)
FileUtils.delete(refsFile,
FileUtils.RECURSIVE | FileUtils.SKIP_MISSING);
for (String r : additional) {
- new File(getDirectory(), r).delete();
+ new File(commonDirectory, r).delete();
}
}
@@ -784,7 +800,7 @@ void convertToReftable(boolean writeLogs, boolean backup)
// Some tools might write directly into .git/refs/heads/BRANCH. By
// putting a file here, this fails spectacularly.
- FileUtils.createNewFile(new File(refsFile, "heads"));
+ FileUtils.createNewFile(new File(refsFile, Constants.HEADS));
repoConfig.setString(ConfigConstants.CONFIG_EXTENSIONS_SECTION, null,
ConfigConstants.CONFIG_KEY_REF_STORAGE,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileSnapshot.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileSnapshot.java
index c88ac98..b6bde6e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileSnapshot.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileSnapshot.java
@@ -15,6 +15,7 @@
import java.io.File;
import java.io.IOException;
+import java.nio.file.FileSystemException;
import java.nio.file.NoSuchFileException;
import java.nio.file.attribute.BasicFileAttributes;
import java.time.Duration;
@@ -22,10 +23,12 @@
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
+import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.util.FileUtils;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.FS.FileStoreAttributes;
import org.slf4j.Logger;
@@ -139,29 +142,6 @@ private static Object getFileKey(BasicFileAttributes fileAttributes) {
* @param modified
* the last modification time of the file
* @return the snapshot.
- * @deprecated use {@link #save(Instant)} instead.
- */
- @Deprecated
- public static FileSnapshot save(long modified) {
- final Instant read = Instant.now();
- return new FileSnapshot(read, Instant.ofEpochMilli(modified),
- UNKNOWN_SIZE, FALLBACK_TIMESTAMP_RESOLUTION, MISSING_FILEKEY);
- }
-
- /**
- * Record a snapshot for a file for which the last modification time is
- * already known.
- * <p>
- * This method should be invoked before the file is accessed.
- * <p>
- * Note that this method cannot rely on measuring file timestamp resolution
- * to avoid racy git issues caused by finite file timestamp resolution since
- * it's unknown in which filesystem the file is located. Hence the worst
- * case fallback for timestamp resolution is used.
- *
- * @param modified
- * the last modification time of the file
- * @return the snapshot.
*/
public static FileSnapshot save(Instant modified) {
final Instant read = Instant.now();
@@ -231,14 +211,8 @@ protected FileSnapshot(File file, boolean useConfig) {
this.useConfig = useConfig;
BasicFileAttributes fileAttributes = null;
try {
- fileAttributes = FS.DETECTED.fileAttributes(file);
- } catch (NoSuchFileException e) {
- this.lastModified = Instant.EPOCH;
- this.size = 0L;
- this.fileKey = MISSING_FILEKEY;
- return;
- } catch (IOException e) {
- LOG.error(e.getMessage(), e);
+ fileAttributes = getFileAttributes(file);
+ } catch (NoSuchElementException e) {
this.lastModified = Instant.EPOCH;
this.size = 0L;
this.fileKey = MISSING_FILEKEY;
@@ -282,17 +256,6 @@ private FileSnapshot(Instant read, Instant modified, long size,
* Get time of last snapshot update
*
* @return time of last snapshot update
- * @deprecated use {@link #lastModifiedInstant()} instead
- */
- @Deprecated
- public long lastModified() {
- return lastModified.toEpochMilli();
- }
-
- /**
- * Get time of last snapshot update
- *
- * @return time of last snapshot update
*/
public Instant lastModifiedInstant() {
return lastModified;
@@ -319,16 +282,11 @@ public boolean isModified(File path) {
long currSize;
Object currFileKey;
try {
- BasicFileAttributes fileAttributes = FS.DETECTED.fileAttributes(path);
+ BasicFileAttributes fileAttributes = getFileAttributes(path);
currLastModified = fileAttributes.lastModifiedTime().toInstant();
currSize = fileAttributes.size();
currFileKey = getFileKey(fileAttributes);
- } catch (NoSuchFileException e) {
- currLastModified = Instant.EPOCH;
- currSize = 0L;
- currFileKey = MISSING_FILEKEY;
- } catch (IOException e) {
- LOG.error(e.getMessage(), e);
+ } catch (NoSuchElementException e) {
currLastModified = Instant.EPOCH;
currSize = 0L;
currFileKey = MISSING_FILEKEY;
@@ -586,4 +544,27 @@ private FileStoreAttributes fileStoreAttributeCache() {
}
return fileStoreAttributeCache;
}
+
+ private static BasicFileAttributes getFileAttributes(File path)
+ throws NoSuchElementException {
+ try {
+ try {
+ return FS.DETECTED.fileAttributes(path);
+ } catch (IOException e) {
+ if (!FileUtils.isStaleFileHandle(e)) {
+ throw e;
+ }
+ }
+ } catch (NoSuchFileException e) {
+ // ignore
+ } catch (FileSystemException e) {
+ String msg = e.getMessage();
+ if (!msg.endsWith("Not a directory")) { //$NON-NLS-1$
+ LOG.error(msg, e);
+ }
+ } catch (IOException e) {
+ LOG.error(e.getMessage(), e);
+ }
+ throw new NoSuchElementException(path.toString());
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
index cf26f8d..05bd970 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
@@ -63,6 +63,8 @@
import java.util.stream.Stream;
import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.api.PackRefsCommand;
+import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.dircache.DirCacheIterator;
import org.eclipse.jgit.errors.CancelledException;
import org.eclipse.jgit.errors.CorruptObjectException;
@@ -100,9 +102,8 @@
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.FS.LockToken;
import org.eclipse.jgit.util.FileUtils;
-import org.eclipse.jgit.util.GitDateParser;
+import org.eclipse.jgit.util.GitTimeParser;
import org.eclipse.jgit.util.StringUtils;
-import org.eclipse.jgit.util.SystemReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -158,11 +159,11 @@ public static void setExecutor(ExecutorService e) {
private long expireAgeMillis = -1;
- private Date expire;
+ private Instant expire;
private long packExpireAgeMillis = -1;
- private Date packExpire;
+ private Instant packExpire;
private Boolean packKeptObjects;
@@ -233,9 +234,11 @@ public GC(FileRepository repo) {
* @throws java.text.ParseException
* If the configuration parameter "gc.pruneexpire" couldn't be
* parsed
+ * @throws GitAPIException
+ * If packing refs failed
*/
public CompletableFuture<Collection<Pack>> gc()
- throws IOException, ParseException {
+ throws IOException, ParseException, GitAPIException {
if (!background) {
return CompletableFuture.completedFuture(doGc());
}
@@ -254,7 +257,7 @@ public CompletableFuture<Collection<Pack>> gc()
gcLog.commit();
}
return newPacks;
- } catch (IOException | ParseException e) {
+ } catch (IOException | ParseException | GitAPIException e) {
try {
gcLog.write(e.getMessage());
StringWriter sw = new StringWriter();
@@ -277,7 +280,8 @@ private ExecutorService executor() {
return (executor != null) ? executor : WorkQueue.getExecutor();
}
- private Collection<Pack> doGc() throws IOException, ParseException {
+ private Collection<Pack> doGc()
+ throws IOException, ParseException, GitAPIException {
if (automatic && !needGc()) {
return Collections.emptyList();
}
@@ -286,7 +290,8 @@ private Collection<Pack> doGc() throws IOException, ParseException {
return Collections.emptyList();
}
pm.start(6 /* tasks */);
- packRefs();
+ new PackRefsCommand(repo).setProgressMonitor(pm).setAll(true)
+ .call();
// TODO: implement reflog_expire(pm, repo);
Collection<Pack> newPacks = repack();
prune(Collections.emptySet());
@@ -692,16 +697,18 @@ private long getExpireDate() throws ParseException {
if (expire == null && expireAgeMillis == -1) {
String pruneExpireStr = getPruneExpireStr();
- if (pruneExpireStr == null)
+ if (pruneExpireStr == null) {
pruneExpireStr = PRUNE_EXPIRE_DEFAULT;
- expire = GitDateParser.parse(pruneExpireStr, null, SystemReader
- .getInstance().getLocale());
+ }
+ expire = GitTimeParser.parseInstant(pruneExpireStr);
expireAgeMillis = -1;
}
- if (expire != null)
- expireDate = expire.getTime();
- if (expireAgeMillis != -1)
+ if (expire != null) {
+ expireDate = expire.toEpochMilli();
+ }
+ if (expireAgeMillis != -1) {
expireDate = System.currentTimeMillis() - expireAgeMillis;
+ }
return expireDate;
}
@@ -718,16 +725,18 @@ private long getPackExpireDate() throws ParseException {
String prunePackExpireStr = repo.getConfig().getString(
ConfigConstants.CONFIG_GC_SECTION, null,
ConfigConstants.CONFIG_KEY_PRUNEPACKEXPIRE);
- if (prunePackExpireStr == null)
+ if (prunePackExpireStr == null) {
prunePackExpireStr = PRUNE_PACK_EXPIRE_DEFAULT;
- packExpire = GitDateParser.parse(prunePackExpireStr, null,
- SystemReader.getInstance().getLocale());
+ }
+ packExpire = GitTimeParser.parseInstant(prunePackExpireStr);
packExpireAgeMillis = -1;
}
- if (packExpire != null)
- packExpireDate = packExpire.getTime();
- if (packExpireAgeMillis != -1)
+ if (packExpire != null) {
+ packExpireDate = packExpire.toEpochMilli();
+ }
+ if (packExpireAgeMillis != -1) {
packExpireDate = System.currentTimeMillis() - packExpireAgeMillis;
+ }
return packExpireDate;
}
@@ -780,43 +789,6 @@ private static boolean equals(Ref r1, Ref r2) {
}
/**
- * Pack ref storage. For a RefDirectory database, this packs all
- * non-symbolic, loose refs into packed-refs. For Reftable, all of the data
- * is compacted into a single table.
- *
- * @throws java.io.IOException
- * if an IO error occurred
- */
- public void packRefs() throws IOException {
- RefDatabase refDb = repo.getRefDatabase();
- if (refDb instanceof FileReftableDatabase) {
- // TODO: abstract this more cleanly.
- pm.beginTask(JGitText.get().packRefs, 1);
- try {
- ((FileReftableDatabase) refDb).compactFully();
- } finally {
- pm.endTask();
- }
- return;
- }
-
- Collection<Ref> refs = refDb.getRefsByPrefix(Constants.R_REFS);
- List<String> refsToBePacked = new ArrayList<>(refs.size());
- pm.beginTask(JGitText.get().packRefs, refs.size());
- try {
- for (Ref ref : refs) {
- checkCancelled();
- if (!ref.isSymbolic() && ref.getStorage().isLoose())
- refsToBePacked.add(ref.getName());
- pm.update(1);
- }
- ((RefDirectory) repo.getRefDatabase()).pack(refsToBePacked);
- } finally {
- pm.endTask();
- }
- }
-
- /**
* Packs all objects which reachable from any of the heads into one pack
* file. Additionally all objects which are not reachable from any head but
* which are reachable from any of the other refs (e.g. tags), special refs
@@ -1047,7 +1019,7 @@ private static boolean isTag(Ref ref) {
}
private void deleteEmptyRefsFolders() throws IOException {
- Path refs = repo.getDirectory().toPath().resolve(Constants.R_REFS);
+ Path refs = repo.getCommonDirectory().toPath().resolve(Constants.R_REFS);
// Avoid deleting a folder that was created after the threshold so that concurrent
// operations trying to create a reference are not impacted
Instant threshold = Instant.now().minus(30, ChronoUnit.SECONDS);
@@ -1185,7 +1157,7 @@ private void deleteTempPacksIdx() {
* if an IO error occurred
*/
private Set<ObjectId> listRefLogObjects(Ref ref, long minTime) throws IOException {
- ReflogReader reflogReader = repo.getReflogReader(ref);
+ ReflogReader reflogReader = repo.getRefDatabase().getReflogReader(ref);
List<ReflogEntry> rlEntries = reflogReader
.getReverseEntries();
if (rlEntries == null || rlEntries.isEmpty())
@@ -1509,6 +1481,18 @@ public static class RepoStatistics {
public long numberOfPackFiles;
/**
+ * The number of pack files that were created since the last bitmap
+ * generation.
+ */
+ public long numberOfPackFilesSinceBitmap;
+
+ /**
+ * The number of objects stored in pack files and as loose object
+ * created after the last bitmap generation.
+ */
+ public long numberOfObjectsSinceBitmap;
+
+ /**
* The number of objects stored as loose objects.
*/
public long numberOfLooseObjects;
@@ -1543,6 +1527,10 @@ public String toString() {
final StringBuilder b = new StringBuilder();
b.append("numberOfPackedObjects=").append(numberOfPackedObjects); //$NON-NLS-1$
b.append(", numberOfPackFiles=").append(numberOfPackFiles); //$NON-NLS-1$
+ b.append(", numberOfPackFilesSinceBitmap=") //$NON-NLS-1$
+ .append(numberOfPackFilesSinceBitmap);
+ b.append(", numberOfObjectsSinceBitmap=") //$NON-NLS-1$
+ .append(numberOfObjectsSinceBitmap);
b.append(", numberOfLooseObjects=").append(numberOfLooseObjects); //$NON-NLS-1$
b.append(", numberOfLooseRefs=").append(numberOfLooseRefs); //$NON-NLS-1$
b.append(", numberOfPackedRefs=").append(numberOfPackedRefs); //$NON-NLS-1$
@@ -1563,12 +1551,22 @@ public String toString() {
public RepoStatistics getStatistics() throws IOException {
RepoStatistics ret = new RepoStatistics();
Collection<Pack> packs = repo.getObjectDatabase().getPacks();
+ long latestBitmapTime = 0L;
for (Pack p : packs) {
- ret.numberOfPackedObjects += p.getIndex().getObjectCount();
+ long packedObjects = p.getIndex().getObjectCount();
+ ret.numberOfPackedObjects += packedObjects;
ret.numberOfPackFiles++;
ret.sizeOfPackedObjects += p.getPackFile().length();
- if (p.getBitmapIndex() != null)
+ if (p.getBitmapIndex() != null) {
ret.numberOfBitmaps += p.getBitmapIndex().getBitmapCount();
+ if (latestBitmapTime == 0L) {
+ latestBitmapTime = p.getFileSnapshot().lastModifiedInstant().toEpochMilli();
+ }
+ }
+ else if (latestBitmapTime == 0L) {
+ ret.numberOfPackFilesSinceBitmap++;
+ ret.numberOfObjectsSinceBitmap += packedObjects;
+ }
}
File objDir = repo.getObjectsDirectory();
String[] fanout = objDir.list();
@@ -1584,6 +1582,9 @@ public RepoStatistics getStatistics() throws IOException {
continue;
ret.numberOfLooseObjects++;
ret.sizeOfLooseObjects += f.length();
+ if (f.lastModified() > latestBitmapTime) {
+ ret.numberOfObjectsSinceBitmap ++;
+ }
}
}
}
@@ -1659,12 +1660,31 @@ public void setPackConfig(@NonNull PackConfig pconfig) {
* candidate for pruning.
*
* @param expire
- * instant in time which defines object expiration
- * objects with modification time before this instant are expired
- * objects with modification time newer or equal to this instant
- * are not expired
+ * instant in time which defines object expiration objects with
+ * modification time before this instant are expired objects with
+ * modification time newer or equal to this instant are not
+ * expired
+ * @deprecated use {@link #setExpire(Instant)} instead
*/
+ @Deprecated(since = "7.2")
public void setExpire(Date expire) {
+ this.expire = expire.toInstant();
+ expireAgeMillis = -1;
+ }
+
+ /**
+ * During gc() or prune() each unreferenced, loose object which has been
+ * created or modified after or at <code>expire</code> will not be pruned.
+ * Only older objects may be pruned. If set to null then every object is a
+ * candidate for pruning.
+ *
+ * @param expire
+ * instant in time which defines object expiration objects with
+ * modification time before this instant are expired objects with
+ * modification time newer or equal to this instant are not
+ * expired
+ */
+ public void setExpire(Instant expire) {
this.expire = expire;
expireAgeMillis = -1;
}
@@ -1677,8 +1697,24 @@ public void setExpire(Date expire) {
*
* @param packExpire
* instant in time which defines packfile expiration
+ * @deprecated use {@link #setPackExpire(Instant)} instead
*/
+ @Deprecated(since = "7.2")
public void setPackExpire(Date packExpire) {
+ this.packExpire = packExpire.toInstant();
+ packExpireAgeMillis = -1;
+ }
+
+ /**
+ * During gc() or prune() packfiles which are created or modified after or
+ * at <code>packExpire</code> will not be deleted. Only older packfiles may
+ * be deleted. If set to null then every packfile is a candidate for
+ * deletion.
+ *
+ * @param packExpire
+ * instant in time which defines packfile expiration
+ */
+ public void setPackExpire(Instant packExpire) {
this.packExpire = packExpire;
packExpireAgeMillis = -1;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GcLog.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GcLog.java
index 628bf5d..862aaab 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GcLog.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GcLog.java
@@ -23,8 +23,7 @@
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.util.FileUtils;
-import org.eclipse.jgit.util.GitDateParser;
-import org.eclipse.jgit.util.SystemReader;
+import org.eclipse.jgit.util.GitTimeParser;
/**
* This class manages the gc.log file for a {@link FileRepository}.
@@ -50,7 +49,7 @@ class GcLog {
*/
GcLog(FileRepository repo) {
this.repo = repo;
- logFile = new File(repo.getDirectory(), "gc.log"); //$NON-NLS-1$
+ logFile = new File(repo.getCommonDirectory(), "gc.log"); //$NON-NLS-1$
lock = new LockFile(logFile);
}
@@ -62,8 +61,7 @@ private Instant getLogExpiry() throws ParseException {
if (logExpiryStr == null) {
logExpiryStr = LOG_EXPIRY_DEFAULT;
}
- gcLogExpire = GitDateParser.parse(logExpiryStr, null,
- SystemReader.getInstance().getLocale()).toInstant();
+ gcLogExpire = GitTimeParser.parseInstant(logExpiryStr);
}
return gcLogExpire;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InfoAttributesNode.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InfoAttributesNode.java
index 11d842b..e8d442b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InfoAttributesNode.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InfoAttributesNode.java
@@ -46,7 +46,7 @@ public AttributesNode load() throws IOException {
FS fs = repository.getFS();
- File attributes = fs.resolve(repository.getDirectory(),
+ File attributes = fs.resolve(repository.getCommonDirectory(),
Constants.INFO_ATTRIBUTES);
FileRepository.AttributesNodeProviderImpl.loadRulesFromFile(r, attributes);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java
index a2d8bd0..9e12ee8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java
@@ -24,6 +24,7 @@
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.FileTime;
import java.text.MessageFormat;
@@ -141,9 +142,8 @@ public boolean lock() throws IOException {
throw new IllegalStateException(
MessageFormat.format(JGitText.get().lockAlreadyHeld, ref));
}
- FileUtils.mkdirs(lck.getParentFile(), true);
try {
- token = FS.DETECTED.createNewFileAtomic(lck);
+ token = createLockFileWithRetry();
} catch (IOException e) {
LOG.error(JGitText.get().failedCreateLockFile, lck, e);
throw e;
@@ -160,6 +160,19 @@ public boolean lock() throws IOException {
return obtainedLock;
}
+ private FS.LockToken createLockFileWithRetry() throws IOException {
+ try {
+ return createLockFile();
+ } catch (NoSuchFileException e) {
+ return createLockFile();
+ }
+ }
+
+ private FS.LockToken createLockFile() throws IOException {
+ FileUtils.mkdirs(lck.getParentFile(), true);
+ return FS.DETECTED.createNewFileAtomic(lck);
+ }
+
/**
* Try to establish the lock for appending.
*
@@ -515,17 +528,6 @@ private void saveStatInformation() {
* Get the modification time of the output file when it was committed.
*
* @return modification time of the lock file right before we committed it.
- * @deprecated use {@link #getCommitLastModifiedInstant()} instead
- */
- @Deprecated
- public long getCommitLastModified() {
- return commitSnapshot.lastModified();
- }
-
- /**
- * Get the modification time of the output file when it was committed.
- *
- * @return modification time of the lock file right before we committed it.
*/
public Instant getCommitLastModifiedInstant() {
return commitSnapshot.lastModifiedInstant();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LooseObjects.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LooseObjects.java
index b4bb2a9..909b3e3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LooseObjects.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LooseObjects.java
@@ -26,8 +26,9 @@
import org.eclipse.jgit.lib.AbbreviatedObjectId;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.CoreConfig;
+import org.eclipse.jgit.lib.CoreConfig.TrustStat;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.util.FileUtils;
@@ -49,13 +50,13 @@ class LooseObjects {
* Maximum number of attempts to read a loose object for which a stale file
* handle exception is thrown
*/
- private final static int MAX_LOOSE_OBJECT_STALE_READ_ATTEMPTS = 5;
+ private final static int MAX_STALE_READ_RETRIES = 5;
private final File directory;
private final UnpackedObjectCache unpackedObjectCache;
- private final boolean trustFolderStat;
+ private final TrustStat trustLooseObjectStat;
/**
* Initialize a reference to an on-disk object directory.
@@ -68,9 +69,8 @@ class LooseObjects {
LooseObjects(Config config, File dir) {
directory = dir;
unpackedObjectCache = new UnpackedObjectCache();
- trustFolderStat = config.getBoolean(
- ConfigConstants.CONFIG_CORE_SECTION,
- ConfigConstants.CONFIG_KEY_TRUSTFOLDERSTAT, true);
+ trustLooseObjectStat = config.get(CoreConfig.KEY)
+ .getTrustLooseObjectStat();
}
/**
@@ -108,7 +108,8 @@ boolean hasCached(AnyObjectId id) {
*/
boolean has(AnyObjectId objectId) {
boolean exists = hasWithoutRefresh(objectId);
- if (trustFolderStat || exists) {
+ if (trustLooseObjectStat == TrustStat.ALWAYS
+ || exists) {
return exists;
}
try (InputStream stream = Files.newInputStream(directory.toPath())) {
@@ -163,13 +164,31 @@ boolean resolve(Set<ObjectId> matches, AbbreviatedObjectId id,
}
ObjectLoader open(WindowCursor curs, AnyObjectId id) throws IOException {
- int readAttempts = 0;
- while (readAttempts < MAX_LOOSE_OBJECT_STALE_READ_ATTEMPTS) {
- readAttempts++;
- File path = fileFor(id);
- if (trustFolderStat && !path.exists()) {
+ File path = fileFor(id);
+ for (int retries = 0; retries < MAX_STALE_READ_RETRIES; retries++) {
+ boolean reload = true;
+ switch (trustLooseObjectStat) {
+ case NEVER:
break;
+ case AFTER_OPEN:
+ try (InputStream stream = Files
+ .newInputStream(path.getParentFile().toPath())) {
+ // open the loose object's fanout directory to refresh
+ // attributes (on some NFS clients)
+ } catch (FileNotFoundException | NoSuchFileException e) {
+ // ignore
+ }
+ //$FALL-THROUGH$
+ case ALWAYS:
+ if (!path.exists()) {
+ reload = false;
+ }
+ break;
+ case INHERIT:
+ // only used in CoreConfig internally
+ throw new IllegalStateException();
}
+ if (reload) {
try {
return getObjectLoader(curs, path, id);
} catch (FileNotFoundException noFile) {
@@ -183,9 +202,10 @@ ObjectLoader open(WindowCursor curs, AnyObjectId id) throws IOException {
}
if (LOG.isDebugEnabled()) {
LOG.debug(MessageFormat.format(
- JGitText.get().looseObjectHandleIsStale, id.name(),
- Integer.valueOf(readAttempts), Integer.valueOf(
- MAX_LOOSE_OBJECT_STALE_READ_ATTEMPTS)));
+ JGitText.get().looseObjectHandleIsStale,
+ id.name(), Integer.valueOf(retries),
+ Integer.valueOf(MAX_STALE_READ_RETRIES)));
+ }
}
}
}
@@ -211,7 +231,7 @@ ObjectLoader getObjectLoader(WindowCursor curs, File path, AnyObjectId id)
try {
return getObjectLoaderWithoutRefresh(curs, path, id);
} catch (FileNotFoundException e) {
- if (trustFolderStat) {
+ if (trustLooseObjectStat == TrustStat.ALWAYS) {
throw e;
}
try (InputStream stream = Files
@@ -248,7 +268,7 @@ long getSize(WindowCursor curs, AnyObjectId id) throws IOException {
return getSizeWithoutRefresh(curs, id);
} catch (FileNotFoundException noFile) {
try {
- if (trustFolderStat) {
+ if (trustLooseObjectStat == TrustStat.ALWAYS) {
throw noFile;
}
try (InputStream stream = Files
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryPackParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryPackParser.java
index 9f27f4b..746e124 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryPackParser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryPackParser.java
@@ -28,6 +28,7 @@
import org.eclipse.jgit.errors.LockFailedException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.pack.PackExt;
+import org.eclipse.jgit.internal.storage.pack.PackIndexWriter;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.CoreConfig;
@@ -110,7 +111,7 @@ public class ObjectDirectoryPackParser extends PackParser {
* @param version
* the version to write. The special version 0 designates the
* oldest (most compatible) format available for the objects.
- * @see PackIndexWriter
+ * @see BasePackIndexWriter
*/
public void setIndexVersion(int version) {
indexVersion = version;
@@ -386,9 +387,9 @@ private void writeIdx() throws IOException {
try (FileOutputStream os = new FileOutputStream(tmpIdx)) {
final PackIndexWriter iw;
if (indexVersion <= 0)
- iw = PackIndexWriter.createOldestPossible(os, list);
+ iw = BasePackIndexWriter.createOldestPossible(os, list);
else
- iw = PackIndexWriter.createVersion(os, indexVersion);
+ iw = BasePackIndexWriter.createVersion(os, indexVersion);
iw.write(list, packHash);
os.getChannel().force(true);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/Pack.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/Pack.java
index f87329c..5813d39 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/Pack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/Pack.java
@@ -95,6 +95,9 @@ public class Pack implements Iterable<PackIndex.MutableEntry> {
private RandomAccessFile fd;
+ /** For managing open/close accounting of {@link #fd}. */
+ private final Object activeLock = new Object();
+
/** Serializes reads performed against {@link #fd}. */
private final Object readLock = new Object();
@@ -113,13 +116,13 @@ public class Pack implements Iterable<PackIndex.MutableEntry> {
private volatile Exception invalidatingCause;
@Nullable
- private PackFile bitmapIdxFile;
+ private volatile PackFile bitmapIdxFile;
private AtomicInteger transientErrorCount = new AtomicInteger();
private byte[] packChecksum;
- private volatile Optionally<PackIndex> loadedIdx = Optionally.empty();
+ private Optionally<PackIndex> loadedIdx = Optionally.empty();
private Optionally<PackReverseIndex> reverseIdx = Optionally.empty();
@@ -159,60 +162,54 @@ public Pack(Config cfg, File packFile, @Nullable PackFile bitmapIdxFile) {
length = Long.MAX_VALUE;
}
- private PackIndex idx() throws IOException {
+ private synchronized PackIndex idx() throws IOException {
Optional<PackIndex> optional = loadedIdx.getOptional();
if (optional.isPresent()) {
return optional.get();
}
- synchronized (this) {
- optional = loadedIdx.getOptional();
- if (optional.isPresent()) {
- return optional.get();
+ if (invalid) {
+ throw new PackInvalidException(packFile, invalidatingCause);
+ }
+ try {
+ long start = System.currentTimeMillis();
+ PackFile idxFile = packFile.create(INDEX);
+ PackIndex idx = PackIndex.open(idxFile);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(String.format(
+ "Opening pack index %s, size %.3f MB took %d ms", //$NON-NLS-1$
+ idxFile.getAbsolutePath(),
+ Float.valueOf(idxFile.length()
+ / (1024f * 1024)),
+ Long.valueOf(System.currentTimeMillis()
+ - start)));
}
- if (invalid) {
- throw new PackInvalidException(packFile, invalidatingCause);
- }
- try {
- long start = System.currentTimeMillis();
- PackFile idxFile = packFile.create(INDEX);
- PackIndex idx = PackIndex.open(idxFile);
- if (LOG.isDebugEnabled()) {
- LOG.debug(String.format(
- "Opening pack index %s, size %.3f MB took %d ms", //$NON-NLS-1$
- idxFile.getAbsolutePath(),
- Float.valueOf(idxFile.length()
- / (1024f * 1024)),
- Long.valueOf(System.currentTimeMillis()
- - start)));
- }
-
if (packChecksum == null) {
- packChecksum = idx.packChecksum;
- fileSnapshot.setChecksum(
- ObjectId.fromRaw(packChecksum));
- } else if (!Arrays.equals(packChecksum,
- idx.packChecksum)) {
- throw new PackMismatchException(MessageFormat
- .format(JGitText.get().packChecksumMismatch,
- packFile.getPath(),
- PackExt.PACK.getExtension(),
- Hex.toHexString(packChecksum),
- PackExt.INDEX.getExtension(),
- Hex.toHexString(idx.packChecksum)));
- }
- loadedIdx = optionally(idx);
- return idx;
- } catch (InterruptedIOException e) {
- // don't invalidate the pack, we are interrupted from
- // another thread
- throw e;
- } catch (IOException e) {
- invalid = true;
- invalidatingCause = e;
- throw e;
+ packChecksum = idx.getChecksum();
+ fileSnapshot.setChecksum(
+ ObjectId.fromRaw(packChecksum));
+ } else if (!Arrays.equals(packChecksum,
+ idx.getChecksum())) {
+ throw new PackMismatchException(MessageFormat
+ .format(JGitText.get().packChecksumMismatch,
+ packFile.getPath(),
+ PackExt.PACK.getExtension(),
+ Hex.toHexString(packChecksum),
+ PackExt.INDEX.getExtension(),
+ Hex.toHexString(idx.getChecksum())));
}
+ loadedIdx = optionally(idx);
+ return idx;
+ } catch (InterruptedIOException e) {
+ // don't invalidate the pack, we are interrupted from
+ // another thread
+ throw e;
+ } catch (IOException e) {
+ invalid = true;
+ invalidatingCause = e;
+ throw e;
}
}
+
/**
* Get the File object which locates this pack on disk.
*
@@ -296,15 +293,28 @@ void resolve(Set<ObjectId> matches, AbbreviatedObjectId id, int matchLimit)
}
/**
- * Close the resources utilized by this repository
+ * Close the resources utilized by these pack files
+ *
+ * @param packs
+ * packs to close
+ */
+ public static void close(Set<Pack> packs) {
+ WindowCache.purge(packs);
+ packs.forEach(p -> p.closeIndices());
+ }
+
+ /**
+ * Close the resources utilized by this pack file
*/
public void close() {
WindowCache.purge(this);
- synchronized (this) {
- loadedIdx.clear();
- reverseIdx.clear();
- bitmapIdx.clear();
- }
+ closeIndices();
+ }
+
+ private synchronized void closeIndices() {
+ loadedIdx.clear();
+ reverseIdx.clear();
+ bitmapIdx.clear();
}
/**
@@ -416,185 +426,202 @@ private void copyAsIs2(PackOutputStream out, LocalObjectToPack src,
final CRC32 crc2 = validate ? new CRC32() : null;
final byte[] buf = out.getCopyBuffer();
+ boolean isHeaderWritten = false;
// Rip apart the header so we can discover the size.
//
- readFully(src.offset, buf, 0, 20, curs);
- int c = buf[0] & 0xff;
- final int typeCode = (c >> 4) & 7;
- long inflatedLength = c & 15;
- int shift = 4;
- int headerCnt = 1;
- while ((c & 0x80) != 0) {
- c = buf[headerCnt++] & 0xff;
- inflatedLength += ((long) (c & 0x7f)) << shift;
- shift += 7;
- }
-
- if (typeCode == Constants.OBJ_OFS_DELTA) {
- do {
- c = buf[headerCnt++] & 0xff;
- } while ((c & 128) != 0);
- if (validate) {
- assert(crc1 != null && crc2 != null);
- crc1.update(buf, 0, headerCnt);
- crc2.update(buf, 0, headerCnt);
- }
- } else if (typeCode == Constants.OBJ_REF_DELTA) {
- if (validate) {
- assert(crc1 != null && crc2 != null);
- crc1.update(buf, 0, headerCnt);
- crc2.update(buf, 0, headerCnt);
- }
-
- readFully(src.offset + headerCnt, buf, 0, 20, curs);
- if (validate) {
- assert(crc1 != null && crc2 != null);
- crc1.update(buf, 0, 20);
- crc2.update(buf, 0, 20);
- }
- headerCnt += 20;
- } else if (validate) {
- assert(crc1 != null && crc2 != null);
- crc1.update(buf, 0, headerCnt);
- crc2.update(buf, 0, headerCnt);
- }
-
- final long dataOffset = src.offset + headerCnt;
- final long dataLength = src.length;
- final long expectedCRC;
- final ByteArrayWindow quickCopy;
-
- // Verify the object isn't corrupt before sending. If it is,
- // we report it missing instead.
- //
try {
- quickCopy = curs.quickCopy(this, dataOffset, dataLength);
+ readFully(src.offset, buf, 0, 20, curs);
- if (validate && idx().hasCRC32Support()) {
- assert(crc1 != null);
- // Index has the CRC32 code cached, validate the object.
- //
- expectedCRC = idx().findCRC32(src);
- if (quickCopy != null) {
- quickCopy.crc32(crc1, dataOffset, (int) dataLength);
- } else {
- long pos = dataOffset;
- long cnt = dataLength;
- while (cnt > 0) {
- final int n = (int) Math.min(cnt, buf.length);
- readFully(pos, buf, 0, n, curs);
- crc1.update(buf, 0, n);
- pos += n;
- cnt -= n;
- }
- }
- if (crc1.getValue() != expectedCRC) {
- setCorrupt(src.offset);
- throw new CorruptObjectException(MessageFormat.format(
- JGitText.get().objectAtHasBadZlibStream,
- Long.valueOf(src.offset), getPackFile()));
- }
- } else if (validate) {
- // We don't have a CRC32 code in the index, so compute it
- // now while inflating the raw data to get zlib to tell us
- // whether or not the data is safe.
- //
- Inflater inf = curs.inflater();
- byte[] tmp = new byte[1024];
- if (quickCopy != null) {
- quickCopy.check(inf, tmp, dataOffset, (int) dataLength);
- } else {
- assert(crc1 != null);
- long pos = dataOffset;
- long cnt = dataLength;
- while (cnt > 0) {
- final int n = (int) Math.min(cnt, buf.length);
- readFully(pos, buf, 0, n, curs);
- crc1.update(buf, 0, n);
- inf.setInput(buf, 0, n);
- while (inf.inflate(tmp, 0, tmp.length) > 0)
- continue;
- pos += n;
- cnt -= n;
- }
- }
- if (!inf.finished() || inf.getBytesRead() != dataLength) {
- setCorrupt(src.offset);
- throw new EOFException(MessageFormat.format(
- JGitText.get().shortCompressedStreamAt,
- Long.valueOf(src.offset)));
- }
- assert(crc1 != null);
- expectedCRC = crc1.getValue();
- } else {
- expectedCRC = -1;
+ int c = buf[0] & 0xff;
+ final int typeCode = (c >> 4) & 7;
+ long inflatedLength = c & 15;
+ int shift = 4;
+ int headerCnt = 1;
+ while ((c & 0x80) != 0) {
+ c = buf[headerCnt++] & 0xff;
+ inflatedLength += ((long) (c & 0x7f)) << shift;
+ shift += 7;
}
- } catch (DataFormatException dataFormat) {
- setCorrupt(src.offset);
- CorruptObjectException corruptObject = new CorruptObjectException(
- MessageFormat.format(
- JGitText.get().objectAtHasBadZlibStream,
- Long.valueOf(src.offset), getPackFile()),
- dataFormat);
+ if (typeCode == Constants.OBJ_OFS_DELTA) {
+ do {
+ c = buf[headerCnt++] & 0xff;
+ } while ((c & 128) != 0);
+ if (validate) {
+ assert(crc1 != null && crc2 != null);
+ crc1.update(buf, 0, headerCnt);
+ crc2.update(buf, 0, headerCnt);
+ }
+ } else if (typeCode == Constants.OBJ_REF_DELTA) {
+ if (validate) {
+ assert(crc1 != null && crc2 != null);
+ crc1.update(buf, 0, headerCnt);
+ crc2.update(buf, 0, headerCnt);
+ }
- throw new StoredObjectRepresentationNotAvailableException(
- corruptObject);
+ readFully(src.offset + headerCnt, buf, 0, 20, curs);
+ if (validate) {
+ assert(crc1 != null && crc2 != null);
+ crc1.update(buf, 0, 20);
+ crc2.update(buf, 0, 20);
+ }
+ headerCnt += 20;
+ } else if (validate) {
+ assert(crc1 != null && crc2 != null);
+ crc1.update(buf, 0, headerCnt);
+ crc2.update(buf, 0, headerCnt);
+ }
- } catch (IOException ioError) {
- throw new StoredObjectRepresentationNotAvailableException(ioError);
- }
+ final long dataOffset = src.offset + headerCnt;
+ final long dataLength = src.length;
+ final long expectedCRC;
+ final ByteArrayWindow quickCopy;
- if (quickCopy != null) {
- // The entire object fits into a single byte array window slice,
- // and we have it pinned. Write this out without copying.
+ // Verify the object isn't corrupt before sending. If it is,
+ // we report it missing instead.
//
- out.writeHeader(src, inflatedLength);
- quickCopy.write(out, dataOffset, (int) dataLength);
+ try {
+ quickCopy = curs.quickCopy(this, dataOffset, dataLength);
- } else if (dataLength <= buf.length) {
- // Tiny optimization: Lots of objects are very small deltas or
- // deflated commits that are likely to fit in the copy buffer.
- //
- if (!validate) {
+ if (validate && idx().hasCRC32Support()) {
+ assert(crc1 != null);
+ // Index has the CRC32 code cached, validate the object.
+ //
+ expectedCRC = idx().findCRC32(src);
+ if (quickCopy != null) {
+ quickCopy.crc32(crc1, dataOffset, (int) dataLength);
+ } else {
+ long pos = dataOffset;
+ long cnt = dataLength;
+ while (cnt > 0) {
+ final int n = (int) Math.min(cnt, buf.length);
+ readFully(pos, buf, 0, n, curs);
+ crc1.update(buf, 0, n);
+ pos += n;
+ cnt -= n;
+ }
+ }
+ if (crc1.getValue() != expectedCRC) {
+ setCorrupt(src.offset);
+ throw new CorruptObjectException(MessageFormat.format(
+ JGitText.get().objectAtHasBadZlibStream,
+ Long.valueOf(src.offset), getPackFile()));
+ }
+ } else if (validate) {
+ // We don't have a CRC32 code in the index, so compute it
+ // now while inflating the raw data to get zlib to tell us
+ // whether or not the data is safe.
+ //
+ Inflater inf = curs.inflater();
+ byte[] tmp = new byte[1024];
+ if (quickCopy != null) {
+ quickCopy.check(inf, tmp, dataOffset, (int) dataLength);
+ } else {
+ assert(crc1 != null);
+ long pos = dataOffset;
+ long cnt = dataLength;
+ while (cnt > 0) {
+ final int n = (int) Math.min(cnt, buf.length);
+ readFully(pos, buf, 0, n, curs);
+ crc1.update(buf, 0, n);
+ inf.setInput(buf, 0, n);
+ while (inf.inflate(tmp, 0, tmp.length) > 0)
+ continue;
+ pos += n;
+ cnt -= n;
+ }
+ }
+ if (!inf.finished() || inf.getBytesRead() != dataLength) {
+ setCorrupt(src.offset);
+ throw new EOFException(MessageFormat.format(
+ JGitText.get().shortCompressedStreamAt,
+ Long.valueOf(src.offset)));
+ }
+ assert(crc1 != null);
+ expectedCRC = crc1.getValue();
+ } else {
+ expectedCRC = -1;
+ }
+ } catch (DataFormatException dataFormat) {
+ setCorrupt(src.offset);
+
+ CorruptObjectException corruptObject = new CorruptObjectException(
+ MessageFormat.format(
+ JGitText.get().objectAtHasBadZlibStream,
+ Long.valueOf(src.offset), getPackFile()),
+ dataFormat);
+
+ throw new StoredObjectRepresentationNotAvailableException(
+ corruptObject);
+ }
+
+ if (quickCopy != null) {
+ // The entire object fits into a single byte array window slice,
+ // and we have it pinned. Write this out without copying.
+ //
+ out.writeHeader(src, inflatedLength);
+ isHeaderWritten = true;
+ quickCopy.write(out, dataOffset, (int) dataLength);
+
+ } else if (dataLength <= buf.length) {
+ // Tiny optimization: Lots of objects are very small deltas or
+ // deflated commits that are likely to fit in the copy buffer.
+ //
+ if (!validate) {
+ long pos = dataOffset;
+ long cnt = dataLength;
+ while (cnt > 0) {
+ final int n = (int) Math.min(cnt, buf.length);
+ readFully(pos, buf, 0, n, curs);
+ pos += n;
+ cnt -= n;
+ }
+ }
+ out.writeHeader(src, inflatedLength);
+ isHeaderWritten = true;
+ out.write(buf, 0, (int) dataLength);
+ } else {
+ // Now we are committed to sending the object. As we spool it out,
+ // check its CRC32 code to make sure there wasn't corruption between
+ // the verification we did above, and us actually outputting it.
+ //
long pos = dataOffset;
long cnt = dataLength;
while (cnt > 0) {
final int n = (int) Math.min(cnt, buf.length);
readFully(pos, buf, 0, n, curs);
- pos += n;
+ if (validate) {
+ assert(crc2 != null);
+ crc2.update(buf, 0, n);
+ }
cnt -= n;
+ if (!isHeaderWritten) {
+ if (invalid && cnt > 0) {
+ // Since this is not the last iteration and the packfile is invalid,
+ // better to assume the iterations will not all complete here while
+ // it is still likely recoverable.
+ throw new StoredObjectRepresentationNotAvailableException(invalidatingCause);
+ }
+ out.writeHeader(src, inflatedLength);
+ isHeaderWritten = true;
+ }
+ out.write(buf, 0, n);
+ pos += n;
}
- }
- out.writeHeader(src, inflatedLength);
- out.write(buf, 0, (int) dataLength);
- } else {
- // Now we are committed to sending the object. As we spool it out,
- // check its CRC32 code to make sure there wasn't corruption between
- // the verification we did above, and us actually outputting it.
- //
- out.writeHeader(src, inflatedLength);
- long pos = dataOffset;
- long cnt = dataLength;
- while (cnt > 0) {
- final int n = (int) Math.min(cnt, buf.length);
- readFully(pos, buf, 0, n, curs);
if (validate) {
assert(crc2 != null);
- crc2.update(buf, 0, n);
- }
- out.write(buf, 0, n);
- pos += n;
- cnt -= n;
- }
- if (validate) {
- assert(crc2 != null);
- if (crc2.getValue() != expectedCRC) {
- throw new CorruptObjectException(MessageFormat.format(
- JGitText.get().objectAtHasBadZlibStream,
- Long.valueOf(src.offset), getPackFile()));
+ if (crc2.getValue() != expectedCRC) {
+ throw new CorruptObjectException(MessageFormat.format(
+ JGitText.get().objectAtHasBadZlibStream,
+ Long.valueOf(src.offset), getPackFile()));
+ }
}
}
+ } catch (IOException ioError) {
+ if (!isHeaderWritten) {
+ throw new StoredObjectRepresentationNotAvailableException(ioError);
+ }
+ throw ioError;
}
}
@@ -621,42 +648,53 @@ private void readFully(final long position, final byte[] dstbuf,
throw new EOFException();
}
- private synchronized void beginCopyAsIs()
+ private void beginCopyAsIs()
throws StoredObjectRepresentationNotAvailableException {
- if (++activeCopyRawData == 1 && activeWindows == 0) {
- try {
- doOpen();
- } catch (IOException thisPackNotValid) {
- throw new StoredObjectRepresentationNotAvailableException(
- thisPackNotValid);
+ synchronized (activeLock) {
+ if (++activeCopyRawData == 1 && activeWindows == 0) {
+ try {
+ doOpen();
+ } catch (IOException thisPackNotValid) {
+ throw new StoredObjectRepresentationNotAvailableException(
+ thisPackNotValid);
+ }
}
}
}
- private synchronized void endCopyAsIs() {
- if (--activeCopyRawData == 0 && activeWindows == 0)
- doClose();
- }
-
- synchronized boolean beginWindowCache() throws IOException {
- if (++activeWindows == 1) {
- if (activeCopyRawData == 0)
- doOpen();
- return true;
+ private void endCopyAsIs() {
+ synchronized (activeLock) {
+ if (--activeCopyRawData == 0 && activeWindows == 0) {
+ doClose();
+ }
}
- return false;
}
- synchronized boolean endWindowCache() {
- final boolean r = --activeWindows == 0;
- if (r && activeCopyRawData == 0)
- doClose();
- return r;
+ boolean beginWindowCache() throws IOException {
+ synchronized (activeLock) {
+ if (++activeWindows == 1) {
+ if (activeCopyRawData == 0) {
+ doOpen();
+ }
+ return true;
+ }
+ return false;
+ }
+ }
+
+ boolean endWindowCache() {
+ synchronized (activeLock) {
+ boolean r = --activeWindows == 0;
+ if (r && activeCopyRawData == 0) {
+ doClose();
+ }
+ return r;
+ }
}
private void doOpen() throws IOException {
if (invalid) {
- openFail(true, invalidatingCause);
+ openFail(invalidatingCause);
throw new PackInvalidException(packFile, invalidatingCause);
}
try {
@@ -667,39 +705,41 @@ private void doOpen() throws IOException {
}
} catch (InterruptedIOException e) {
// don't invalidate the pack, we are interrupted from another thread
- openFail(false, e);
+ openFail(e);
throw e;
} catch (FileNotFoundException fn) {
- // don't invalidate the pack if opening an existing file failed
- // since it may be related to a temporary lack of resources (e.g.
- // max open files)
- openFail(!packFile.exists(), fn);
+ if (!packFile.exists()) {
+ // Failure to open an existing file may be related to a temporary lack of resources
+ // (e.g. max open files)
+ invalid = true;
+ }
+ openFail(fn);
throw fn;
} catch (EOFException | AccessDeniedException | NoSuchFileException
| CorruptObjectException | NoPackSignatureException
| PackMismatchException | UnpackException
| UnsupportedPackIndexVersionException
| UnsupportedPackVersionException pe) {
- // exceptions signaling permanent problems with a pack
- openFail(true, pe);
+ invalid = true; // exceptions signaling permanent problems with a pack
+ openFail(pe);
throw pe;
} catch (IOException ioe) {
- // mark this packfile as invalid when NFS stale file handle error
- // occur
- openFail(FileUtils.isStaleFileHandleInCausalChain(ioe), ioe);
+ if (FileUtils.isStaleFileHandleInCausalChain(ioe)) {
+ invalid = true;
+ }
+ openFail(ioe);
throw ioe;
} catch (RuntimeException ge) {
// generic exceptions could be transient so we should not mark the
// pack invalid to avoid false MissingObjectExceptions
- openFail(false, ge);
+ openFail(ge);
throw ge;
}
}
- private void openFail(boolean invalidate, Exception cause) {
+ private void openFail(Exception cause) {
activeWindows = 0;
activeCopyRawData = 0;
- invalid = invalidate;
invalidatingCause = cause;
doClose();
}
@@ -791,7 +831,7 @@ private void onOpenPack() throws IOException {
MessageFormat.format(JGitText.get().packChecksumMismatch,
getPackFile(), PackExt.PACK.getExtension(),
Hex.toHexString(buf), PackExt.INDEX.getExtension(),
- Hex.toHexString(idx.packChecksum)));
+ Hex.toHexString(idx.getChecksum())));
}
}
@@ -1173,17 +1213,8 @@ synchronized PackBitmapIndex getBitmapIndex() throws IOException {
return null;
}
- synchronized void refreshBitmapIndex(PackFile bitmapIndexFile) {
- this.bitmapIdx = Optionally.empty();
- this.invalid = false;
+ void setBitmapIndexFile(PackFile bitmapIndexFile) {
this.bitmapIdxFile = bitmapIndexFile;
- try {
- getBitmapIndex();
- } catch (IOException e) {
- LOG.warn(JGitText.get().bitmapFailedToGet, bitmapIdxFile, e);
- this.bitmapIdx = Optionally.empty();
- this.bitmapIdxFile = null;
- }
}
private synchronized PackReverseIndex getReverseIdx() throws IOException {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackDirectory.java
index 8221cff..f50c17e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackDirectory.java
@@ -17,6 +17,8 @@
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
@@ -24,6 +26,7 @@
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -41,7 +44,8 @@
import org.eclipse.jgit.lib.AbbreviatedObjectId;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.ConfigConstants;
+import org.eclipse.jgit.lib.CoreConfig;
+import org.eclipse.jgit.lib.CoreConfig.TrustStat;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.util.FileUtils;
@@ -71,7 +75,7 @@ class PackDirectory {
private final AtomicReference<PackList> packList;
- private final boolean trustFolderStat;
+ private final TrustStat trustPackStat;
/**
* Initialize a reference to an on-disk 'pack' directory.
@@ -85,14 +89,7 @@ class PackDirectory {
this.config = config;
this.directory = directory;
packList = new AtomicReference<>(NO_PACKS);
-
- // Whether to trust the pack folder's modification time. If set to false
- // we will always scan the .git/objects/pack folder to check for new
- // pack files. If set to true (default) we use the folder's size,
- // modification time, and key (inode) and assume that no new pack files
- // can be in this folder if these attributes have not changed.
- trustFolderStat = config.getBoolean(ConfigConstants.CONFIG_CORE_SECTION,
- ConfigConstants.CONFIG_KEY_TRUSTFOLDERSTAT, true);
+ trustPackStat = config.get(CoreConfig.KEY).getTrustPackStat();
}
/**
@@ -111,9 +108,7 @@ void create() throws IOException {
void close() {
PackList packs = packList.get();
if (packs != NO_PACKS && packList.compareAndSet(packs, NO_PACKS)) {
- for (Pack p : packs.packs) {
- p.close();
- }
+ Pack.close(Set.of(packs.packs));
}
}
@@ -314,38 +309,42 @@ private int checkRescanPackThreshold(int retries, PackMismatchException e)
}
private void handlePackError(IOException e, Pack p) {
- String warnTmpl = null;
+ String warnTemplate = null;
+ String debugTemplate = null;
int transientErrorCount = 0;
- String errTmpl = JGitText.get().exceptionWhileReadingPack;
+ String errorTemplate = JGitText.get().exceptionWhileReadingPack;
if ((e instanceof CorruptObjectException)
|| (e instanceof PackInvalidException)) {
- warnTmpl = JGitText.get().corruptPack;
- LOG.warn(MessageFormat.format(warnTmpl,
+ warnTemplate = JGitText.get().corruptPack;
+ LOG.warn(MessageFormat.format(warnTemplate,
p.getPackFile().getAbsolutePath()), e);
// Assume the pack is corrupted, and remove it from the list.
remove(p);
} else if (e instanceof FileNotFoundException) {
if (p.getPackFile().exists()) {
- errTmpl = JGitText.get().packInaccessible;
+ errorTemplate = JGitText.get().packInaccessible;
transientErrorCount = p.incrementTransientErrorCount();
} else {
- warnTmpl = JGitText.get().packWasDeleted;
+ debugTemplate = JGitText.get().packWasDeleted;
remove(p);
}
} else if (FileUtils.isStaleFileHandleInCausalChain(e)) {
- warnTmpl = JGitText.get().packHandleIsStale;
+ warnTemplate = JGitText.get().packHandleIsStale;
remove(p);
} else {
transientErrorCount = p.incrementTransientErrorCount();
}
- if (warnTmpl != null) {
- LOG.warn(MessageFormat.format(warnTmpl,
+ if (warnTemplate != null) {
+ LOG.warn(MessageFormat.format(warnTemplate,
p.getPackFile().getAbsolutePath()), e);
+ } else if (debugTemplate != null) {
+ LOG.debug(MessageFormat.format(debugTemplate,
+ p.getPackFile().getAbsolutePath()), e);
} else {
if (doLogExponentialBackoff(transientErrorCount)) {
// Don't remove the pack from the list, as the error may be
// transient.
- LOG.error(MessageFormat.format(errTmpl,
+ LOG.error(MessageFormat.format(errorTemplate,
p.getPackFile().getAbsolutePath(),
Integer.valueOf(transientErrorCount)), e);
}
@@ -362,8 +361,26 @@ private boolean doLogExponentialBackoff(int n) {
}
boolean searchPacksAgain(PackList old) {
- return (!trustFolderStat || old.snapshot.isModified(directory))
- && old != scanPacks(old);
+ switch (trustPackStat) {
+ case NEVER:
+ break;
+ case AFTER_OPEN:
+ try (InputStream stream = Files
+ .newInputStream(directory.toPath())) {
+ // open the pack directory to refresh attributes (on some NFS clients)
+ } catch (IOException e) {
+ // ignore
+ }
+ //$FALL-THROUGH$
+ case ALWAYS:
+ if (!old.snapshot.isModified(directory)) {
+ return false;
+ }
+ break;
+ case INHERIT:
+ // only used in CoreConfig internally
+ }
+ return old != scanPacks(old);
}
void insert(Pack pack) {
@@ -460,12 +477,9 @@ private PackList scanPacksImpl(PackList old) {
&& !oldPack.getFileSnapshot().isModified(packFile)) {
forReuse.remove(packFile.getName());
list.add(oldPack);
- try {
- if(oldPack.getBitmapIndex() == null) {
- oldPack.refreshBitmapIndex(packFilesByExt.get(BITMAP_INDEX));
- }
- } catch (IOException e) {
- LOG.warn(JGitText.get().bitmapAccessErrorForPackfile, oldPack.getPackName(), e);
+ PackFile bitMaps = packFilesByExt.get(BITMAP_INDEX);
+ if (bitMaps != null) {
+ oldPack.setBitmapIndexFile(bitMaps);
}
continue;
}
@@ -484,9 +498,7 @@ private PackList scanPacksImpl(PackList old) {
return old;
}
- for (Pack p : forReuse.values()) {
- p.close();
- }
+ Pack.close(new HashSet<>(forReuse.values()));
if (list.isEmpty()) {
return new PackList(snapshot, NO_PACKS.packs);
@@ -545,7 +557,7 @@ private Map<String, Map<PackExt, PackFile>> getPackFilesByExtById() {
for (String name : nameList) {
try {
PackFile pack = new PackFile(directory, name);
- if (pack.getPackExt() != null) {
+ if (pack.getPackExt() != null && !pack.isTmpGCFile()) {
Map<PackExt, PackFile> packByExt = packFilesByExtById
.get(pack.getId());
if (packByExt == null) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFile.java
index 19979d0..c9b05ad 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFile.java
@@ -27,6 +27,7 @@ public class PackFile extends File {
private static final long serialVersionUID = 1L;
private static final String PREFIX = "pack-"; //$NON-NLS-1$
+ private static final String TMP_GC_PREFIX = ".tmp-"; //$NON-NLS-1$
private final String base; // PREFIX + id i.e.
// pack-0123456789012345678901234567890123456789
@@ -126,6 +127,13 @@ public PackExt getPackExt() {
}
/**
+ * @return whether the file is a temporary GC file
+ */
+ public boolean isTmpGCFile() {
+ return id.startsWith(TMP_GC_PREFIX);
+ }
+
+ /**
* Create a new similar PackFile with the given extension instead.
*
* @param ext
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndex.java
index c2c3775..b3e4efb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndex.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndex.java
@@ -42,8 +42,8 @@
* by ObjectId.
* </p>
*/
-public abstract class PackIndex
- implements Iterable<PackIndex.MutableEntry>, ObjectIdSet {
+public interface PackIndex
+ extends Iterable<PackIndex.MutableEntry>, ObjectIdSet {
/**
* Open an existing pack <code>.idx</code> file for reading.
* <p>
@@ -61,7 +61,7 @@ public abstract class PackIndex
* the file exists but could not be read due to security errors,
* unrecognized data version, or unexpected data corruption.
*/
- public static PackIndex open(File idxFile) throws IOException {
+ static PackIndex open(File idxFile) throws IOException {
try (SilentFileInputStream fd = new SilentFileInputStream(
idxFile)) {
return read(fd);
@@ -92,7 +92,7 @@ public static PackIndex open(File idxFile) throws IOException {
* @throws org.eclipse.jgit.errors.CorruptObjectException
* the stream does not contain a valid pack index.
*/
- public static PackIndex read(InputStream fd) throws IOException,
+ static PackIndex read(InputStream fd) throws IOException,
CorruptObjectException {
final byte[] hdr = new byte[8];
IO.readFully(fd, hdr, 0, hdr.length);
@@ -109,16 +109,13 @@ public static PackIndex read(InputStream fd) throws IOException,
}
private static boolean isTOC(byte[] h) {
- final byte[] toc = PackIndexWriter.TOC;
+ final byte[] toc = BasePackIndexWriter.TOC;
for (int i = 0; i < toc.length; i++)
if (h[i] != toc[i])
return false;
return true;
}
- /** Footer checksum applied on the bottom of the pack file. */
- protected byte[] packChecksum;
-
/**
* Determine if an object is contained within the pack file.
*
@@ -126,12 +123,12 @@ private static boolean isTOC(byte[] h) {
* the object to look for. Must not be null.
* @return true if the object is listed in this index; false otherwise.
*/
- public boolean hasObject(AnyObjectId id) {
+ default boolean hasObject(AnyObjectId id) {
return findOffset(id) != -1;
}
@Override
- public boolean contains(AnyObjectId id) {
+ default boolean contains(AnyObjectId id) {
return findOffset(id) != -1;
}
@@ -147,7 +144,7 @@ public boolean contains(AnyObjectId id) {
* </p>
*/
@Override
- public abstract Iterator<MutableEntry> iterator();
+ Iterator<MutableEntry> iterator();
/**
* Obtain the total number of objects described by this index.
@@ -155,7 +152,7 @@ public boolean contains(AnyObjectId id) {
* @return number of objects in this index, and likewise in the associated
* pack that this index was generated from.
*/
- public abstract long getObjectCount();
+ long getObjectCount();
/**
* Obtain the total number of objects needing 64 bit offsets.
@@ -163,7 +160,7 @@ public boolean contains(AnyObjectId id) {
* @return number of objects in this index using a 64 bit offset; that is an
* object positioned after the 2 GB position within the file.
*/
- public abstract long getOffset64Count();
+ long getOffset64Count();
/**
* Get ObjectId for the n-th object entry returned by {@link #iterator()}.
@@ -185,7 +182,7 @@ public boolean contains(AnyObjectId id) {
* is 0, the second is 1, etc.
* @return the ObjectId for the corresponding entry.
*/
- public abstract ObjectId getObjectId(long nthPosition);
+ ObjectId getObjectId(long nthPosition);
/**
* Get ObjectId for the n-th object entry returned by {@link #iterator()}.
@@ -209,7 +206,7 @@ public boolean contains(AnyObjectId id) {
* negative, but still valid.
* @return the ObjectId for the corresponding entry.
*/
- public final ObjectId getObjectId(int nthPosition) {
+ default ObjectId getObjectId(int nthPosition) {
if (nthPosition >= 0)
return getObjectId((long) nthPosition);
final int u31 = nthPosition >>> 1;
@@ -228,7 +225,7 @@ public final ObjectId getObjectId(int nthPosition) {
* etc. Positions past 2**31-1 are negative, but still valid.
* @return the offset in a pack for the corresponding entry.
*/
- protected abstract long getOffset(long nthPosition);
+ long getOffset(long nthPosition);
/**
* Locate the file offset position for the requested object.
@@ -239,7 +236,7 @@ public final ObjectId getObjectId(int nthPosition) {
* object does not exist in this index and is thus not stored in the
* associated pack.
*/
- public abstract long findOffset(AnyObjectId objId);
+ long findOffset(AnyObjectId objId);
/**
* Locate the position of this id in the list of object-ids in the index
@@ -250,7 +247,7 @@ public final ObjectId getObjectId(int nthPosition) {
* of ids stored in this index; -1 if the object does not exist in
* this index and is thus not stored in the associated pack.
*/
- public abstract int findPosition(AnyObjectId objId);
+ int findPosition(AnyObjectId objId);
/**
* Retrieve stored CRC32 checksum of the requested object raw-data
@@ -264,7 +261,7 @@ public final ObjectId getObjectId(int nthPosition) {
* @throws java.lang.UnsupportedOperationException
* when this index doesn't support CRC32 checksum
*/
- public abstract long findCRC32(AnyObjectId objId)
+ long findCRC32(AnyObjectId objId)
throws MissingObjectException, UnsupportedOperationException;
/**
@@ -272,7 +269,7 @@ public abstract long findCRC32(AnyObjectId objId)
*
* @return true if CRC32 is stored, false otherwise
*/
- public abstract boolean hasCRC32Support();
+ boolean hasCRC32Support();
/**
* Find objects matching the prefix abbreviation.
@@ -288,8 +285,8 @@ public abstract long findCRC32(AnyObjectId objId)
* @throws java.io.IOException
* the index cannot be read.
*/
- public abstract void resolve(Set<ObjectId> matches, AbbreviatedObjectId id,
- int matchLimit) throws IOException;
+ void resolve(Set<ObjectId> matches, AbbreviatedObjectId id,
+ int matchLimit) throws IOException;
/**
* Get pack checksum
@@ -297,18 +294,18 @@ public abstract void resolve(Set<ObjectId> matches, AbbreviatedObjectId id,
* @return the checksum of the pack; caller must not modify it
* @since 5.5
*/
- public byte[] getChecksum() {
- return packChecksum;
- }
+ byte[] getChecksum();
/**
* Represent mutable entry of pack index consisting of object id and offset
* in pack (both mutable).
*
*/
- public static class MutableEntry {
+ class MutableEntry {
+ /** Buffer of the ObjectId visited by the EntriesIterator. */
final MutableObjectId idBuffer = new MutableObjectId();
+ /** Offset into the packfile of the current object. */
long offset;
/**
@@ -326,7 +323,6 @@ public long getOffset() {
* @return hex string describing the object id of this entry.
*/
public String name() {
- ensureId();
return idBuffer.name();
}
@@ -336,7 +332,6 @@ public String name() {
* @return a copy of the object id.
*/
public ObjectId toObjectId() {
- ensureId();
return idBuffer.toObjectId();
}
@@ -347,27 +342,64 @@ public ObjectId toObjectId() {
*/
public MutableEntry cloneEntry() {
final MutableEntry r = new MutableEntry();
- ensureId();
r.idBuffer.fromObjectId(idBuffer);
r.offset = offset;
return r;
}
- void ensureId() {
- // Override in implementations.
+ /**
+ * Similar to {@link Comparable#compareTo(Object)}, using only the
+ * object id in the entry.
+ *
+ * @param other
+ * Another mutable entry (probably from another index)
+ *
+ * @return a negative integer, zero, or a positive integer as this
+ * object is less than, equal to, or greater than the specified
+ * object.
+ */
+ public int compareBySha1To(MutableEntry other) {
+ return idBuffer.compareTo(other.idBuffer);
+ }
+
+ /**
+ * Copy the current ObjectId to dest
+ * <p>
+ * Like {@link #toObjectId()}, but reusing the destination instead of
+ * creating a new ObjectId instance.
+ *
+ * @param dest
+ * destination for the object id
+ */
+ public void copyOidTo(MutableObjectId dest) {
+ dest.fromObjectId(idBuffer);
}
}
+ /**
+ * Base implementation of the iterator over index entries.
+ */
abstract class EntriesIterator implements Iterator<MutableEntry> {
- protected final MutableEntry entry = initEntry();
+ private final long objectCount;
- protected long returnedNumber = 0;
+ private final MutableEntry entry = new MutableEntry();
- protected abstract MutableEntry initEntry();
+ /** Counts number of entries accessed so far. */
+ private long returnedNumber = 0;
+
+ /**
+ * Construct an iterator that can move objectCount times forward.
+ *
+ * @param objectCount
+ * the number of objects in the PackFile.
+ */
+ protected EntriesIterator(long objectCount) {
+ this.objectCount = objectCount;
+ }
@Override
public boolean hasNext() {
- return returnedNumber < getObjectCount();
+ return returnedNumber < objectCount;
}
/**
@@ -375,7 +407,55 @@ public boolean hasNext() {
* element.
*/
@Override
- public abstract MutableEntry next();
+ public MutableEntry next() {
+ readNext();
+ returnedNumber++;
+ return entry;
+ }
+
+ /**
+ * Used by subclasses to load the next entry into the MutableEntry.
+ * <p>
+ * Subclasses are expected to populate the entry with
+ * {@link #setIdBuffer} and {@link #setOffset}.
+ */
+ protected abstract void readNext();
+
+ /**
+ * Copies to the entry an {@link ObjectId} from the int buffer and
+ * position idx
+ *
+ * @param raw
+ * the raw data
+ * @param idx
+ * the index into {@code raw}
+ */
+ protected void setIdBuffer(int[] raw, int idx) {
+ entry.idBuffer.fromRaw(raw, idx);
+ }
+
+ /**
+ * Copies to the entry an {@link ObjectId} from the byte array at
+ * position idx.
+ *
+ * @param raw
+ * the raw data
+ * @param idx
+ * the index into {@code raw}
+ */
+ protected void setIdBuffer(byte[] raw, int idx) {
+ entry.idBuffer.fromRaw(raw, idx);
+ }
+
+ /**
+ * Sets the {@code offset} to the entry
+ *
+ * @param offset
+ * the offset in the pack file
+ */
+ protected void setOffset(long offset) {
+ entry.offset = offset;
+ }
@Override
public void remove() {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV1.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV1.java
index 5180df4..be48358 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV1.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV1.java
@@ -29,13 +29,16 @@
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.NB;
-class PackIndexV1 extends PackIndex {
+class PackIndexV1 implements PackIndex {
private static final int IDX_HDR_LEN = 256 * 4;
private static final int RECORD_SIZE = 4 + Constants.OBJECT_ID_LENGTH;
private final long[] idxHeader;
+ /** Footer checksum applied on the bottom of the pack file. */
+ protected byte[] packChecksum;
+
byte[][] idxdata;
private long objectCnt;
@@ -118,7 +121,7 @@ public ObjectId getObjectId(long nthPosition) {
}
@Override
- protected long getOffset(long nthPosition) {
+ public long getOffset(long nthPosition) {
final int levelOne = findLevelOne(nthPosition);
final int levelTwo = getLevelTwo(nthPosition, levelOne);
final int p = (4 + Constants.OBJECT_ID_LENGTH) * levelTwo;
@@ -200,7 +203,7 @@ public boolean hasCRC32Support() {
@Override
public Iterator<MutableEntry> iterator() {
- return new IndexV1Iterator();
+ return new EntriesIteratorV1(this);
}
@Override
@@ -238,32 +241,35 @@ private static int idOffset(int mid) {
return (RECORD_SIZE * mid) + 4;
}
- private class IndexV1Iterator extends EntriesIterator {
- int levelOne;
+ @Override
+ public byte[] getChecksum() {
+ return packChecksum;
+ }
- int levelTwo;
+ private static class EntriesIteratorV1 extends EntriesIterator {
+ private int levelOne;
- @Override
- protected MutableEntry initEntry() {
- return new MutableEntry() {
- @Override
- protected void ensureId() {
- idBuffer.fromRaw(idxdata[levelOne], levelTwo
- - Constants.OBJECT_ID_LENGTH);
- }
- };
+ private int levelTwo;
+
+ private final PackIndexV1 packIndex;
+
+ private EntriesIteratorV1(PackIndexV1 packIndex) {
+ super(packIndex.objectCnt);
+ this.packIndex = packIndex;
}
@Override
- public MutableEntry next() {
- for (; levelOne < idxdata.length; levelOne++) {
- if (idxdata[levelOne] == null)
+ protected void readNext() {
+ for (; levelOne < packIndex.idxdata.length; levelOne++) {
+ if (packIndex.idxdata[levelOne] == null)
continue;
- if (levelTwo < idxdata[levelOne].length) {
- entry.offset = NB.decodeUInt32(idxdata[levelOne], levelTwo);
- levelTwo += Constants.OBJECT_ID_LENGTH + 4;
- returnedNumber++;
- return entry;
+ if (levelTwo < packIndex.idxdata[levelOne].length) {
+ super.setOffset(NB.decodeUInt32(packIndex.idxdata[levelOne],
+ levelTwo));
+ this.levelTwo += Constants.OBJECT_ID_LENGTH + 4;
+ super.setIdBuffer(packIndex.idxdata[levelOne],
+ levelTwo - Constants.OBJECT_ID_LENGTH);
+ return;
}
levelTwo = 0;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV2.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV2.java
index 751b62d..36e54fc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV2.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV2.java
@@ -28,7 +28,7 @@
import org.eclipse.jgit.util.NB;
/** Support for the pack index v2 format. */
-class PackIndexV2 extends PackIndex {
+class PackIndexV2 implements PackIndex {
private static final long IS_O64 = 1L << 31;
private static final int FANOUT = 256;
@@ -37,6 +37,9 @@ class PackIndexV2 extends PackIndex {
private static final byte[] NO_BYTES = {};
+ /** Footer checksum applied on the bottom of the pack file. */
+ protected byte[] packChecksum;
+
private long objectCnt;
private final long[] fanoutTable;
@@ -221,7 +224,7 @@ public boolean hasCRC32Support() {
@Override
public Iterator<MutableEntry> iterator() {
- return new EntriesIteratorV2();
+ return new EntriesIteratorV2(this);
}
@Override
@@ -281,37 +284,39 @@ else if (cmp == 0) {
return -1;
}
- private class EntriesIteratorV2 extends EntriesIterator {
- int levelOne;
+ @Override
+ public byte[] getChecksum() {
+ return packChecksum;
+ }
- int levelTwo;
+ private static class EntriesIteratorV2 extends EntriesIterator {
+ private int levelOne = 0;
- @Override
- protected MutableEntry initEntry() {
- return new MutableEntry() {
- @Override
- protected void ensureId() {
- idBuffer.fromRaw(names[levelOne], levelTwo
- - Constants.OBJECT_ID_LENGTH / 4);
- }
- };
+ private int levelTwo = 0;
+
+ private final PackIndexV2 packIndex;
+
+ private EntriesIteratorV2(PackIndexV2 packIndex) {
+ super(packIndex.objectCnt);
+ this.packIndex = packIndex;
}
@Override
- public MutableEntry next() {
- for (; levelOne < names.length; levelOne++) {
- if (levelTwo < names[levelOne].length) {
+ protected void readNext() {
+ for (; levelOne < packIndex.names.length; levelOne++) {
+ if (levelTwo < packIndex.names[levelOne].length) {
int idx = levelTwo / (Constants.OBJECT_ID_LENGTH / 4) * 4;
- long offset = NB.decodeUInt32(offset32[levelOne], idx);
+ long offset = NB.decodeUInt32(packIndex.offset32[levelOne],
+ idx);
if ((offset & IS_O64) != 0) {
idx = (8 * (int) (offset & ~IS_O64));
- offset = NB.decodeUInt64(offset64, idx);
+ offset = NB.decodeUInt64(packIndex.offset64, idx);
}
- entry.offset = offset;
-
- levelTwo += Constants.OBJECT_ID_LENGTH / 4;
- returnedNumber++;
- return entry;
+ super.setOffset(offset);
+ this.levelTwo += Constants.OBJECT_ID_LENGTH / 4;
+ super.setIdBuffer(packIndex.names[levelOne],
+ levelTwo - Constants.OBJECT_ID_LENGTH / 4);
+ return;
}
levelTwo = 0;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexWriterV1.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexWriterV1.java
index 7e28b5e..f0b6193 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexWriterV1.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexWriterV1.java
@@ -21,10 +21,10 @@
/**
* Creates the version 1 (old style) pack table of contents files.
*
- * @see PackIndexWriter
+ * @see BasePackIndexWriter
* @see PackIndexV1
*/
-class PackIndexWriterV1 extends PackIndexWriter {
+class PackIndexWriterV1 extends BasePackIndexWriter {
static boolean canStore(PackedObjectInfo oe) {
// We are limited to 4 GB per pack as offset is 32 bit unsigned int.
//
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexWriterV2.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexWriterV2.java
index fc5ef61..b72b35a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexWriterV2.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexWriterV2.java
@@ -19,10 +19,10 @@
/**
* Creates the version 2 pack table of contents files.
*
- * @see PackIndexWriter
+ * @see BasePackIndexWriter
* @see PackIndexV2
*/
-class PackIndexWriterV2 extends PackIndexWriter {
+class PackIndexWriterV2 extends BasePackIndexWriter {
private static final int MAX_OFFSET_32 = 0x7fffffff;
private static final int IS_OFFSET_64 = 0x80000000;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackInserter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackInserter.java
index 1b092a3..55e047b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackInserter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackInserter.java
@@ -77,6 +77,7 @@
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.pack.PackExt;
+import org.eclipse.jgit.internal.storage.pack.PackIndexWriter;
import org.eclipse.jgit.lib.AbbreviatedObjectId;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
@@ -320,7 +321,8 @@ public void flush() throws IOException {
private static void writePackIndex(File idx, byte[] packHash,
List<PackedObjectInfo> list) throws IOException {
try (OutputStream os = new FileOutputStream(idx)) {
- PackIndexWriter w = PackIndexWriter.createVersion(os, INDEX_VERSION);
+ PackIndexWriter w = BasePackIndexWriter.createVersion(os,
+ INDEX_VERSION);
w.write(list, packHash);
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackObjectSizeIndexV1.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackObjectSizeIndexV1.java
index a3d74be..9957f54 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackObjectSizeIndexV1.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackObjectSizeIndexV1.java
@@ -12,7 +12,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
-import java.util.Arrays;
+import java.text.MessageFormat;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.util.NB;
@@ -35,7 +35,7 @@ class PackObjectSizeIndexV1 implements PackObjectSizeIndex {
private final UInt24Array positions24;
- private final int[] positions32;
+ private final IntArray positions32;
/**
* Parallel array to concat(positions24, positions32) with the size of the
@@ -45,35 +45,37 @@ class PackObjectSizeIndexV1 implements PackObjectSizeIndex {
* doesn't fit in an int and |value|-1 is the position for the size in the
* size64 array e.g. a value of -1 is sizes64[0], -2 = sizes64[1], ...
*/
- private final int[] sizes32;
+ private final IntArray sizes32;
- private final long[] sizes64;
+ private final LongArray sizes64;
static PackObjectSizeIndex parse(InputStream in) throws IOException {
/** Header and version already out of the input */
- IndexInputStreamReader stream = new IndexInputStreamReader(in);
- int threshold = stream.readInt(); // minSize
- int objCount = stream.readInt();
+ byte[] buffer = new byte[8];
+ in.readNBytes(buffer, 0, 8);
+ int threshold = NB.decodeInt32(buffer, 0); // minSize
+ int objCount = NB.decodeInt32(buffer, 4);
if (objCount == 0) {
return new EmptyPackObjectSizeIndex(threshold);
}
- return new PackObjectSizeIndexV1(stream, threshold, objCount);
+ return new PackObjectSizeIndexV1(in, threshold, objCount);
}
- private PackObjectSizeIndexV1(IndexInputStreamReader stream, int threshold,
+ private PackObjectSizeIndexV1(InputStream stream, int threshold,
int objCount) throws IOException {
this.threshold = threshold;
UInt24Array pos24 = null;
- int[] pos32 = null;
+ IntArray pos32 = null;
+ StreamHelper helper = new StreamHelper();
byte positionEncoding;
- while ((positionEncoding = stream.readByte()) != 0) {
+ while ((positionEncoding = helper.readByte(stream)) != 0) {
if (Byte.compareUnsigned(positionEncoding, BITS_24) == 0) {
- int sz = stream.readInt();
+ int sz = helper.readInt(stream);
pos24 = new UInt24Array(stream.readNBytes(sz * 3));
} else if (Byte.compareUnsigned(positionEncoding, BITS_32) == 0) {
- int sz = stream.readInt();
- pos32 = stream.readIntArray(sz);
+ int sz = helper.readInt(stream);
+ pos32 = IntArray.from(stream, sz);
} else {
throw new UnsupportedEncodingException(
String.format(JGitText.get().unknownPositionEncoding,
@@ -81,16 +83,16 @@ private PackObjectSizeIndexV1(IndexInputStreamReader stream, int threshold,
}
}
positions24 = pos24 != null ? pos24 : UInt24Array.EMPTY;
- positions32 = pos32 != null ? pos32 : new int[0];
+ positions32 = pos32 != null ? pos32 : IntArray.EMPTY;
- sizes32 = stream.readIntArray(objCount);
- int c64sizes = stream.readInt();
+ sizes32 = IntArray.from(stream, objCount);
+ int c64sizes = helper.readInt(stream);
if (c64sizes == 0) {
- sizes64 = new long[0];
+ sizes64 = LongArray.EMPTY;
return;
}
- sizes64 = stream.readLongArray(c64sizes);
- int c128sizes = stream.readInt();
+ sizes64 = LongArray.from(stream, c64sizes);
+ int c128sizes = helper.readInt(stream);
if (c128sizes != 0) {
// this MUST be 0 (we don't support 128 bits sizes yet)
throw new IOException(JGitText.get().unsupportedSizesObjSizeIndex);
@@ -102,8 +104,8 @@ public long getSize(int idxOffset) {
int pos = -1;
if (!positions24.isEmpty() && idxOffset <= positions24.getLastValue()) {
pos = positions24.binarySearch(idxOffset);
- } else if (positions32.length > 0 && idxOffset >= positions32[0]) {
- int pos32 = Arrays.binarySearch(positions32, idxOffset);
+ } else if (!positions32.empty() && idxOffset >= positions32.get(0)) {
+ int pos32 = positions32.binarySearch(idxOffset);
if (pos32 >= 0) {
pos = pos32 + positions24.size();
}
@@ -112,17 +114,17 @@ public long getSize(int idxOffset) {
return -1;
}
- int objSize = sizes32[pos];
+ int objSize = sizes32.get(pos);
if (objSize < 0) {
int secondPos = Math.abs(objSize) - 1;
- return sizes64[secondPos];
+ return sizes64.get(secondPos);
}
return objSize;
}
@Override
public long getObjectCount() {
- return (long) positions24.size() + positions32.length;
+ return (long) positions24.size() + positions32.size();
}
@Override
@@ -131,19 +133,114 @@ public int getThreshold() {
}
/**
- * Wrapper to read parsed content from the byte stream
+ * A byte[] that should be interpreted as an int[]
*/
- private static class IndexInputStreamReader {
+ private static class IntArray {
+ private static final IntArray EMPTY = new IntArray(new byte[0]);
- private final byte[] buffer = new byte[8];
+ private static final int INT_SIZE = 4;
- private final InputStream in;
+ private final byte[] data;
- IndexInputStreamReader(InputStream in) {
- this.in = in;
+ private final int size;
+
+ static IntArray from(InputStream in, int ints) throws IOException {
+ int expectedBytes = ints * INT_SIZE;
+ byte[] data = in.readNBytes(expectedBytes);
+ if (data.length < expectedBytes) {
+ throw new IOException(MessageFormat
+ .format(JGitText.get().unableToReadFullArray,
+ Integer.valueOf(ints)));
+ }
+ return new IntArray(data);
}
- int readInt() throws IOException {
+ private IntArray(byte[] data) {
+ this.data = data;
+ size = data.length / INT_SIZE;
+ }
+
+ /**
+ * Returns position of element in array, -1 if not there
+ *
+ * @param needle
+ * element to look for
+ * @return position of the element in the array or -1 if not found
+ */
+ int binarySearch(int needle) {
+ if (size == 0) {
+ return -1;
+ }
+ int high = size;
+ int low = 0;
+ do {
+ int mid = (low + high) >>> 1;
+ int cmp = Integer.compare(needle, get(mid));
+ if (cmp < 0)
+ high = mid;
+ else if (cmp == 0) {
+ return mid;
+ } else
+ low = mid + 1;
+ } while (low < high);
+ return -1;
+ }
+
+ int get(int position) {
+ if (position < 0 || position >= size) {
+ throw new IndexOutOfBoundsException(position);
+ }
+ return NB.decodeInt32(data, position * INT_SIZE);
+ }
+
+ boolean empty() {
+ return size == 0;
+ }
+
+ int size() {
+ return size;
+ }
+ }
+
+ /**
+ * A byte[] that should be interpreted as an long[]
+ */
+ private static class LongArray {
+ private static final LongArray EMPTY = new LongArray(new byte[0]);
+
+ private static final int LONG_SIZE = 8; // bytes
+
+ private final byte[] data;
+
+ private final int size;
+
+ static LongArray from(InputStream in, int longs) throws IOException {
+ byte[] data = in.readNBytes(longs * LONG_SIZE);
+ if (data.length < longs * LONG_SIZE) {
+ throw new IOException(MessageFormat
+ .format(JGitText.get().unableToReadFullArray,
+ Integer.valueOf(longs)));
+ }
+ return new LongArray(data);
+ }
+
+ private LongArray(byte[] data) {
+ this.data = data;
+ size = data.length / LONG_SIZE;
+ }
+
+ long get(int position) {
+ if (position < 0 || position >= size) {
+ throw new IndexOutOfBoundsException(position);
+ }
+ return NB.decodeInt64(data, position * LONG_SIZE);
+ }
+ }
+
+ private static class StreamHelper {
+ private final byte[] buffer = new byte[8];
+
+ int readInt(InputStream in) throws IOException {
int n = in.readNBytes(buffer, 0, 4);
if (n < 4) {
throw new IOException(JGitText.get().unableToReadFullInt);
@@ -151,49 +248,13 @@ int readInt() throws IOException {
return NB.decodeInt32(buffer, 0);
}
- int[] readIntArray(int intsCount) throws IOException {
- if (intsCount == 0) {
- return new int[0];
- }
-
- int[] dest = new int[intsCount];
- for (int i = 0; i < intsCount; i++) {
- dest[i] = readInt();
- }
- return dest;
- }
-
- long readLong() throws IOException {
- int n = in.readNBytes(buffer, 0, 8);
- if (n < 8) {
- throw new IOException(JGitText.get().unableToReadFullInt);
- }
- return NB.decodeInt64(buffer, 0);
- }
-
- long[] readLongArray(int longsCount) throws IOException {
- if (longsCount == 0) {
- return new long[0];
- }
-
- long[] dest = new long[longsCount];
- for (int i = 0; i < longsCount; i++) {
- dest[i] = readLong();
- }
- return dest;
- }
-
- byte readByte() throws IOException {
+ byte readByte(InputStream in) throws IOException {
int n = in.readNBytes(buffer, 0, 1);
if (n != 1) {
throw new IOException(JGitText.get().cannotReadByte);
}
return buffer[0];
}
-
- byte[] readNBytes(int sz) throws IOException {
- return in.readNBytes(sz);
- }
}
private static class EmptyPackObjectSizeIndex
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackReverseIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackReverseIndex.java
index ef9753c..720a3bc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackReverseIndex.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackReverseIndex.java
@@ -75,20 +75,20 @@ long findNextOffset(long offset, long maxOffset)
throws CorruptObjectException;
/**
- * Find the position in the primary index of the object at the given pack
+ * Find the position in the reverse index of the object at the given pack
* offset.
*
* @param offset
* the pack offset of the object
- * @return the position in the primary index of the object
+ * @return the position in the reverse index of the object
*/
int findPosition(long offset);
/**
- * Find the object that is in the given position in the primary index.
+ * Find the object that is in the given position in the reverse index.
*
* @param nthPosition
- * the position of the object in the primary index
+ * the position of the object in the reverse index
* @return the object in that position
*/
ObjectId findObjectByPosition(int nthPosition);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
index 8e57bf9..05f1ef5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
@@ -16,6 +16,7 @@
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.eclipse.jgit.lib.Constants.HEAD;
import static org.eclipse.jgit.lib.Constants.LOGS;
+import static org.eclipse.jgit.lib.Constants.L_LOGS;
import static org.eclipse.jgit.lib.Constants.OBJECT_ID_STRING_LENGTH;
import static org.eclipse.jgit.lib.Constants.PACKED_REFS;
import static org.eclipse.jgit.lib.Constants.R_HEADS;
@@ -56,23 +57,25 @@
import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.api.PackRefsCommand;
import org.eclipse.jgit.errors.InvalidObjectIdException;
import org.eclipse.jgit.errors.LockFailedException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.ObjectWritingException;
import org.eclipse.jgit.events.RefsChangedEvent;
import org.eclipse.jgit.internal.JGitText;
-import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.CoreConfig.TrustLooseRefStat;
-import org.eclipse.jgit.lib.CoreConfig.TrustPackedRefsStat;
+import org.eclipse.jgit.lib.CoreConfig;
+import org.eclipse.jgit.lib.CoreConfig.TrustStat;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdRef;
+import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefComparator;
import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.RefWriter;
+import org.eclipse.jgit.lib.ReflogReader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.SymbolicRef;
import org.eclipse.jgit.revwalk.RevObject;
@@ -124,6 +127,8 @@ public class RefDirectory extends RefDatabase {
private final File gitDir;
+ private final File gitCommonDir;
+
final File refsDir;
final File packedRefsFile;
@@ -179,24 +184,19 @@ public class RefDirectory extends RefDatabase {
private List<Integer> retrySleepMs = RETRY_SLEEP_MS;
- private final boolean trustFolderStat;
-
- private final TrustPackedRefsStat trustPackedRefsStat;
-
- private final TrustLooseRefStat trustLooseRefStat;
+ private final CoreConfig coreConfig;
RefDirectory(RefDirectory refDb) {
parent = refDb.parent;
gitDir = refDb.gitDir;
+ gitCommonDir = refDb.gitCommonDir;
refsDir = refDb.refsDir;
logsDir = refDb.logsDir;
logsRefsDir = refDb.logsRefsDir;
packedRefsFile = refDb.packedRefsFile;
looseRefs.set(refDb.looseRefs.get());
packedRefs.set(refDb.packedRefs.get());
- trustFolderStat = refDb.trustFolderStat;
- trustPackedRefsStat = refDb.trustPackedRefsStat;
- trustLooseRefStat = refDb.trustLooseRefStat;
+ coreConfig = refDb.coreConfig;
inProcessPackedRefsLock = refDb.inProcessPackedRefsLock;
}
@@ -204,24 +204,15 @@ public class RefDirectory extends RefDatabase {
final FS fs = db.getFS();
parent = db;
gitDir = db.getDirectory();
- refsDir = fs.resolve(gitDir, R_REFS);
- logsDir = fs.resolve(gitDir, LOGS);
- logsRefsDir = fs.resolve(gitDir, LOGS + '/' + R_REFS);
- packedRefsFile = fs.resolve(gitDir, PACKED_REFS);
+ gitCommonDir = db.getCommonDirectory();
+ refsDir = fs.resolve(gitCommonDir, R_REFS);
+ logsDir = fs.resolve(gitCommonDir, LOGS);
+ logsRefsDir = fs.resolve(gitCommonDir, L_LOGS + R_REFS);
+ packedRefsFile = fs.resolve(gitCommonDir, PACKED_REFS);
looseRefs.set(RefList.<LooseRef> emptyList());
packedRefs.set(NO_PACKED_REFS);
- trustFolderStat = db.getConfig()
- .getBoolean(ConfigConstants.CONFIG_CORE_SECTION,
- ConfigConstants.CONFIG_KEY_TRUSTFOLDERSTAT, true);
- trustPackedRefsStat = db.getConfig()
- .getEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
- ConfigConstants.CONFIG_KEY_TRUST_PACKED_REFS_STAT,
- TrustPackedRefsStat.UNSET);
- trustLooseRefStat = db.getConfig()
- .getEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
- ConfigConstants.CONFIG_KEY_TRUST_LOOSE_REF_STAT,
- TrustLooseRefStat.ALWAYS);
+ coreConfig = db.getConfig().get(CoreConfig.KEY);
inProcessPackedRefsLock = new ReentrantLock(true);
}
@@ -282,6 +273,33 @@ public void refresh() {
clearReferences();
}
+ /**
+ * {@inheritDoc}
+ *
+ * For a RefDirectory database, by default this packs non-symbolic, loose
+ * tag refs into packed-refs. If {@code all} flag is set, this packs all the
+ * non-symbolic, loose refs.
+ */
+ @Override
+ public void packRefs(ProgressMonitor pm, PackRefsCommand packRefs)
+ throws IOException {
+ String prefix = packRefs.isAll() ? R_REFS : R_TAGS;
+ Collection<Ref> refs = getRefsByPrefix(prefix);
+ List<String> refsToBePacked = new ArrayList<>(refs.size());
+ pm.beginTask(JGitText.get().packRefs, refs.size());
+ try {
+ for (Ref ref : refs) {
+ if (!ref.isSymbolic() && ref.getStorage().isLoose()) {
+ refsToBePacked.add(ref.getName());
+ }
+ pm.update(1);
+ }
+ pack(refsToBePacked);
+ } finally {
+ pm.endTask();
+ }
+ }
+
@Override
public boolean isNameConflicting(String name) throws IOException {
// Cannot be nested within an existing reference.
@@ -422,6 +440,11 @@ public List<Ref> getAdditionalRefs() throws IOException {
return ret;
}
+ @Override
+ public ReflogReader getReflogReader(Ref ref) throws IOException {
+ return new ReflogReaderImpl(getRepository(), ref.getName());
+ }
+
@SuppressWarnings("unchecked")
private RefList<Ref> upcast(RefList<? extends Ref> loose) {
return (RefList<Ref>) loose;
@@ -939,7 +962,7 @@ else if (0 <= (idx = packed.find(dst.getName())))
PackedRefList getPackedRefs() throws IOException {
final PackedRefList curList = packedRefs.get();
- switch (trustPackedRefsStat) {
+ switch (coreConfig.getTrustPackedRefsStat()) {
case NEVER:
break;
case AFTER_OPEN:
@@ -955,12 +978,8 @@ PackedRefList getPackedRefs() throws IOException {
return curList;
}
break;
- case UNSET:
- if (trustFolderStat
- && !curList.snapshot.isModified(packedRefsFile)) {
- return curList;
- }
- break;
+ case INHERIT:
+ // only used in CoreConfig internally
}
return refreshPackedRefs(curList);
@@ -1146,7 +1165,7 @@ private Ref readRef(String name, RefList<Ref> packed) throws IOException {
LooseRef scanRef(LooseRef ref, String name) throws IOException {
final File path = fileFor(name);
- if (trustLooseRefStat.equals(TrustLooseRefStat.AFTER_OPEN)) {
+ if (coreConfig.getTrustLooseRefStat() == TrustStat.AFTER_OPEN) {
refreshPathToLooseRef(Paths.get(name));
}
@@ -1329,7 +1348,12 @@ File fileFor(String name) {
name = name.substring(R_REFS.length());
return new File(refsDir, name);
}
- return new File(gitDir, name);
+ // HEAD needs to get resolved from git dir as resolving it from common dir
+ // would always lead back to current default branch
+ if (name.equals(HEAD)) {
+ return new File(gitDir, name);
+ }
+ return new File(gitCommonDir, name);
}
static int levelsIn(String name) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogReaderImpl.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogReaderImpl.java
index 21b5a54..f1888eb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogReaderImpl.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogReaderImpl.java
@@ -10,6 +10,8 @@
package org.eclipse.jgit.internal.storage.file;
+import static org.eclipse.jgit.lib.Constants.HEAD;
+
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -37,7 +39,9 @@ class ReflogReaderImpl implements ReflogReader {
* {@code Ref} name
*/
ReflogReaderImpl(Repository db, String refname) {
- logName = new File(db.getDirectory(), Constants.LOGS + '/' + refname);
+ File logBaseDir = refname.equals(HEAD) ? db.getDirectory()
+ : db.getCommonDirectory();
+ logName = new File(logBaseDir, Constants.L_LOGS + refname);
}
/* (non-Javadoc)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java
index 30f8240..15c125c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java
@@ -15,8 +15,10 @@
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.Collections;
+import java.util.HashSet;
import java.util.Map;
import java.util.Random;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -397,7 +399,11 @@ static final ByteWindow get(Pack pack, long offset)
}
static final void purge(Pack pack) {
- cache.removeAll(pack);
+ purge(Collections.singleton(pack));
+ }
+
+ static final void purge(Set<Pack> packs) {
+ cache.queueRemoveAll(packs);
}
/** cleanup released and/or garbage collected windows. */
@@ -441,6 +447,29 @@ static final void purge(Pack pack) {
private final boolean useStrongIndexRefs;
+ /** Removers are purely CPU/mem bound (no I/O), so likely should not go above # CPUs */
+ private final int idealNumRemovers;
+
+ /** Number of blocks to split the Pack removal into.
+ *
+ * Consolidation is better with more blocks since it increases
+ * the wait before moving to the next set by allowing more work
+ * to accumulate in the next set. On the flip side, the more
+ * blocks, the more synchronization overhead increasing each
+ * removers latency.
+ */
+ private final int numRemovalBlocks;
+
+ private final int removalBlockSize;
+
+ private Set<Pack> packsToRemove = new HashSet<>();
+
+ private Set<Pack> packsBeingRemoved;
+
+ private int numRemovers;
+
+ private int blockBeingRemoved;
+
private WindowCache(WindowCacheConfig cfg) {
tableSize = tableSize(cfg);
final int lockCount = lockCount(cfg);
@@ -479,6 +508,23 @@ else if (eb < 4)
statsRecorder = mbean;
publishMBean.set(cfg.getExposeStatsViaJmx());
+ /* Since each worker will only process up to one full set of blocks, at least 2
+ * workers are needed anytime there are queued removals to ensure that all the
+ * blocks will get processed. However, if workers are maxed out at only 2, then
+ * enough newer workers will never start in order to make it safe for older
+ * workers to quit early. At least 3 workers are needed to make older worker
+ * relief transitions possible.
+ */
+ idealNumRemovers = Math.max(3, Runtime.getRuntime().availableProcessors());
+
+ int bs = 1024;
+ if (tableSize < 2 * bs) {
+ bs = tableSize / 2;
+ }
+ removalBlockSize = bs;
+ numRemovalBlocks = tableSize / removalBlockSize;
+ blockBeingRemoved = numRemovalBlocks - 1;
+
if (maxFiles < 1)
throw new IllegalArgumentException(JGitText.get().openFilesMustBeAtLeast1);
if (maxBytes < windowSize)
@@ -708,22 +754,105 @@ private void removeAll() {
}
/**
- * Clear all entries related to a single file.
+ * Asynchronously clear all entries related to files.
* <p>
- * Typically this method is invoked during {@link Pack#close()}, when we
- * know the pack is never going to be useful to us again (for example, it no
- * longer exists on disk). A concurrent reader loading an entry from this
- * same pack may cause the pack to become stuck in the cache anyway.
+ * Typically this method is invoked during {@link Pack#close()}, or
+ * {@link Pack#close(Set)}, when we know the packs are never going to be
+ * useful to us again (for example, they no longer exist on disk after gc).
+ * A concurrent reader loading an entry from these same packs may cause a
+ * pack to become stuck in the cache anyway.
*
- * @param pack
- * the file to purge all entries of.
+ * Work on clearing files will be split up into blocks so that removing
+ * can be shared by more than one thread. This potential work sharing
+ * can provide 2 optimizations for removals:
+ * <ol>
+ * <li> It provides an opportunity for separate removal requests to be
+ * consolidated into one removal pass.</li>
+ * <li> It can reduce removing thread latencies by sharing the removal work
+ * with other removing threads which otherwise might not have any work to do
+ * due to their removal request being consolidated. This makes the system
+ * more efficient and can actually reduce latencies as system load increases
+ * due to pack removals!</li>
+ * </ol>
+ * The optimizations above are all achieved without blockng threads to wait
+ * for other threads to complete (although naturally there are some
+ * synchronization points), and while ensuring that no threads do more work
+ * than if they were the only thread available to perform a removal.
+ *
+ * @param packs
+ * the files to purge all entries of
*/
- private void removeAll(Pack pack) {
- for (int s = 0; s < tableSize; s++) {
+ private void queueRemoveAll(Set<Pack> packs) {
+ synchronized (this) {
+ packsToRemove.addAll(packs);
+ if (numRemovers >= idealNumRemovers) {
+ return;
+ }
+ numRemovers++;
+ }
+ for (int numRemoved = 0; removeNextBlock(numRemoved); numRemoved++) {
+ // empty
+ }
+ synchronized (this) {
+ if (numRemovers > 0) {
+ numRemovers--;
+ }
+ }
+ }
+
+ /** Determine which block to remove next, if any, and do so.
+ *
+ * @param numRemoved
+ * the number of already processed block removals by the current thread
+ * @return whether more processing should be done by the current thread
+ */
+ private boolean removeNextBlock(int numRemoved) {
+ Set<Pack> toRemove;
+ int block;
+ synchronized (this) {
+ if (packsBeingRemoved == null || blockBeingRemoved >= numRemovalBlocks - 1) {
+ if (packsToRemove.isEmpty()) {
+ return false;
+ }
+
+ blockBeingRemoved = 0;
+ packsBeingRemoved = packsToRemove;
+ packsToRemove = new HashSet<>();
+ } else {
+ blockBeingRemoved++;
+ }
+
+ toRemove = packsBeingRemoved;
+ block = blockBeingRemoved;
+ }
+
+ removeBlock(toRemove, block);
+ numRemoved++;
+
+ /* Ensure threads never work on more than a full set of blocks (the equivalent
+ * of removing one Pack) */
+ boolean isLast = numRemoved >= numRemovalBlocks;
+ synchronized (this) {
+ if (numRemovers >= idealNumRemovers) {
+ isLast = true;
+ }
+ }
+ return !isLast;
+ }
+
+ /** Remove a block of entries for a Set of files
+ * @param packs
+ * the files to purge all entries of
+ * @param block
+ * the specific block to process removals for
+ */
+ private void removeBlock(Set<Pack> packs, int block) {
+ int starting = block * removalBlockSize;
+ for (int s = starting; s < starting + removalBlockSize && s < tableSize; s++) {
final Entry e1 = table.get(s);
boolean hasDead = false;
for (Entry e = e1; e != null; e = e.next) {
- if (e.ref.getPack() == pack) {
+ if (packs.contains(e.ref.getPack())) {
e.kill();
hasDead = true;
} else if (e.dead)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCursor.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCursor.java
index 01f514b..11c4547 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCursor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCursor.java
@@ -205,7 +205,7 @@ public void writeObjects(PackOutputStream out, List<ObjectToPack> list)
* @param cnt
* number of bytes to copy. This value may exceed the number of
* bytes remaining in the window starting at offset
- * <code>pos</code>.
+ * <code>position</code>.
* @return number of bytes actually copied; this may be less than
* <code>cnt</code> if <code>cnt</code> exceeded the number of bytes
* available.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndexConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndexConstants.java
new file mode 100644
index 0000000..6122a9a
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndexConstants.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2025, Google Inc.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.storage.midx;
+
+class MultiPackIndexConstants {
+ static final int MIDX_SIGNATURE = 0x4d494458; /* MIDX */
+
+ static final byte MIDX_VERSION = 1;
+
+ /**
+ * We infer the length of object IDs (OIDs) from this value:
+ *
+ * <pre>
+ * 1 => SHA-1
+ * 2 => SHA-256
+ * </pre>
+ */
+ static final byte OID_HASH_VERSION = 1;
+
+ static final int MULTIPACK_INDEX_FANOUT_SIZE = 4 * 256;
+
+ /**
+ * First 4 bytes describe the chunk id. Value 0 is a terminating label.
+ * Other 8 bytes provide the byte-offset in current file for chunk to start.
+ */
+ static final int CHUNK_LOOKUP_WIDTH = 12;
+
+ /** "PNAM" chunk */
+ static final int MIDX_CHUNKID_PACKNAMES = 0x504e414d;
+
+ /** "OIDF" chunk */
+ static final int MIDX_CHUNKID_OIDFANOUT = 0x4f494446;
+
+ /** "OIDL" chunk */
+ static final int MIDX_CHUNKID_OIDLOOKUP = 0x4f49444c;
+
+ /** "OOFF" chunk */
+ static final int MIDX_CHUNKID_OBJECTOFFSETS = 0x4f4f4646;
+
+ /** "LOFF" chunk */
+ static final int MIDX_CHUNKID_LARGEOFFSETS = 0x4c4f4646;
+
+ /** "RIDX" chunk */
+ static final int MIDX_CHUNKID_REVINDEX = 0x52494458;
+
+ private MultiPackIndexConstants() {
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndexPrettyPrinter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndexPrettyPrinter.java
new file mode 100644
index 0000000..795d39e
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndexPrettyPrinter.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2025, Google Inc.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.storage.midx;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.CHUNK_LOOKUP_WIDTH;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.util.NB;
+
+/**
+ * Prints a multipack index file in a human-readable format.
+ *
+ * @since 7.2
+ */
+@SuppressWarnings({ "boxing", "nls" })
+public class MultiPackIndexPrettyPrinter {
+
+ /**
+ * Writes to out, in human-readable format, the multipack index in rawMidx
+ *
+ * @param rawMidx the bytes of a multipack index
+ * @param out a writer
+ */
+ public static void prettyPrint(byte[] rawMidx, PrintWriter out) {
+ // Header (12 bytes)
+ out.println("[ 0] Magic: " + new String(rawMidx, 0, 4, UTF_8));
+ out.println("[ 4] Version number: " + (int) rawMidx[4]);
+ out.println("[ 5] OID version: " + (int) rawMidx[5]);
+ int chunkCount = rawMidx[6];
+ out.println("[ 6] # of chunks: " + chunkCount);
+ out.println("[ 7] # of bases: " + (int) rawMidx[7]);
+ int numberOfPacks = NB.decodeInt32(rawMidx, 8);
+ out.println("[ 8] # of packs: " + numberOfPacks);
+
+ // Chunk lookup table
+ List<ChunkSegment> chunkSegments = new ArrayList<>();
+ int current = printChunkLookup(out, rawMidx, chunkCount, chunkSegments);
+
+ for (int i = 0; i < chunkSegments.size() - 1; i++) {
+ ChunkSegment segment = chunkSegments.get(i);
+ if (current != segment.startOffset()) {
+ throw new IllegalStateException(String.format(
+ "We are at byte %d, but segment should start at %d",
+ current, segment.startOffset()));
+ }
+ out.printf("Starting chunk: %s @ %d%n", segment.chunkName(),
+ segment.startOffset());
+ switch (segment.chunkName()) {
+ case "OIDF" -> current = printOIDF(out, rawMidx, current);
+ case "OIDL" -> current = printOIDL(out, rawMidx, current,
+ chunkSegments.get(i + 1).startOffset);
+ case "OOFF" -> current = printOOFF(out, rawMidx, current,
+ chunkSegments.get(i + 1).startOffset);
+ case "PNAM" -> current = printPNAM(out, rawMidx, current,
+ chunkSegments.get(i + 1).startOffset);
+ case "RIDX" -> current = printRIDX(out, rawMidx, current,
+ chunkSegments.get(i + 1).startOffset);
+ default -> {
+ out.printf(
+ "Skipping %s (don't know how to print it yet)%n",
+ segment.chunkName());
+ current = (int) chunkSegments.get(i + 1).startOffset();
+ }
+ }
+ }
+ // Checksum is a SHA-1, use ObjectId to parse it
+ out.printf("[ %d] Checksum %s%n", current,
+ ObjectId.fromRaw(rawMidx, current).name());
+ out.printf("Total size: " + (current + 20));
+ }
+
+ private static int printChunkLookup(PrintWriter out, byte[] rawMidx, int chunkCount,
+ List<ChunkSegment> chunkSegments) {
+ out.println("Starting chunk lookup @ 12");
+ int current = 12;
+ for (int i = 0; i < chunkCount; i++) {
+ String chunkName = new String(rawMidx, current, 4, UTF_8);
+ long offset = NB.decodeInt64(rawMidx, current + 4);
+ out.printf("[ %d] |%8s|%8d|%n", current, chunkName, offset);
+ current += CHUNK_LOOKUP_WIDTH;
+ chunkSegments.add(new ChunkSegment(chunkName, offset));
+ }
+ String chunkName = "0000";
+ long offset = NB.decodeInt64(rawMidx, current + 4);
+ out.printf("[ %d] |%8s|%8d|%n", current, chunkName, offset);
+ current += CHUNK_LOOKUP_WIDTH;
+ chunkSegments.add(new ChunkSegment(chunkName, offset));
+ return current;
+ }
+
+ private static int printOIDF(PrintWriter out, byte[] rawMidx, int start) {
+ int current = start;
+ for (short i = 0; i < 256; i++) {
+ out.printf("[ %d] (%02X) %d%n", current, i,
+ NB.decodeInt32(rawMidx, current));
+ current += 4;
+ }
+ return current;
+ }
+
+ private static int printOIDL(PrintWriter out, byte[] rawMidx, int start, long end) {
+ int i = start;
+ while (i < end) {
+ out.printf("[ %d] %s%n", i,
+ ObjectId.fromRaw(rawMidx, i).name());
+ i += 20;
+ }
+ return i;
+ }
+
+ private static int printOOFF(PrintWriter out, byte[] rawMidx, int start, long end) {
+ int i = start;
+ while (i < end) {
+ out.printf("[ %d] %d %d%n", i, NB.decodeInt32(rawMidx, i),
+ NB.decodeInt32(rawMidx, i + 4));
+ i += 8;
+ }
+ return i;
+ }
+
+ private static int printRIDX(PrintWriter out, byte[] rawMidx, int start, long end) {
+ int i = start;
+ while (i < end) {
+ out.printf("[ %d] %d%n", i, NB.decodeInt32(rawMidx, i));
+ i += 4;
+ }
+ return (int) end;
+ }
+
+ private static int printPNAM(PrintWriter out, byte[] rawMidx, int start, long end) {
+ int nameStart = start;
+ for (int i = start; i < end; i++) {
+ if (rawMidx[i] == 0) {
+ out
+ .println(new String(rawMidx, nameStart, i - nameStart));
+ nameStart = i + 1;
+ }
+ }
+ return (int) end;
+ }
+
+ private record ChunkSegment(String chunkName, long startOffset) {
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndexWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndexWriter.java
new file mode 100644
index 0000000..bddf3ac
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndexWriter.java
@@ -0,0 +1,426 @@
+/*
+ * Copyright (C) 2025, Google Inc.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.storage.midx;
+
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.CHUNK_LOOKUP_WIDTH;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.MIDX_CHUNKID_LARGEOFFSETS;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.MIDX_CHUNKID_OBJECTOFFSETS;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.MIDX_CHUNKID_OIDFANOUT;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.MIDX_CHUNKID_OIDLOOKUP;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.MIDX_CHUNKID_PACKNAMES;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.MIDX_CHUNKID_REVINDEX;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.MIDX_SIGNATURE;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.MIDX_VERSION;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.MULTIPACK_INDEX_FANOUT_SIZE;
+import static org.eclipse.jgit.internal.storage.midx.MultiPackIndexConstants.OID_HASH_VERSION;
+import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.file.PackIndex;
+import org.eclipse.jgit.internal.storage.io.CancellableDigestOutputStream;
+import org.eclipse.jgit.internal.storage.midx.PackIndexMerger.MidxMutableEntry;
+import org.eclipse.jgit.lib.ProgressMonitor;
+import org.eclipse.jgit.util.NB;
+
+/**
+ * Writes a collection of indexes as a multipack index.
+ * <p>
+ * See <a href=
+ * "https://git-scm.com/docs/pack-format#_multi_pack_index_midx_files_have_the_following_format">multipack
+ * index format spec</a>
+ *
+ * @since 7.2
+ */
+public class MultiPackIndexWriter {
+
+ private static final int LIMIT_31_BITS = (1 << 31) - 1;
+
+ private static final int MIDX_HEADER_SIZE = 12;
+
+ /**
+ * Writes the inputs in the multipack index format in the outputStream.
+ *
+ * @param monitor
+ * progress monitor
+ * @param outputStream
+ * stream to write the multipack index file
+ * @param inputs
+ * pairs of name and index for each pack to include in the
+ * multipack index.
+ * @throws IOException
+ * Error writing to the stream
+ */
+ public void write(ProgressMonitor monitor, OutputStream outputStream,
+ Map<String, PackIndex> inputs) throws IOException {
+ PackIndexMerger data = new PackIndexMerger(inputs);
+
+ // List of chunks in the order they need to be written
+ List<ChunkHeader> chunkHeaders = createChunkHeaders(data);
+ long expectedSize = calculateExpectedSize(chunkHeaders);
+ try (CancellableDigestOutputStream out = new CancellableDigestOutputStream(
+ monitor, outputStream)) {
+ writeHeader(out, chunkHeaders.size(), data.getPackCount());
+ writeChunkLookup(out, chunkHeaders);
+
+ WriteContext ctx = new WriteContext(out, data);
+ for (ChunkHeader chunk : chunkHeaders) {
+ chunk.writerFn.write(ctx);
+ }
+ writeCheckSum(out);
+ if (expectedSize != out.length()) {
+ throw new IllegalStateException(String.format(
+ JGitText.get().multiPackIndexUnexpectedSize,
+ Long.valueOf(expectedSize),
+ Long.valueOf(out.length())));
+ }
+ } catch (InterruptedIOException e) {
+ throw new IOException(JGitText.get().multiPackIndexWritingCancelled,
+ e);
+ }
+ }
+
+ private static long calculateExpectedSize(List<ChunkHeader> chunks) {
+ int chunkLookup = (chunks.size() + 1) * CHUNK_LOOKUP_WIDTH;
+ long chunkContent = chunks.stream().mapToLong(c -> c.size).sum();
+ return /* header */ 12 + chunkLookup + chunkContent + /* CRC */ 20;
+ }
+
+ private List<ChunkHeader> createChunkHeaders(PackIndexMerger data) {
+ List<ChunkHeader> chunkHeaders = new ArrayList<>();
+ chunkHeaders.add(new ChunkHeader(MIDX_CHUNKID_OIDFANOUT,
+ MULTIPACK_INDEX_FANOUT_SIZE, this::writeFanoutTable));
+ chunkHeaders.add(new ChunkHeader(MIDX_CHUNKID_OIDLOOKUP,
+ (long) data.getUniqueObjectCount() * OBJECT_ID_LENGTH,
+ this::writeOidLookUp));
+ chunkHeaders.add(new ChunkHeader(MIDX_CHUNKID_OBJECTOFFSETS,
+ 8L * data.getUniqueObjectCount(), this::writeObjectOffsets));
+ if (data.needsLargeOffsetsChunk()) {
+ chunkHeaders.add(new ChunkHeader(MIDX_CHUNKID_LARGEOFFSETS,
+ 8L * data.getOffsetsOver31BitsCount(),
+ this::writeObjectLargeOffsets));
+ }
+ chunkHeaders.add(new ChunkHeader(MIDX_CHUNKID_REVINDEX,
+ 4L * data.getUniqueObjectCount(), this::writeRidx));
+
+ int packNamesSize = data.getPackNames().stream()
+ .mapToInt(String::length).map(i -> i + 1 /* null at the end */)
+ .sum();
+ chunkHeaders.add(new ChunkHeader(MIDX_CHUNKID_PACKNAMES, packNamesSize,
+ this::writePackfileNames));
+ return chunkHeaders;
+ }
+
+ /**
+ * Write the first 12 bytes of the multipack index.
+ * <p>
+ * These bytes include things like magic number, version, number of
+ * chunks...
+ *
+ * @param out
+ * output stream to write
+ * @param numChunks
+ * number of chunks this multipack index is going to have
+ * @param packCount
+ * number of packs covered by this multipack index
+ * @throws IOException
+ * error writing to the output stream
+ */
+ private void writeHeader(CancellableDigestOutputStream out, int numChunks,
+ int packCount) throws IOException {
+ byte[] headerBuffer = new byte[MIDX_HEADER_SIZE];
+ NB.encodeInt32(headerBuffer, 0, MIDX_SIGNATURE);
+ byte[] buff = { MIDX_VERSION, OID_HASH_VERSION, (byte) numChunks,
+ (byte) 0 };
+ System.arraycopy(buff, 0, headerBuffer, 4, 4);
+ NB.encodeInt32(headerBuffer, 8, packCount);
+ out.write(headerBuffer, 0, headerBuffer.length);
+ out.flush();
+ }
+
+ /**
+ * Write a table of "chunkId, start-offset", with a special value "0,
+ * end-of-previous_chunk", to mark the end.
+ *
+ * @param out
+ * output stream to write
+ * @param chunkHeaders
+ * list of chunks in the order they are expected to be written
+ * @throws IOException
+ * error writing to the output stream
+ */
+ private void writeChunkLookup(CancellableDigestOutputStream out,
+ List<ChunkHeader> chunkHeaders) throws IOException {
+
+ // first chunk will start at header + this lookup block
+ long chunkStart = MIDX_HEADER_SIZE
+ + (long) (chunkHeaders.size() + 1) * CHUNK_LOOKUP_WIDTH;
+ byte[] chunkEntry = new byte[CHUNK_LOOKUP_WIDTH];
+ for (ChunkHeader chunkHeader : chunkHeaders) {
+ NB.encodeInt32(chunkEntry, 0, chunkHeader.chunkId);
+ NB.encodeInt64(chunkEntry, 4, chunkStart);
+ out.write(chunkEntry);
+ chunkStart += chunkHeader.size;
+ }
+ // Terminating label for the block
+ // (chunkid 0, offset where the next block would start)
+ NB.encodeInt32(chunkEntry, 0, 0);
+ NB.encodeInt64(chunkEntry, 4, chunkStart);
+ out.write(chunkEntry);
+ }
+
+ /**
+ * Write the fanout table for the object ids
+ * <p>
+ * Table with 256 entries (one byte), where the ith entry, F[i], stores the
+ * number of OIDs with first byte at most i. Thus, F[255] stores the total
+ * number of objects.
+ *
+ * @param ctx
+ * write context
+ * @throws IOException
+ * error writing to the output stream
+ */
+
+ private void writeFanoutTable(WriteContext ctx) throws IOException {
+ byte[] tmp = new byte[4];
+ int[] fanout = new int[256];
+ Iterator<MidxMutableEntry> iterator = ctx.data.bySha1Iterator();
+ while (iterator.hasNext()) {
+ MidxMutableEntry e = iterator.next();
+ fanout[e.getObjectId().getFirstByte() & 0xff]++;
+ }
+ for (int i = 1; i < fanout.length; i++) {
+ fanout[i] += fanout[i - 1];
+ }
+ for (int n : fanout) {
+ NB.encodeInt32(tmp, 0, n);
+ ctx.out.write(tmp, 0, 4);
+ }
+ }
+
+ /**
+ * Write the OID lookup chunk
+ * <p>
+ * A list of OIDs in sha1 order.
+ *
+ * @param ctx
+ * write context
+ * @throws IOException
+ * error writing to the output stream
+ */
+ private void writeOidLookUp(WriteContext ctx) throws IOException {
+ byte[] tmp = new byte[OBJECT_ID_LENGTH];
+
+ Iterator<MidxMutableEntry> iterator = ctx.data.bySha1Iterator();
+ while (iterator.hasNext()) {
+ MidxMutableEntry e = iterator.next();
+ e.getObjectId().copyRawTo(tmp, 0);
+ ctx.out.write(tmp, 0, OBJECT_ID_LENGTH);
+ }
+ }
+
+ /**
+ * Write the object offsets chunk
+ * <p>
+ * A list of offsets, parallel to the list of OIDs. If the offset is too
+ * large (see {@link #fitsIn31bits(long)}), this contains the position in
+ * the large offsets list (marked with a 1 in the most significant bit).
+ *
+ * @param ctx
+ * write context
+ * @throws IOException
+ * error writing to the output stream
+ */
+ private void writeObjectOffsets(WriteContext ctx) throws IOException {
+ byte[] entry = new byte[8];
+ Iterator<MidxMutableEntry> iterator = ctx.data.bySha1Iterator();
+ while (iterator.hasNext()) {
+ MidxMutableEntry e = iterator.next();
+ NB.encodeInt32(entry, 0, e.getPackId());
+ if (!ctx.data.needsLargeOffsetsChunk()
+ || fitsIn31bits(e.getOffset())) {
+ NB.encodeInt32(entry, 4, (int) e.getOffset());
+ } else {
+ int offloadedPosition = ctx.largeOffsets.append(e.getOffset());
+ NB.encodeInt32(entry, 4, offloadedPosition | (1 << 31));
+ }
+ ctx.out.write(entry);
+ }
+ }
+
+ /**
+ * Writes the reverse index chunk
+ * <p>
+ * This stores the position of the objects in the main index, ordered first
+ * by pack and then by offset
+ *
+ * @param ctx
+ * write context
+ * @throws IOException
+ * erorr writing to the output stream
+ */
+ private void writeRidx(WriteContext ctx) throws IOException {
+ Map<Integer, List<OffsetPosition>> packOffsets = new HashMap<>(
+ ctx.data.getPackCount());
+ // TODO(ifrade): Brute force solution loading all offsets/packs in
+ // memory. We could also iterate reverse indexes looking up
+ // their position in the midx (and discarding if the pack doesn't
+ // match).
+ Iterator<MidxMutableEntry> iterator = ctx.data.bySha1Iterator();
+ int midxPosition = 0;
+ while (iterator.hasNext()) {
+ MidxMutableEntry e = iterator.next();
+ OffsetPosition op = new OffsetPosition(e.getOffset(), midxPosition);
+ midxPosition++;
+ packOffsets.computeIfAbsent(Integer.valueOf(e.getPackId()),
+ k -> new ArrayList<>()).add(op);
+ }
+
+ for (int i = 0; i < ctx.data.getPackCount(); i++) {
+ List<OffsetPosition> offsetsForPack = packOffsets
+ .get(Integer.valueOf(i));
+ if (offsetsForPack.isEmpty()) {
+ continue;
+ }
+ offsetsForPack.sort(Comparator.comparing(OffsetPosition::offset));
+ byte[] ridxForPack = new byte[4 * offsetsForPack.size()];
+ for (int j = 0; j < offsetsForPack.size(); j++) {
+ NB.encodeInt32(ridxForPack, j * 4,
+ offsetsForPack.get(j).position);
+ }
+ ctx.out.write(ridxForPack);
+ }
+ }
+
+ /**
+ * Write the large offset chunk
+ * <p>
+ * A list of large offsets (long). The regular offset chunk will point to a
+ * position here.
+ *
+ * @param ctx
+ * writer context
+ * @throws IOException
+ * error writing to the output stream
+ */
+ private void writeObjectLargeOffsets(WriteContext ctx) throws IOException {
+ ctx.out.write(ctx.largeOffsets.offsets, 0,
+ ctx.largeOffsets.bytePosition);
+ }
+
+ /**
+ * Write the list of packfiles chunk
+ * <p>
+ * List of packfiles (in lexicographical order) with an \0 at the end
+ *
+ * @param ctx
+ * writer context
+ * @throws IOException
+ * error writing to the output stream
+ */
+ private void writePackfileNames(WriteContext ctx) throws IOException {
+ for (String packName : ctx.data.getPackNames()) {
+ // Spec doesn't talk about encoding.
+ ctx.out.write(packName.getBytes(StandardCharsets.UTF_8));
+ ctx.out.write(0);
+ }
+ }
+
+ /**
+ * Write final checksum of the data written to the stream
+ *
+ * @param out
+ * output stream used to write
+ * @throws IOException
+ * error writing to the output stream
+ */
+ private void writeCheckSum(CancellableDigestOutputStream out)
+ throws IOException {
+ out.write(out.getDigest());
+ out.flush();
+ }
+
+
+ private record OffsetPosition(long offset, int position) {
+ }
+
+ /**
+ * If there is at least one offset value larger than 2^32-1, then the large
+ * offset chunk must exist, and offsets larger than 2^31-1 must be stored in
+ * it instead
+ *
+ * @param offset object offset
+ *
+ * @return true if the offset fits in 31 bits
+ */
+ private static boolean fitsIn31bits(long offset) {
+ return offset <= LIMIT_31_BITS;
+ }
+
+ private static class LargeOffsets {
+ private final byte[] offsets;
+
+ private int bytePosition;
+
+ LargeOffsets(int largeOffsetsCount) {
+ offsets = new byte[largeOffsetsCount * 8];
+ bytePosition = 0;
+ }
+
+ /**
+ * Add an offset to the large offset chunk
+ *
+ * @param largeOffset
+ * a large offset
+ * @return the position of the just inserted offset (as in number of
+ * offsets, NOT in bytes)
+ */
+ int append(long largeOffset) {
+ int at = bytePosition;
+ NB.encodeInt64(offsets, at, largeOffset);
+ bytePosition += 8;
+ return at / 8;
+ }
+ }
+
+ private record ChunkHeader(int chunkId, long size, ChunkWriter writerFn) {
+ }
+
+ @FunctionalInterface
+ private interface ChunkWriter {
+ void write(WriteContext ctx) throws IOException;
+ }
+
+ private static class WriteContext {
+ final CancellableDigestOutputStream out;
+
+ final PackIndexMerger data;
+
+ final LargeOffsets largeOffsets;
+
+ WriteContext(CancellableDigestOutputStream out, PackIndexMerger data) {
+ this.out = out;
+ this.data = data;
+ this.largeOffsets = new LargeOffsets(
+ data.getOffsetsOver31BitsCount());
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/PackIndexMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/PackIndexMerger.java
new file mode 100644
index 0000000..89814af
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/PackIndexMerger.java
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) 2025, Google Inc.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.storage.midx;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.stream.Collectors;
+
+import org.eclipse.jgit.internal.storage.file.PackIndex;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.MutableObjectId;
+
+/**
+ * Collect the stats and offers an iterator over the union of n-pack indexes.
+ * <p>
+ * The multipack index is a list of (sha1, packid, offset) ordered by sha1. We
+ * can build it from the individual pack indexes (sha1, offset) ordered by sha1,
+ * with a simple merge ignoring duplicates.
+ * <p>
+ * This class encapsulates the merging logic and precalculates the stats that
+ * the index needs (like total count of objects). To limit memory consumption,
+ * it does the merge as it goes during the iteration and iterators use mutable
+ * entries. The stats of the combined index are calculated in an iteration at
+ * construction time.
+ */
+class PackIndexMerger {
+
+ private static final int LIMIT_31_BITS = (1 << 31) - 1;
+
+ private static final long LIMIT_32_BITS = (1L << 32) - 1;
+
+ /**
+ * Object returned by the iterator.
+ * <p>
+ * The iterator returns (on each next()) the same instance with different
+ * values, to avoid allocating many short-lived objects. Callers should not
+ * keep a reference to that returned value.
+ */
+ static class MidxMutableEntry {
+ // The object id
+ private final MutableObjectId oid = new MutableObjectId();
+
+ // Position of the pack in the ordered list of pack in this merger
+ private int packId;
+
+ // Offset in its pack
+ private long offset;
+
+ public AnyObjectId getObjectId() {
+ return oid;
+ }
+
+ public int getPackId() {
+ return packId;
+ }
+
+ public long getOffset() {
+ return offset;
+ }
+
+ /**
+ * Copy values from another mutable entry
+ *
+ * @param packId
+ * packId
+ * @param other
+ * another mutable entry
+ */
+ private void fill(int packId, PackIndex.MutableEntry other) {
+ other.copyOidTo(oid);
+ this.packId = packId;
+ this.offset = other.getOffset();
+ }
+ }
+
+ private final List<String> packNames;
+
+ private final List<PackIndex> indexes;
+
+ private final boolean needsLargeOffsetsChunk;
+
+ private final int offsetsOver31BitsCount;
+
+ private final int uniqueObjectCount;
+
+ PackIndexMerger(Map<String, PackIndex> packs) {
+ this.packNames = packs.keySet().stream().sorted()
+ .collect(Collectors.toUnmodifiableList());
+
+ this.indexes = packNames.stream().map(packs::get)
+ .collect(Collectors.toUnmodifiableList());
+
+ // Iterate for duplicates
+ int objectCount = 0;
+ boolean hasLargeOffsets = false;
+ int over31bits = 0;
+ MutableObjectId lastSeen = new MutableObjectId();
+ MultiIndexIterator it = new MultiIndexIterator(indexes);
+ while (it.hasNext()) {
+ MidxMutableEntry entry = it.next();
+ if (lastSeen.equals(entry.oid)) {
+ continue;
+ }
+ // If there is at least one offset value larger than 2^32-1, then
+ // the large offset chunk must exist, and offsets larger than
+ // 2^31-1 must be stored in it instead
+ if (entry.offset > LIMIT_32_BITS) {
+ hasLargeOffsets = true;
+ }
+ if (entry.offset > LIMIT_31_BITS) {
+ over31bits++;
+ }
+
+ lastSeen.fromObjectId(entry.oid);
+ objectCount++;
+ }
+ uniqueObjectCount = objectCount;
+ offsetsOver31BitsCount = over31bits;
+ needsLargeOffsetsChunk = hasLargeOffsets;
+ }
+
+ /**
+ * Object count of the merged index (i.e. without duplicates)
+ *
+ * @return object count of the merged index
+ */
+ int getUniqueObjectCount() {
+ return uniqueObjectCount;
+ }
+
+ /**
+ * If any object in any of the indexes has an offset over 2^32-1
+ *
+ * @return true if there is any object with offset > 2^32 -1
+ */
+ boolean needsLargeOffsetsChunk() {
+ return needsLargeOffsetsChunk;
+ }
+
+ /**
+ * How many object have offsets over 2^31-1
+ * <p>
+ * Per multipack index spec, IF there is large offset chunk, all this
+ * offsets should be there.
+ *
+ * @return number of objects with offsets over 2^31-1
+ */
+ int getOffsetsOver31BitsCount() {
+ return offsetsOver31BitsCount;
+ }
+
+ /**
+ * List of pack names in alphabetical order.
+ * <p>
+ * Order matters: In case of duplicates, the multipack index prefers the
+ * first package with it. This is in the same order we are using to
+ * prioritize duplicates.
+ *
+ * @return List of pack names, in the order used by the merge.
+ */
+ List<String> getPackNames() {
+ return packNames;
+ }
+
+ /**
+ * How many packs are being merged
+ *
+ * @return count of packs merged
+ */
+ int getPackCount() {
+ return packNames.size();
+ }
+
+ /**
+ * Iterator over the merged indexes in sha1 order without duplicates
+ * <p>
+ * The returned entry in the iterator is mutable, callers should NOT keep a
+ * reference to it.
+ *
+ * @return an iterator in sha1 order without duplicates.
+ */
+ Iterator<MidxMutableEntry> bySha1Iterator() {
+ return new DedupMultiIndexIterator(new MultiIndexIterator(indexes),
+ getUniqueObjectCount());
+ }
+
+ /**
+ * For testing. Iterate all entries, not skipping duplicates (stable order)
+ *
+ * @return an iterator of all objects in sha1 order, including duplicates.
+ */
+ Iterator<MidxMutableEntry> rawIterator() {
+ return new MultiIndexIterator(indexes);
+ }
+
+ /**
+ * Iterator over n-indexes in ObjectId order.
+ * <p>
+ * It returns duplicates if the same object id is in different indexes. Wrap
+ * it with {@link DedupMultiIndexIterator (Iterator, int)} to avoid
+ * duplicates.
+ */
+ private static final class MultiIndexIterator
+ implements Iterator<MidxMutableEntry> {
+
+ private final List<PackIndexPeekIterator> indexIterators;
+
+ private final MidxMutableEntry mutableEntry = new MidxMutableEntry();
+
+ MultiIndexIterator(List<PackIndex> indexes) {
+ this.indexIterators = new ArrayList<>(indexes.size());
+ for (int i = 0; i < indexes.size(); i++) {
+ PackIndexPeekIterator it = new PackIndexPeekIterator(i,
+ indexes.get(i));
+ // Position in the first element
+ if (it.next() != null) {
+ indexIterators.add(it);
+ }
+ }
+ }
+
+ @Override
+ public boolean hasNext() {
+ return !indexIterators.isEmpty();
+ }
+
+ @Override
+ public MidxMutableEntry next() {
+ PackIndexPeekIterator winner = null;
+ for (int index = 0; index < indexIterators.size(); index++) {
+ PackIndexPeekIterator current = indexIterators.get(index);
+ if (winner == null
+ || current.peek().compareBySha1To(winner.peek()) < 0) {
+ winner = current;
+ }
+ }
+
+ if (winner == null) {
+ throw new NoSuchElementException();
+ }
+
+ mutableEntry.fill(winner.getPackId(), winner.peek());
+ if (winner.next() == null) {
+ indexIterators.remove(winner);
+ };
+ return mutableEntry;
+ }
+ }
+
+ private static class DedupMultiIndexIterator
+ implements Iterator<MidxMutableEntry> {
+ private final MultiIndexIterator src;
+
+ private int remaining;
+
+ private final MutableObjectId lastOid = new MutableObjectId();
+
+ DedupMultiIndexIterator(MultiIndexIterator src, int totalCount) {
+ this.src = src;
+ this.remaining = totalCount;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return remaining > 0;
+ }
+
+ @Override
+ public MidxMutableEntry next() {
+ MidxMutableEntry next = src.next();
+ while (next != null && lastOid.equals(next.oid)) {
+ next = src.next();
+ }
+
+ if (next == null) {
+ throw new NoSuchElementException();
+ }
+
+ lastOid.fromObjectId(next.oid);
+ remaining--;
+ return next;
+ }
+ }
+
+ /**
+ * Convenience around the PackIndex iterator to read the current value
+ * multiple times without consuming it.
+ * <p>
+ * This is used to merge indexes in the multipack index, where we need to
+ * compare the current value between indexes multiple times to find the
+ * next.
+ * <p>
+ * We could also implement this keeping the position (int) and
+ * MutableEntry#getObjectId, but that would create an ObjectId per entry.
+ * This implementation reuses the MutableEntry and avoid instantiations.
+ */
+ // Visible for testing
+ static class PackIndexPeekIterator {
+ private final Iterator<PackIndex.MutableEntry> it;
+
+ private final int packId;
+
+ PackIndex.MutableEntry current;
+
+ PackIndexPeekIterator(int packId, PackIndex index) {
+ it = index.iterator();
+ this.packId = packId;
+ }
+
+ PackIndex.MutableEntry next() {
+ if (it.hasNext()) {
+ current = it.next();
+ } else {
+ current = null;
+ }
+ return current;
+ }
+
+ PackIndex.MutableEntry peek() {
+ return current;
+ }
+
+ int getPackId() {
+ return packId;
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackIndexWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackIndexWriter.java
new file mode 100644
index 0000000..f69e68d
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackIndexWriter.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024, Google LLC.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.eclipse.jgit.internal.storage.pack;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.eclipse.jgit.transport.PackedObjectInfo;
+
+/**
+ * Represents a function that accepts a collection of objects to write into a
+ * primary pack index storage format.
+ */
+public interface PackIndexWriter {
+ /**
+ * Write all object entries to the index stream.
+ *
+ * @param toStore
+ * sorted list of objects to store in the index. The caller must
+ * have previously sorted the list using
+ * {@link org.eclipse.jgit.transport.PackedObjectInfo}'s native
+ * {@link java.lang.Comparable} implementation.
+ * @param packDataChecksum
+ * checksum signature of the entire pack data content. This is
+ * traditionally the last 20 bytes of the pack file's own stream.
+ * @throws java.io.IOException
+ * an error occurred while writing to the output stream, or the
+ * underlying format cannot store the object data supplied.
+ */
+ void write(List<? extends PackedObjectInfo> toStore,
+ byte[] packDataChecksum) throws IOException;
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java
index 4350f97..f025d4e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java
@@ -58,10 +58,10 @@
import org.eclipse.jgit.errors.SearchForReuseTimeout;
import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException;
import org.eclipse.jgit.internal.JGitText;
-import org.eclipse.jgit.internal.storage.file.PackIndexWriter;
+import org.eclipse.jgit.internal.storage.file.BasePackIndexWriter;
+import org.eclipse.jgit.internal.storage.file.PackBitmapIndexBuilder;
import org.eclipse.jgit.internal.storage.file.PackObjectSizeIndexWriter;
import org.eclipse.jgit.internal.storage.file.PackReverseIndexWriter;
-import org.eclipse.jgit.internal.storage.file.PackBitmapIndexBuilder;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.AsyncObjectSizeQueue;
import org.eclipse.jgit.lib.BatchingProgressMonitor;
@@ -118,7 +118,7 @@
* {@link #preparePack(ProgressMonitor, Set, Set)}, and streaming with
* {@link #writePack(ProgressMonitor, ProgressMonitor, OutputStream)}. If the
* pack is being stored as a file the matching index can be written out after
- * writing the pack by {@link #writeIndex(OutputStream)}. An optional bitmap
+ * writing the pack by {@link #writeIndex(PackIndexWriter)}. An optional bitmap
* index can be made by calling {@link #prepareBitmapIndex(ProgressMonitor)}
* followed by {@link #writeBitmapIndex(PackBitmapIndexWriter)}.
* </p>
@@ -303,19 +303,6 @@ public PackWriter(Repository repo) {
}
/**
- * Create a writer to load objects from the specified reader.
- * <p>
- * Objects for packing are specified in {@link #preparePack(Iterator)} or
- * {@link #preparePack(ProgressMonitor, Set, Set)}.
- *
- * @param reader
- * reader to read from the repository with.
- */
- public PackWriter(ObjectReader reader) {
- this(new PackConfig(), reader);
- }
-
- /**
* Create writer for specified repository.
* <p>
* Objects for packing are specified in {@link #preparePack(Iterator)} or
@@ -1091,7 +1078,7 @@ public int getIndexVersion() {
if (indexVersion <= 0) {
for (BlockList<ObjectToPack> objs : objectsLists)
indexVersion = Math.max(indexVersion,
- PackIndexWriter.oldestPossibleFormat(objs));
+ BasePackIndexWriter.oldestPossibleFormat(objs));
}
return indexVersion;
}
@@ -1112,12 +1099,28 @@ public int getIndexVersion() {
* the index data could not be written to the supplied stream.
*/
public void writeIndex(OutputStream indexStream) throws IOException {
+ writeIndex(BasePackIndexWriter.createVersion(indexStream,
+ getIndexVersion()));
+ }
+
+ /**
+ * Create an index file to match the pack file just written.
+ * <p>
+ * Called after
+ * {@link #writePack(ProgressMonitor, ProgressMonitor, OutputStream)}.
+ * <p>
+ * Writing an index is only required for local pack storage. Packs sent on
+ * the network do not need to create an index.
+ *
+ * @param iw
+ * an {@link PackIndexWriter} instance to write the index
+ * @throws java.io.IOException
+ * the index data could not be written to the supplied stream.
+ */
+ public void writeIndex(PackIndexWriter iw) throws IOException {
if (isIndexDisabled())
throw new IOException(JGitText.get().cachedPacksPreventsIndexCreation);
-
long writeStart = System.currentTimeMillis();
- final PackIndexWriter iw = PackIndexWriter.createVersion(
- indexStream, getIndexVersion());
iw.write(sortByName(), packcsum);
stats.timeWriting += System.currentTimeMillis() - writeStart;
}
@@ -2461,7 +2464,7 @@ private final boolean have(ObjectToPack ptr, AnyObjectId objectId) {
* object graph at selected commits. Writing a bitmap index is an optional
* feature that not all pack users may require.
* <p>
- * Called after {@link #writeIndex(OutputStream)}.
+ * Called after {@link #writeIndex(PackIndexWriter)}.
* <p>
* To reduce memory internal state is cleared during this method, rendering
* the PackWriter instance useless for anything further than a call to write
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java
index dabc1f0..bf87c4c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java
@@ -14,6 +14,7 @@
import static org.eclipse.jgit.revwalk.RevFlag.SEEN;
import java.io.IOException;
+import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -28,16 +29,16 @@
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.revwalk.AddUnseenToBitmapFilter;
import org.eclipse.jgit.internal.storage.file.BitmapIndexImpl;
+import org.eclipse.jgit.internal.storage.file.BitmapIndexImpl.CompressedBitmap;
import org.eclipse.jgit.internal.storage.file.PackBitmapIndex;
import org.eclipse.jgit.internal.storage.file.PackBitmapIndexBuilder;
import org.eclipse.jgit.internal.storage.file.PackBitmapIndexRemapper;
-import org.eclipse.jgit.internal.storage.file.BitmapIndexImpl.CompressedBitmap;
import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.ProgressMonitor;
-import org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder;
import org.eclipse.jgit.revwalk.BitmapWalker;
import org.eclipse.jgit.revwalk.ObjectWalk;
import org.eclipse.jgit.revwalk.RevCommit;
@@ -99,10 +100,10 @@ class PackWriterBitmapPreparer {
this.excessiveBranchCount = config.getBitmapExcessiveBranchCount();
this.excessiveBranchTipCount = Math.max(excessiveBranchCount,
config.getBitmapExcessiveBranchTipCount());
- long now = SystemReader.getInstance().getCurrentTime();
+ Instant now = SystemReader.getInstance().now();
long ageInSeconds = (long) config.getBitmapInactiveBranchAgeInDays()
* DAY_IN_SECONDS;
- this.inactiveBranchTimestamp = (now / 1000) - ageInSeconds;
+ this.inactiveBranchTimestamp = now.getEpochSecond() - ageInSeconds;
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockReader.java
index d07713d..e9ff027 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockReader.java
@@ -32,6 +32,8 @@
import java.io.IOException;
import java.nio.ByteBuffer;
+import java.time.Instant;
+import java.time.ZoneOffset;
import java.util.Arrays;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
@@ -245,9 +247,9 @@ private String readValueString() {
private PersonIdent readPersonIdent() {
String name = readValueString();
String email = readValueString();
- long ms = readVarint64() * 1000;
- int tz = readInt16();
- return new PersonIdent(name, email, ms, tz);
+ long epochSeconds = readVarint64();
+ ZoneOffset tz = ZoneOffset.ofTotalSeconds(readInt16() * 60);
+ return new PersonIdent(name, email, Instant.ofEpochSecond(epochSeconds), tz);
}
void readBlock(BlockSource src, long pos, int fileBlockSize)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java
index 3e75a9d..542d6e9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/ssh/OpenSshConfigFile.java
@@ -427,7 +427,35 @@ protected List<String> validate(String key, List<String> value) {
return value;
}
- private static boolean patternMatchesHost(String pattern, String name) {
+ /**
+ * Tells whether a given {@code name} matches the given list of patterns,
+ * accounting for negative matches.
+ *
+ * @param patterns
+ * to test {@code name} against; any pattern starting with an
+ * exclamation mark is a negative pattern
+ * @param name
+ * to test
+ * @return {@code true} if the {@code name} matches at least one of the
+ * non-negative patterns and none of the negative patterns,
+ * {@code false} otherwise
+ * @since 7.1
+ */
+ public static boolean patternMatch(Iterable<String> patterns, String name) {
+ boolean doesMatch = false;
+ for (String pattern : patterns) {
+ if (pattern.startsWith("!")) { //$NON-NLS-1$
+ if (patternMatches(pattern.substring(1), name)) {
+ return false;
+ }
+ } else if (!doesMatch && patternMatches(pattern, name)) {
+ doesMatch = true;
+ }
+ }
+ return doesMatch;
+ }
+
+ private static boolean patternMatches(String pattern, String name) {
if (pattern.indexOf('*') >= 0 || pattern.indexOf('?') >= 0) {
final FileNameMatcher fn;
try {
@@ -680,18 +708,7 @@ public HostEntry(List<String> patterns) {
}
boolean matches(String hostName) {
- boolean doesMatch = false;
- for (String pattern : patterns) {
- if (pattern.startsWith("!")) { //$NON-NLS-1$
- if (patternMatchesHost(pattern.substring(1), hostName)) {
- return false;
- }
- } else if (!doesMatch
- && patternMatchesHost(pattern, hostName)) {
- doesMatch = true;
- }
- }
- return doesMatch;
+ return patternMatch(patterns, hostName);
}
private static String toKey(String key) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/util/Optionally.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/util/Optionally.java
index 3447f66..270b760 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/util/Optionally.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/util/Optionally.java
@@ -53,24 +53,24 @@ public class Hard<T> implements Optionally<T> {
/**
* The mutable optional object
*/
- protected T element;
+ protected Optional<T> optional;
/**
* @param element
* the mutable optional object
*/
public Hard(T element) {
- this.element = element;
+ optional = Optional.ofNullable(element);
}
@Override
public void clear() {
- element = null;
+ optional = Optional.empty();
}
@Override
public Optional<T> getOptional() {
- return Optional.ofNullable(element);
+ return optional;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AbstractGpgSignatureVerifier.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AbstractGpgSignatureVerifier.java
deleted file mode 100644
index 06a89dc..0000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AbstractGpgSignatureVerifier.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
-*
-* This program and the accompanying materials are made available under the
-* terms of the Eclipse Distribution License v. 1.0 which is available at
-* https://www.eclipse.org/org/documents/edl-v10.php.
-*
-* SPDX-License-Identifier: BSD-3-Clause
-*/
-package org.eclipse.jgit.lib;
-
-import java.io.IOException;
-import java.util.Arrays;
-
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevObject;
-import org.eclipse.jgit.revwalk.RevTag;
-import org.eclipse.jgit.util.RawParseUtils;
-
-/**
- * Provides a base implementation of
- * {@link GpgSignatureVerifier#verifySignature(RevObject, GpgConfig)}.
- *
- * @since 6.9
- */
-public abstract class AbstractGpgSignatureVerifier
- implements GpgSignatureVerifier {
-
- @Override
- public SignatureVerification verifySignature(RevObject object,
- GpgConfig config) throws IOException {
- if (object instanceof RevCommit) {
- RevCommit commit = (RevCommit) object;
- byte[] signatureData = commit.getRawGpgSignature();
- if (signatureData == null) {
- return null;
- }
- byte[] raw = commit.getRawBuffer();
- // Now remove the GPG signature
- byte[] header = { 'g', 'p', 'g', 's', 'i', 'g' };
- int start = RawParseUtils.headerStart(header, raw, 0);
- if (start < 0) {
- return null;
- }
- int end = RawParseUtils.nextLfSkippingSplitLines(raw, start);
- // start is at the beginning of the header's content
- start -= header.length + 1;
- // end is on the terminating LF; we need to skip that, too
- if (end < raw.length) {
- end++;
- }
- byte[] data = new byte[raw.length - (end - start)];
- System.arraycopy(raw, 0, data, 0, start);
- System.arraycopy(raw, end, data, start, raw.length - end);
- return verify(config, data, signatureData);
- } else if (object instanceof RevTag) {
- RevTag tag = (RevTag) object;
- byte[] signatureData = tag.getRawGpgSignature();
- if (signatureData == null) {
- return null;
- }
- byte[] raw = tag.getRawBuffer();
- // The signature is just tacked onto the end of the message, which
- // is last in the buffer.
- byte[] data = Arrays.copyOfRange(raw, 0,
- raw.length - signatureData.length);
- return verify(config, data, signatureData);
- }
- return null;
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java
index c58133a..f742e99 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java
@@ -35,23 +35,6 @@ public abstract class AnyObjectId implements Comparable<AnyObjectId> {
* @param secondObjectId
* the second identifier to compare. Must not be null.
* @return true if the two identifiers are the same.
- * @deprecated use {@link #isEqual(AnyObjectId, AnyObjectId)} instead
- */
- @Deprecated
- @SuppressWarnings("AmbiguousMethodReference")
- public static boolean equals(final AnyObjectId firstObjectId,
- final AnyObjectId secondObjectId) {
- return isEqual(firstObjectId, secondObjectId);
- }
-
- /**
- * Compare two object identifier byte sequences for equality.
- *
- * @param firstObjectId
- * the first identifier to compare. Must not be null.
- * @param secondObjectId
- * the second identifier to compare. Must not be null.
- * @return true if the two identifiers are the same.
* @since 5.4
*/
public static boolean isEqual(final AnyObjectId firstObjectId,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java
index 5dfb648..0c1da83 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java
@@ -13,13 +13,17 @@
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_CORE_SECTION;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_BARE;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_WORKTREE;
+import static org.eclipse.jgit.lib.Constants.CONFIG;
import static org.eclipse.jgit.lib.Constants.DOT_GIT;
+import static org.eclipse.jgit.lib.Constants.GITDIR_FILE;
import static org.eclipse.jgit.lib.Constants.GIT_ALTERNATE_OBJECT_DIRECTORIES_KEY;
import static org.eclipse.jgit.lib.Constants.GIT_CEILING_DIRECTORIES_KEY;
+import static org.eclipse.jgit.lib.Constants.GIT_COMMON_DIR_KEY;
import static org.eclipse.jgit.lib.Constants.GIT_DIR_KEY;
import static org.eclipse.jgit.lib.Constants.GIT_INDEX_FILE_KEY;
import static org.eclipse.jgit.lib.Constants.GIT_OBJECT_DIRECTORY_KEY;
import static org.eclipse.jgit.lib.Constants.GIT_WORK_TREE_KEY;
+import static org.eclipse.jgit.lib.Constants.OBJECTS;
import java.io.File;
import java.io.IOException;
@@ -70,7 +74,21 @@ private static boolean isSymRef(byte[] ref) {
&& ref[7] == ' ';
}
- private static File getSymRef(File workTree, File dotGit, FS fs)
+ /**
+ * Read symbolic reference file
+ *
+ * @param workTree
+ * the work tree path
+ * @param dotGit
+ * the .git file
+ * @param fs
+ * th FS util
+ * @return the file read from symbolic reference file
+ * @throws java.io.IOException
+ * the dotGit file is invalid reference
+ * @since 7.0
+ */
+ static File getSymRef(File workTree, File dotGit, FS fs)
throws IOException {
byte[] content = IO.readFully(dotGit);
if (!isSymRef(content)) {
@@ -102,6 +120,8 @@ private static File getSymRef(File workTree, File dotGit, FS fs)
private File gitDir;
+ private File gitCommonDir;
+
private File objectDirectory;
private List<File> alternateObjectDirectories;
@@ -172,6 +192,30 @@ public File getGitDir() {
}
/**
+ * Set common dir.
+ *
+ * @param gitCommonDir
+ * {@code GIT_COMMON_DIR}, the common repository meta directory.
+ * @return {@code this} (for chaining calls).
+ * @since 7.0
+ */
+ public B setGitCommonDir(File gitCommonDir) {
+ this.gitCommonDir = gitCommonDir;
+ this.config = null;
+ return self();
+ }
+
+ /**
+ * Get common dir.
+ *
+ * @return common dir; null if not set.
+ * @since 7.0
+ */
+ public File getGitCommonDir() {
+ return gitCommonDir;
+ }
+
+ /**
* Set the directory storing the repository's objects.
*
* @param objectDirectory
@@ -396,9 +440,9 @@ public B setInitialBranch(String branch) throws InvalidRefNameException {
* Read standard Git environment variables and configure from those.
* <p>
* This method tries to read the standard Git environment variables, such as
- * {@code GIT_DIR} and {@code GIT_WORK_TREE} to configure this builder
- * instance. If an environment variable is set, it overrides the value
- * already set in this builder.
+ * {@code GIT_DIR}, {@code GIT_COMMON_DIR}, {@code GIT_WORK_TREE} etc. to
+ * configure this builder instance. If an environment variable is set, it
+ * overrides the value already set in this builder.
*
* @return {@code this} (for chaining calls).
*/
@@ -410,9 +454,9 @@ public B readEnvironment() {
* Read standard Git environment variables and configure from those.
* <p>
* This method tries to read the standard Git environment variables, such as
- * {@code GIT_DIR} and {@code GIT_WORK_TREE} to configure this builder
- * instance. If a property is already set in the builder, the environment
- * variable is not used.
+ * {@code GIT_DIR}, {@code GIT_COMMON_DIR}, {@code GIT_WORK_TREE} etc. to
+ * configure this builder instance. If a property is already set in the
+ * builder, the environment variable is not used.
*
* @param sr
* the SystemReader abstraction to access the environment.
@@ -425,6 +469,13 @@ public B readEnvironment(SystemReader sr) {
setGitDir(new File(val));
}
+ if (getGitCommonDir() == null) {
+ String val = sr.getenv(GIT_COMMON_DIR_KEY);
+ if (val != null) {
+ setGitCommonDir(new File(val));
+ }
+ }
+
if (getObjectDirectory() == null) {
String val = sr.getenv(GIT_OBJECT_DIRECTORY_KEY);
if (val != null)
@@ -434,7 +485,7 @@ public B readEnvironment(SystemReader sr) {
if (getAlternateObjectDirectories() == null) {
String val = sr.getenv(GIT_ALTERNATE_OBJECT_DIRECTORIES_KEY);
if (val != null) {
- for (String path : val.split(File.pathSeparator))
+ for (String path : val.split(File.pathSeparator, -1))
addAlternateObjectDirectory(new File(path));
}
}
@@ -454,7 +505,7 @@ public B readEnvironment(SystemReader sr) {
if (ceilingDirectories == null) {
String val = sr.getenv(GIT_CEILING_DIRECTORIES_KEY);
if (val != null) {
- for (String path : val.split(File.pathSeparator))
+ for (String path : val.split(File.pathSeparator, -1))
addCeilingDirectory(new File(path));
}
}
@@ -601,6 +652,7 @@ public B findGitDir(File current) {
public B setup() throws IllegalArgumentException, IOException {
requireGitDirOrWorkTree();
setupGitDir();
+ setupCommonDir();
setupWorkTree();
setupInternals();
return self();
@@ -658,6 +710,20 @@ protected void setupGitDir() throws IOException {
}
/**
+ * Perform standard common dir initialization.
+ *
+ * @throws java.io.IOException
+ * the repository could not be accessed
+ * @since 7.0
+ */
+ protected void setupCommonDir() throws IOException {
+ // no gitCommonDir? Try to get it from gitDir
+ if (getGitCommonDir() == null) {
+ setGitCommonDir(safeFS().getCommonDir(getGitDir()));
+ }
+ }
+
+ /**
* Perform standard work-tree initialization.
* <p>
* This is a method typically invoked inside of {@link #setup()}, near the
@@ -695,8 +761,12 @@ protected void setupWorkTree() throws IOException {
* the repository could not be accessed
*/
protected void setupInternals() throws IOException {
- if (getObjectDirectory() == null && getGitDir() != null)
- setObjectDirectory(safeFS().resolve(getGitDir(), Constants.OBJECTS));
+ if (getObjectDirectory() == null) {
+ File commonDir = getGitCommonDir();
+ if (commonDir != null) {
+ setObjectDirectory(safeFS().resolve(commonDir, OBJECTS));
+ }
+ }
}
/**
@@ -723,12 +793,13 @@ protected Config getConfig() throws IOException {
* the configuration is not available.
*/
protected Config loadConfig() throws IOException {
- if (getGitDir() != null) {
+ File commonDir = getGitCommonDir();
+ if (commonDir != null) {
// We only want the repository's configuration file, and not
// the user file, as these parameters must be unique to this
// repository and not inherited from other files.
//
- File path = safeFS().resolve(getGitDir(), Constants.CONFIG);
+ File path = safeFS().resolve(commonDir, CONFIG);
FileBasedConfig cfg = new FileBasedConfig(path, safeFS());
try {
cfg.load();
@@ -749,8 +820,29 @@ private File guessWorkTreeOrFail() throws IOException {
//
String path = cfg.getString(CONFIG_CORE_SECTION, null,
CONFIG_KEY_WORKTREE);
- if (path != null)
+ if (path != null) {
return safeFS().resolve(getGitDir(), path).getCanonicalFile();
+ }
+
+ /*
+ * We are in worktree's $GIT_DIR folder
+ * ".git/worktrees/<worktree-name>" and want to get the working
+ * tree (checkout) path; so here we have an opposite link in file
+ * "gitdir" showing to the ".git" file located in the working tree read
+ * it and convert it to absolute path if it's relative
+ */
+ File gitDirFile = new File(getGitDir(), GITDIR_FILE);
+ if (gitDirFile.isFile()) {
+ String workDirPath = new String(IO.readFully(gitDirFile)).trim();
+ File workTreeDotGitFile = new File(workDirPath);
+ if (!workTreeDotGitFile.isAbsolute()) {
+ workTreeDotGitFile = new File(getGitDir(), workDirPath)
+ .getCanonicalFile();
+ }
+ if (workTreeDotGitFile != null) {
+ return workTreeDotGitFile.getParentFile();
+ }
+ }
// If core.bare is set, honor its value. Assume workTree is
// the parent directory of the repository.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BranchConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BranchConfig.java
index e15c7af..7921052 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BranchConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BranchConfig.java
@@ -187,8 +187,7 @@ public boolean isRebase() {
* @since 4.5
*/
public BranchRebaseMode getRebaseMode() {
- return config.getEnum(BranchRebaseMode.values(),
- ConfigConstants.CONFIG_BRANCH_SECTION, branchName,
+ return config.getEnum(ConfigConstants.CONFIG_BRANCH_SECTION, branchName,
ConfigConstants.CONFIG_KEY_REBASE, BranchRebaseMode.NONE);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitBuilder.java
index ea33082..ad3c2c0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitBuilder.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitBuilder.java
@@ -194,19 +194,6 @@ public void addParentId(AnyObjectId additionalParent) {
}
}
- /**
- * Set the encoding for the commit information.
- *
- * @param encodingName
- * the encoding name. See
- * {@link java.nio.charset.Charset#forName(String)}.
- * @deprecated use {@link #setEncoding(Charset)} instead.
- */
- @Deprecated
- public void setEncoding(String encodingName) {
- setEncoding(Charset.forName(encodingName));
- }
-
@Override
public byte[] build() throws UnsupportedEncodingException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitConfig.java
index f701a41..b1ba5df 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitConfig.java
@@ -119,7 +119,7 @@ private CommitConfig(Config rc) {
if (!StringUtils.isEmptyOrNull(comment)) {
if ("auto".equalsIgnoreCase(comment)) { //$NON-NLS-1$
autoCommentChar = true;
- } else {
+ } else if (comment != null) {
char first = comment.charAt(0);
if (first > ' ' && first < 127) {
commentCharacter = first;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
index 07c5fa4..345cb22 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
@@ -34,6 +34,7 @@
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.events.ConfigChangedEvent;
import org.eclipse.jgit.events.ConfigChangedListener;
@@ -254,9 +255,8 @@ static String escapeSubsection(String x) {
* default value to return if no value was present.
* @return an integer value from the configuration, or defaultValue.
*/
- public int getInt(final String section, final String name,
- final int defaultValue) {
- return typedGetter.getInt(this, section, null, name, defaultValue);
+ public int getInt(String section, String name, int defaultValue) {
+ return getInt(section, null, name, defaultValue);
}
/**
@@ -264,6 +264,23 @@ public int getInt(final String section, final String name,
*
* @param section
* section the key is grouped within.
+ * @param name
+ * name of the key to get.
+ * @return an integer value from the configuration, or {@code null} if not
+ * set.
+ * @since 7.2
+ */
+ @Nullable
+ public Integer getInt(String section, String name) {
+ return getInt(section, null, name);
+ }
+
+
+ /**
+ * Obtain an integer value from the configuration.
+ *
+ * @param section
+ * section the key is grouped within.
* @param subsection
* subsection name, such a remote or branch name.
* @param name
@@ -272,10 +289,30 @@ public int getInt(final String section, final String name,
* default value to return if no value was present.
* @return an integer value from the configuration, or defaultValue.
*/
- public int getInt(final String section, String subsection,
- final String name, final int defaultValue) {
+ public int getInt(String section, String subsection, String name,
+ int defaultValue) {
+ Integer v = typedGetter.getInt(this, section, subsection, name,
+ Integer.valueOf(defaultValue));
+ return v == null ? defaultValue : v.intValue();
+ }
+
+ /**
+ * Obtain an integer value from the configuration.
+ *
+ * @param section
+ * section the key is grouped within.
+ * @param subsection
+ * subsection name, such a remote or branch name.
+ * @param name
+ * name of the key to get.
+ * @return an integer value from the configuration, or {@code null} if not
+ * set.
+ * @since 7.2
+ */
+ @Nullable
+ public Integer getInt(String section, String subsection, String name) {
return typedGetter.getInt(this, section, subsection, name,
- defaultValue);
+ null);
}
/**
@@ -297,8 +334,30 @@ public int getInt(final String section, String subsection,
*/
public int getIntInRange(String section, String name, int minValue,
int maxValue, int defaultValue) {
- return typedGetter.getIntInRange(this, section, null, name, minValue,
- maxValue, defaultValue);
+ return getIntInRange(section, null, name,
+ minValue, maxValue, defaultValue);
+ }
+
+ /**
+ * Obtain an integer value from the configuration which must be inside given
+ * range.
+ *
+ * @param section
+ * section the key is grouped within.
+ * @param name
+ * name of the key to get.
+ * @param minValue
+ * minimum value
+ * @param maxValue
+ * maximum value
+ * @return an integer value from the configuration, or {@code null} if not
+ * set.
+ * @since 7.2
+ */
+ @Nullable
+ public Integer getIntInRange(String section, String name, int minValue,
+ int maxValue) {
+ return getIntInRange(section, null, name, minValue, maxValue);
}
/**
@@ -322,8 +381,34 @@ public int getIntInRange(String section, String name, int minValue,
*/
public int getIntInRange(String section, String subsection, String name,
int minValue, int maxValue, int defaultValue) {
+ Integer v = typedGetter.getIntInRange(this, section, subsection, name,
+ minValue, maxValue, Integer.valueOf(defaultValue));
+ return v == null ? defaultValue : v.intValue();
+ }
+
+ /**
+ * Obtain an integer value from the configuration which must be inside given
+ * range.
+ *
+ * @param section
+ * section the key is grouped within.
+ * @param subsection
+ * subsection name, such a remote or branch name.
+ * @param name
+ * name of the key to get.
+ * @param minValue
+ * minimum value
+ * @param maxValue
+ * maximum value
+ * @return an integer value from the configuration, or {@code null} if not
+ * set.
+ * @since 7.2
+ */
+ @Nullable
+ public Integer getIntInRange(String section, String subsection, String name,
+ int minValue, int maxValue) {
return typedGetter.getIntInRange(this, section, subsection, name,
- minValue, maxValue, defaultValue);
+ minValue, maxValue, null);
}
/**
@@ -338,7 +423,23 @@ public int getIntInRange(String section, String subsection, String name,
* @return an integer value from the configuration, or defaultValue.
*/
public long getLong(String section, String name, long defaultValue) {
- return typedGetter.getLong(this, section, null, name, defaultValue);
+ return getLong(section, null, name, defaultValue);
+ }
+
+ /**
+ * Obtain an integer value from the configuration.
+ *
+ * @param section
+ * section the key is grouped within.
+ * @param name
+ * name of the key to get.
+ * @return an integer value from the configuration, or {@code null} if not
+ * set.
+ * @since 7.2
+ */
+ @Nullable
+ public Long getLong(String section, String name) {
+ return getLong(section, null, name);
}
/**
@@ -355,9 +456,28 @@ public long getLong(String section, String name, long defaultValue) {
* @return an integer value from the configuration, or defaultValue.
*/
public long getLong(final String section, String subsection,
- final String name, final long defaultValue) {
- return typedGetter.getLong(this, section, subsection, name,
- defaultValue);
+ String name, long defaultValue) {
+ Long v = typedGetter.getLong(this, section, subsection, name,
+ Long.valueOf(defaultValue));
+ return v == null ? defaultValue : v.longValue();
+ }
+
+ /**
+ * Obtain an integer value from the configuration.
+ *
+ * @param section
+ * section the key is grouped within.
+ * @param subsection
+ * subsection name, such a remote or branch name.
+ * @param name
+ * name of the key to get.
+ * @return an integer value from the configuration, or {@code null} if not
+ * set.
+ * @since 7.2
+ */
+ @Nullable
+ public Long getLong(String section, String subsection, String name) {
+ return typedGetter.getLong(this, section, subsection, name, null);
}
/**
@@ -372,9 +492,26 @@ public long getLong(final String section, String subsection,
* @return true if any value or defaultValue is true, false for missing or
* explicit false
*/
- public boolean getBoolean(final String section, final String name,
- final boolean defaultValue) {
- return typedGetter.getBoolean(this, section, null, name, defaultValue);
+ public boolean getBoolean(String section, String name,
+ boolean defaultValue) {
+ Boolean v = typedGetter.getBoolean(this, section, null, name,
+ Boolean.valueOf(defaultValue));
+ return v == null ? defaultValue : v.booleanValue();
+ }
+
+ /**
+ * Get a boolean value from the git config
+ *
+ * @param section
+ * section the key is grouped within.
+ * @param name
+ * name of the key to get.
+ * @return configured boolean value, or {@code null} if not set.
+ * @since 7.2
+ */
+ @Nullable
+ public Boolean getBoolean(String section, String name) {
+ return getBoolean(section, null, name);
}
/**
@@ -391,10 +528,28 @@ public boolean getBoolean(final String section, final String name,
* @return true if any value or defaultValue is true, false for missing or
* explicit false
*/
- public boolean getBoolean(final String section, String subsection,
- final String name, final boolean defaultValue) {
- return typedGetter.getBoolean(this, section, subsection, name,
- defaultValue);
+ public boolean getBoolean(String section, String subsection, String name,
+ boolean defaultValue) {
+ Boolean v = typedGetter.getBoolean(this, section, subsection, name,
+ Boolean.valueOf(defaultValue));
+ return v == null ? defaultValue : v.booleanValue();
+ }
+
+ /**
+ * Get a boolean value from the git config
+ *
+ * @param section
+ * section the key is grouped within.
+ * @param subsection
+ * subsection name, such a remote or branch name.
+ * @param name
+ * name of the key to get.
+ * @return configured boolean value, or {@code null} if not set.
+ * @since 7.2
+ */
+ @Nullable
+ public Boolean getBoolean(String section, String subsection, String name) {
+ return typedGetter.getBoolean(this, section, subsection, name, null);
}
/**
@@ -412,8 +567,8 @@ public boolean getBoolean(final String section, String subsection,
* default value to return if no value was present.
* @return the selected enumeration value, or {@code defaultValue}.
*/
- public <T extends Enum<?>> T getEnum(final String section,
- final String subsection, final String name, final T defaultValue) {
+ public <T extends Enum<?>> T getEnum(String section, String subsection,
+ String name, @NonNull T defaultValue) {
final T[] all = allValuesOf(defaultValue);
return typedGetter.getEnum(this, all, section, subsection, name,
defaultValue);
@@ -448,14 +603,41 @@ public <T extends Enum<?>> T getEnum(final String section,
* @param defaultValue
* default value to return if no value was present.
* @return the selected enumeration value, or {@code defaultValue}.
+ * @deprecated use {@link #getEnum(String, String, String, Enum)} or
+ * {{@link #getEnum(Enum[], String, String, String)}} instead.
*/
- public <T extends Enum<?>> T getEnum(final T[] all, final String section,
- final String subsection, final String name, final T defaultValue) {
+ @Nullable
+ @Deprecated
+ public <T extends Enum<?>> T getEnum(T[] all, String section,
+ String subsection, String name, @Nullable T defaultValue) {
return typedGetter.getEnum(this, all, section, subsection, name,
defaultValue);
}
/**
+ * Parse an enumeration from the configuration.
+ *
+ * @param <T>
+ * type of the returned enum
+ * @param all
+ * all possible values in the enumeration which should be
+ * recognized. Typically {@code EnumType.values()}.
+ * @param section
+ * section the key is grouped within.
+ * @param subsection
+ * subsection name, such a remote or branch name.
+ * @param name
+ * name of the key to get.
+ * @return the selected enumeration value, or {@code null} if not set.
+ * @since 7.2
+ */
+ @Nullable
+ public <T extends Enum<?>> T getEnum(T[] all, String section,
+ String subsection, String name) {
+ return typedGetter.getEnum(this, all, section, subsection, name, null);
+ }
+
+ /**
* Get string value or null if not found.
*
* @param section
@@ -466,8 +648,8 @@ public <T extends Enum<?>> T getEnum(final T[] all, final String section,
* the key name
* @return a String value from the config, <code>null</code> if not found
*/
- public String getString(final String section, String subsection,
- final String name) {
+ @Nullable
+ public String getString(String section, String subsection, String name) {
return getRawString(section, subsection, name);
}
@@ -526,8 +708,34 @@ public String getString(final String section, String subsection,
*/
public long getTimeUnit(String section, String subsection, String name,
long defaultValue, TimeUnit wantUnit) {
+ Long v = typedGetter.getTimeUnit(this, section, subsection, name,
+ Long.valueOf(defaultValue), wantUnit);
+ return v == null ? defaultValue : v.longValue();
+
+ }
+
+ /**
+ * Parse a numerical time unit, such as "1 minute", from the configuration.
+ *
+ * @param section
+ * section the key is in.
+ * @param subsection
+ * subsection the key is in, or null if not in a subsection.
+ * @param name
+ * the key name.
+ * @param wantUnit
+ * the units of {@code defaultValue} and the return value, as
+ * well as the units to assume if the value does not contain an
+ * indication of the units.
+ * @return the value, or {@code null} if not set, expressed in
+ * {@code units}.
+ * @since 7.2
+ */
+ @Nullable
+ public Long getTimeUnit(String section, String subsection, String name,
+ TimeUnit wantUnit) {
return typedGetter.getTimeUnit(this, section, subsection, name,
- defaultValue, wantUnit);
+ null, wantUnit);
}
/**
@@ -555,8 +763,9 @@ public long getTimeUnit(String section, String subsection, String name,
* @return the {@link Path}, or {@code defaultValue} if not set
* @since 5.10
*/
+ @Nullable
public Path getPath(String section, String subsection, String name,
- @NonNull FS fs, File resolveAgainst, Path defaultValue) {
+ @NonNull FS fs, File resolveAgainst, @Nullable Path defaultValue) {
return typedGetter.getPath(this, section, subsection, name, fs,
resolveAgainst, defaultValue);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
index 0edf3c5..c455032 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
@@ -78,6 +78,13 @@ public final class ConfigConstants {
public static final String CONFIG_DFS_SECTION = "dfs";
/**
+ * The dfs cache subsection prefix.
+ *
+ * @since 7.0
+ */
+ public static final String CONFIG_DFS_CACHE_PREFIX = "dfs.";
+
+ /**
* The "receive" section
* @since 4.6
*/
@@ -199,7 +206,36 @@ public final class ConfigConstants {
public static final String CONFIG_KEY_SIGNINGKEY = "signingKey";
/**
+ * The "ssh" subsection key.
+ *
+ * @since 7.1
+ */
+ public static final String CONFIG_SSH_SUBSECTION = "ssh";
+
+ /**
+ * The "defaultKeyCommand" key.
+ *
+ * @since 7.1
+ */
+ public static final String CONFIG_KEY_SSH_DEFAULT_KEY_COMMAND = "defaultKeyCommand";
+
+ /**
+ * The "allowedSignersFile" key.
+ *
+ * @since 7.1
+ */
+ public static final String CONFIG_KEY_SSH_ALLOWED_SIGNERS_FILE = "allowedSignersFile";
+
+ /**
+ * The "revocationFile" key,
+ *
+ * @since 7.1
+ */
+ public static final String CONFIG_KEY_SSH_REVOCATION_FILE = "revocationFile";
+
+ /**
* The "commit" section
+ *
* @since 5.2
*/
public static final String CONFIG_COMMIT_SECTION = "commit";
@@ -332,6 +368,13 @@ public final class ConfigConstants {
public static final String CONFIG_KEY_DELTA_BASE_CACHE_LIMIT = "deltaBaseCacheLimit";
/**
+ * The "packExtensions" key
+ *
+ * @since 7.0
+ **/
+ public static final String CONFIG_KEY_PACK_EXTENSIONS = "packExtensions";
+
+ /**
* The "symlinks" key
* @since 3.3
*/
@@ -345,12 +388,6 @@ public final class ConfigConstants {
public static final String CONFIG_KEY_STREAM_FILE_THRESHOLD = "streamFileThreshold";
/**
- * @deprecated typo, use CONFIG_KEY_STREAM_FILE_THRESHOLD instead
- */
- @Deprecated(since = "6.8")
- public static final String CONFIG_KEY_STREAM_FILE_TRESHOLD = CONFIG_KEY_STREAM_FILE_THRESHOLD;
-
- /**
* The "packedGitMmap" key
* @since 5.1.13
*/
@@ -409,6 +446,13 @@ public final class ConfigConstants {
/** The "rebase" key */
public static final String CONFIG_KEY_REBASE = "rebase";
+ /**
+ * The "checkout" key
+ *
+ * @since 7.2
+ */
+ public static final String CONFIG_KEY_CHECKOUT = "checkout";
+
/** The "url" key */
public static final String CONFIG_KEY_URL = "url";
@@ -556,11 +600,21 @@ public final class ConfigConstants {
/**
* The "trustfolderstat" key in the "core" section
+ *
* @since 3.6
+ * @deprecated use {CONFIG_KEY_TRUST_STAT} instead
*/
+ @Deprecated(since = "7.2", forRemoval = true)
public static final String CONFIG_KEY_TRUSTFOLDERSTAT = "trustfolderstat";
/**
+ * The "trustfilestat" key in the "core"section
+ *
+ * @since 7.2
+ */
+ public static final String CONFIG_KEY_TRUST_STAT = "truststat";
+
+ /**
* The "supportsAtomicFileCreation" key in the "core" section
*
* @since 4.5
@@ -979,6 +1033,27 @@ public final class ConfigConstants {
public static final String CONFIG_KEY_TRUST_LOOSE_REF_STAT = "trustLooseRefStat";
/**
+ * The "trustLooseRefStat" key
+ *
+ * @since 7.2
+ */
+ public static final String CONFIG_KEY_TRUST_PACK_STAT = "trustPackStat";
+
+ /**
+ * The "trustLooseObjectFileStat" key
+ *
+ * @since 7.2
+ */
+ public static final String CONFIG_KEY_TRUST_LOOSE_OBJECT_STAT = "trustLooseObjectStat";
+
+ /**
+ * The "trustTablesListStat" key
+ *
+ * @since 7.2
+ */
+ public static final String CONFIG_KEY_TRUST_TABLESLIST_STAT = "trustTablesListStat";
+
+ /**
* The "pack.preserveOldPacks" key
*
* @since 5.13.2
@@ -1012,4 +1087,32 @@ public final class ConfigConstants {
* @since 6.7
*/
public static final String CONFIG_KEY_READ_CHANGED_PATHS = "readChangedPaths";
+
+ /**
+ * The "useObjectSizeIndex" key
+ *
+ * @since 7.0
+ */
+ public static final String CONFIG_KEY_USE_OBJECT_SIZE_INDEX = "useObjectSizeIndex";
+
+ /**
+ * The "loadRevIndexInParallel" key
+ *
+ * @since 7.1
+ */
+ public static final String CONFIG_KEY_LOAD_REV_INDEX_IN_PARALLEL = "loadRevIndexInParallel";
+
+ /**
+ * The "reftable" section
+ *
+ * @since 7.2
+ */
+ public static final String CONFIG_REFTABLE_SECTION = "reftable";
+
+ /**
+ * The "autorefresh" key
+ *
+ * @since 7.2
+ */
+ public static final String CONFIG_KEY_AUTOREFRESH = "autorefresh";
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
index 60a23dd..997f4ed 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
@@ -12,10 +12,13 @@
package org.eclipse.jgit.lib;
+import static java.nio.charset.StandardCharsets.US_ASCII;
import static java.nio.charset.StandardCharsets.UTF_8;
-import java.nio.ByteBuffer;
-import java.nio.charset.Charset;
+import java.nio.CharBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.CodingErrorAction;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.MessageFormat;
@@ -203,24 +206,6 @@ public final class Constants {
*/
public static final byte[] PACK_SIGNATURE = { 'P', 'A', 'C', 'K' };
- /**
- * Native character encoding for commit messages, file names...
- *
- * @deprecated Use {@link java.nio.charset.StandardCharsets#UTF_8} directly
- * instead.
- */
- @Deprecated
- public static final Charset CHARSET;
-
- /**
- * Native character encoding for commit messages, file names...
- *
- * @deprecated Use {@link java.nio.charset.StandardCharsets#UTF_8} directly
- * instead.
- */
- @Deprecated
- public static final String CHARACTER_ENCODING;
-
/** Default main branch name */
public static final String MASTER = "master";
@@ -273,6 +258,20 @@ public final class Constants {
public static final String INFO_REFS = "info/refs";
/**
+ * Name of heads folder or file in refs.
+ *
+ * @since 7.0
+ */
+ public static final String HEADS = "heads";
+
+ /**
+ * Prefix for any log.
+ *
+ * @since 7.0
+ */
+ public static final String L_LOGS = LOGS + "/";
+
+ /**
* Info alternates file (goes under OBJECTS)
* @since 5.5
*/
@@ -358,6 +357,14 @@ public final class Constants {
public static final String GIT_DIR_KEY = "GIT_DIR";
/**
+ * The environment variable that tells us which directory is the common
+ * ".git" directory.
+ *
+ * @since 7.0
+ */
+ public static final String GIT_COMMON_DIR_KEY = "GIT_COMMON_DIR";
+
+ /**
* The environment variable that tells us which directory is the working
* directory.
*/
@@ -459,6 +466,36 @@ public final class Constants {
public static final String GITDIR = "gitdir: ";
/**
+ * Name of the file (inside gitDir) that references the worktree's .git
+ * file (opposite link).
+ *
+ * .git/worktrees/<worktree-name>/gitdir
+ *
+ * A text file containing the absolute path back to the .git file that
+ * points here. This file is used to verify if the linked repository has been
+ * manually removed in which case this directory is no longer needed.
+ * The modification time (mtime) of this file should be updated each time
+ * the linked repository is accessed.
+ *
+ * @since 7.0
+ */
+ public static final String GITDIR_FILE = "gitdir";
+
+ /**
+ * Name of the file (inside gitDir) that has reference to $GIT_COMMON_DIR.
+ *
+ * .git/worktrees/<worktree-name>/commondir
+ *
+ * If this file exists, $GIT_COMMON_DIR will be set to the path specified in
+ * this file unless it is explicitly set. If the specified path is relative,
+ * it is relative to $GIT_DIR. The repository with commondir is incomplete
+ * without the repository pointed by "commondir".
+ *
+ * @since 7.0
+ */
+ public static final String COMMONDIR_FILE = "commondir";
+
+ /**
* Name of the folder (inside gitDir) where submodules are stored
*
* @since 3.6
@@ -494,6 +531,34 @@ public final class Constants {
public static final String ATTR_BUILTIN_BINARY_MERGER = "binary"; //$NON-NLS-1$
/**
+ * Prefix of a GPG signature.
+ *
+ * @since 7.0
+ */
+ public static final String GPG_SIGNATURE_PREFIX = "-----BEGIN PGP SIGNATURE-----"; //$NON-NLS-1$
+
+ /**
+ * Prefix of a CMS signature (X.509, S/MIME).
+ *
+ * @since 7.0
+ */
+ public static final String CMS_SIGNATURE_PREFIX = "-----BEGIN SIGNED MESSAGE-----"; //$NON-NLS-1$
+
+ /**
+ * Prefix of an SSH signature.
+ *
+ * @since 7.0
+ */
+ public static final String SSH_SIGNATURE_PREFIX = "-----BEGIN SSH SIGNATURE-----"; //$NON-NLS-1$
+
+ /**
+ * Union built-in merge driver
+ *
+ * @since 6.10.1
+ */
+ public static final String ATTR_BUILTIN_UNION_MERGE_DRIVER = "union"; //$NON-NLS-1$
+
+ /**
* Create a new digest function for objects.
*
* @return a new digest object.
@@ -661,44 +726,32 @@ public static int decodeTypeString(final AnyObjectId id,
* the 7-bit ASCII character space.
*/
public static byte[] encodeASCII(String s) {
- final byte[] r = new byte[s.length()];
- for (int k = r.length - 1; k >= 0; k--) {
- final char c = s.charAt(k);
- if (c > 127)
- throw new IllegalArgumentException(MessageFormat.format(JGitText.get().notASCIIString, s));
- r[k] = (byte) c;
+ try {
+ CharsetEncoder encoder = US_ASCII.newEncoder()
+ .onUnmappableCharacter(CodingErrorAction.REPORT)
+ .onMalformedInput(CodingErrorAction.REPORT);
+ return encoder.encode(CharBuffer.wrap(s)).array();
+ } catch (CharacterCodingException e) {
+ throw new IllegalArgumentException(
+ MessageFormat.format(JGitText.get().notASCIIString, s), e);
}
- return r;
}
/**
- * Convert a string to a byte array in the standard character encoding.
+ * Convert a string to a byte array in the standard character encoding UTF8.
*
* @param str
* the string to convert. May contain any Unicode characters.
* @return a byte array representing the requested string, encoded using the
* default character encoding (UTF-8).
- * @see #CHARACTER_ENCODING
*/
public static byte[] encode(String str) {
- final ByteBuffer bb = UTF_8.encode(str);
- final int len = bb.limit();
- if (bb.hasArray() && bb.arrayOffset() == 0) {
- final byte[] arr = bb.array();
- if (arr.length == len)
- return arr;
- }
-
- final byte[] arr = new byte[len];
- bb.get(arr);
- return arr;
+ return str.getBytes(UTF_8);
}
static {
if (OBJECT_ID_LENGTH != newMessageDigest().getDigestLength())
throw new LinkageError(JGitText.get().incorrectOBJECT_ID_LENGTH);
- CHARSET = UTF_8;
- CHARACTER_ENCODING = UTF_8.name();
}
/** name of the file containing the commit msg for a merge commit */
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java
index 9fa5d75..0e27b27 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java
@@ -17,12 +17,16 @@
import static java.util.zip.Deflater.DEFAULT_COMPRESSION;
+import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Config.SectionParser;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* This class keeps git repository core parameters.
*/
public class CoreConfig {
+ private static final Logger LOG = LoggerFactory.getLogger(CoreConfig.class);
/** Key for {@link Config#get(SectionParser)}. */
public static final Config.SectionParser<CoreConfig> KEY = CoreConfig::new;
@@ -127,7 +131,9 @@ public enum LogRefUpdates {
* Permissible values for {@code core.trustPackedRefsStat}.
*
* @since 6.1.1
+ * @deprecated use {@link TrustStat} instead
*/
+ @Deprecated(since = "7.2", forRemoval = true)
public enum TrustPackedRefsStat {
/** Do not trust file attributes of the packed-refs file. */
NEVER,
@@ -135,12 +141,15 @@ public enum TrustPackedRefsStat {
/** Trust file attributes of the packed-refs file. */
ALWAYS,
- /** Open and close the packed-refs file to refresh its file attributes
- * and then trust it. */
+ /**
+ * Open and close the packed-refs file to refresh its file attributes
+ * and then trust it.
+ */
AFTER_OPEN,
- /** {@code core.trustPackedRefsStat} defaults to this when it is
- * not set */
+ /**
+ * {@code core.trustPackedRefsStat} defaults to this when it is not set
+ */
UNSET
}
@@ -148,29 +157,66 @@ public enum TrustPackedRefsStat {
* Permissible values for {@code core.trustLooseRefStat}.
*
* @since 6.9
+ * @deprecated use {@link TrustStat} instead
*/
+ @Deprecated(since = "7.2", forRemoval = true)
public enum TrustLooseRefStat {
/** Trust file attributes of the loose ref. */
ALWAYS,
- /** Open and close parent directories of the loose ref file until the
- * repository root to refresh its file attributes and then trust it. */
+ /**
+ * Open and close parent directories of the loose ref file until the
+ * repository root to refresh its file attributes and then trust it.
+ */
AFTER_OPEN,
}
+ /**
+ * Values for {@code core.trustXXX} options.
+ *
+ * @since 7.2
+ */
+ public enum TrustStat {
+ /** Do not trust file attributes of a File. */
+ NEVER,
+
+ /** Always trust file attributes of a File. */
+ ALWAYS,
+
+ /** Open and close the File to refresh its file attributes
+ * and then trust it. */
+ AFTER_OPEN,
+
+ /**
+ * Used for specific options to inherit value from value set for
+ * core.trustStat.
+ */
+ INHERIT
+ }
+
private final int compression;
private final int packIndexVersion;
- private final LogRefUpdates logAllRefUpdates;
-
private final String excludesfile;
private final String attributesfile;
private final boolean commitGraph;
+ private final TrustStat trustStat;
+
+ private final TrustStat trustPackedRefsStat;
+
+ private final TrustStat trustLooseRefStat;
+
+ private final TrustStat trustPackStat;
+
+ private final TrustStat trustLooseObjectStat;
+
+ private final TrustStat trustTablesListStat;
+
/**
* Options for symlink handling
*
@@ -200,14 +246,17 @@ public enum HideDotFiles {
DOTGITONLY
}
- private CoreConfig(Config rc) {
+ /**
+ * Create a new core configuration from the passed configuration.
+ *
+ * @param rc
+ * git configuration
+ */
+ CoreConfig(Config rc) {
compression = rc.getInt(ConfigConstants.CONFIG_CORE_SECTION,
ConfigConstants.CONFIG_KEY_COMPRESSION, DEFAULT_COMPRESSION);
packIndexVersion = rc.getInt(ConfigConstants.CONFIG_PACK_SECTION,
ConfigConstants.CONFIG_KEY_INDEXVERSION, 2);
- logAllRefUpdates = rc.getEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
- ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES,
- LogRefUpdates.TRUE);
excludesfile = rc.getString(ConfigConstants.CONFIG_CORE_SECTION, null,
ConfigConstants.CONFIG_KEY_EXCLUDESFILE);
attributesfile = rc.getString(ConfigConstants.CONFIG_CORE_SECTION,
@@ -215,6 +264,68 @@ private CoreConfig(Config rc) {
commitGraph = rc.getBoolean(ConfigConstants.CONFIG_CORE_SECTION,
ConfigConstants.CONFIG_COMMIT_GRAPH,
DEFAULT_COMMIT_GRAPH_ENABLE);
+
+ trustStat = parseTrustStat(rc);
+ trustPackedRefsStat = parseTrustPackedRefsStat(rc);
+ trustLooseRefStat = parseTrustLooseRefStat(rc);
+ trustPackStat = parseTrustPackFileStat(rc);
+ trustLooseObjectStat = parseTrustLooseObjectFileStat(rc);
+ trustTablesListStat = parseTablesListStat(rc);
+ }
+
+ private static TrustStat parseTrustStat(Config rc) {
+ Boolean tfs = rc.getBoolean(ConfigConstants.CONFIG_CORE_SECTION,
+ ConfigConstants.CONFIG_KEY_TRUSTFOLDERSTAT);
+ TrustStat ts = rc.getEnum(TrustStat.values(),
+ ConfigConstants.CONFIG_CORE_SECTION, null,
+ ConfigConstants.CONFIG_KEY_TRUST_STAT);
+ if (tfs != null) {
+ if (ts == null) {
+ LOG.warn(JGitText.get().deprecatedTrustFolderStat);
+ return tfs.booleanValue() ? TrustStat.ALWAYS : TrustStat.NEVER;
+ }
+ LOG.warn(JGitText.get().precedenceTrustConfig);
+ }
+ if (ts == null) {
+ ts = TrustStat.ALWAYS;
+ } else if (ts == TrustStat.INHERIT) {
+ LOG.warn(JGitText.get().invalidTrustStat);
+ ts = TrustStat.ALWAYS;
+ }
+ return ts;
+ }
+
+ private TrustStat parseTrustPackedRefsStat(Config rc) {
+ return inheritParseTrustStat(rc,
+ ConfigConstants.CONFIG_KEY_TRUST_PACKED_REFS_STAT);
+ }
+
+ private TrustStat parseTrustLooseRefStat(Config rc) {
+ return inheritParseTrustStat(rc,
+ ConfigConstants.CONFIG_KEY_TRUST_LOOSE_REF_STAT);
+ }
+
+ private TrustStat parseTrustPackFileStat(Config rc) {
+ return inheritParseTrustStat(rc,
+ ConfigConstants.CONFIG_KEY_TRUST_PACK_STAT);
+ }
+
+ private TrustStat parseTrustLooseObjectFileStat(Config rc) {
+ return inheritParseTrustStat(rc,
+ ConfigConstants.CONFIG_KEY_TRUST_LOOSE_OBJECT_STAT);
+ }
+
+ private TrustStat inheritParseTrustStat(Config rc, String key) {
+ TrustStat t = rc.getEnum(ConfigConstants.CONFIG_CORE_SECTION, null, key,
+ TrustStat.INHERIT);
+ return t == TrustStat.INHERIT ? trustStat : t;
+ }
+
+ private TrustStat parseTablesListStat(Config rc) {
+ TrustStat t = rc.getEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
+ ConfigConstants.CONFIG_KEY_TRUST_TABLESLIST_STAT,
+ TrustStat.INHERIT);
+ return t == TrustStat.INHERIT ? trustStat : t;
}
/**
@@ -236,20 +347,6 @@ public int getPackIndexVersion() {
}
/**
- * Whether to log all refUpdates
- *
- * @return whether to log all refUpdates
- * @deprecated since 5.6; default value depends on whether the repository is
- * bare. Use
- * {@link Config#getEnum(String, String, String, Enum)}
- * directly.
- */
- @Deprecated
- public boolean isLogAllRefUpdates() {
- return !LogRefUpdates.FALSE.equals(logAllRefUpdates);
- }
-
- /**
* Get path of excludesfile
*
* @return path of excludesfile
@@ -279,4 +376,70 @@ public String getAttributesFile() {
public boolean enableCommitGraph() {
return commitGraph;
}
+
+ /**
+ * Get how far we can trust file attributes of packed-refs file which is
+ * used to store {@link org.eclipse.jgit.lib.Ref}s in
+ * {@link org.eclipse.jgit.internal.storage.file.RefDirectory}.
+ *
+ * @return how far we can trust file attributes of packed-refs file.
+ *
+ * @since 7.2
+ */
+ public TrustStat getTrustPackedRefsStat() {
+ return trustPackedRefsStat;
+ }
+
+ /**
+ * Get how far we can trust file attributes of loose ref files which are
+ * used to store {@link org.eclipse.jgit.lib.Ref}s in
+ * {@link org.eclipse.jgit.internal.storage.file.RefDirectory}.
+ *
+ * @return how far we can trust file attributes of loose ref files.
+ *
+ * @since 7.2
+ */
+ public TrustStat getTrustLooseRefStat() {
+ return trustLooseRefStat;
+ }
+
+ /**
+ * Get how far we can trust file attributes of packed-refs file which is
+ * used to store {@link org.eclipse.jgit.lib.Ref}s in
+ * {@link org.eclipse.jgit.internal.storage.file.RefDirectory}.
+ *
+ * @return how far we can trust file attributes of packed-refs file.
+ *
+ * @since 7.2
+ */
+ public TrustStat getTrustPackStat() {
+ return trustPackStat;
+ }
+
+ /**
+ * Get how far we can trust file attributes of loose ref files which are
+ * used to store {@link org.eclipse.jgit.lib.Ref}s in
+ * {@link org.eclipse.jgit.internal.storage.file.RefDirectory}.
+ *
+ * @return how far we can trust file attributes of loose ref files.
+ *
+ * @since 7.2
+ */
+ public TrustStat getTrustLooseObjectStat() {
+ return trustLooseObjectStat;
+ }
+
+ /**
+ * Get how far we can trust file attributes of the "tables.list" file which
+ * is used to store the list of filenames of the files storing
+ * {@link org.eclipse.jgit.internal.storage.reftable.Reftable}s in
+ * {@link org.eclipse.jgit.internal.storage.file.FileReftableDatabase}.
+ *
+ * @return how far we can trust file attributes of the "tables.list" file.
+ *
+ * @since 7.2
+ */
+ public TrustStat getTrustTablesListStat() {
+ return trustTablesListStat;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java
index a71549c..3059f28 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java
@@ -18,6 +18,7 @@
import java.util.regex.Pattern;
import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Config.ConfigEnum;
import org.eclipse.jgit.transport.RefSpec;
@@ -31,27 +32,37 @@
*/
public class DefaultTypedConfigGetter implements TypedConfigGetter {
+ @SuppressWarnings("boxed")
@Override
public boolean getBoolean(Config config, String section, String subsection,
String name, boolean defaultValue) {
+ return neverNull(getBoolean(config, section, subsection, name,
+ Boolean.valueOf(defaultValue)));
+ }
+
+ @Nullable
+ @Override
+ public Boolean getBoolean(Config config, String section, String subsection,
+ String name, @Nullable Boolean defaultValue) {
String n = config.getString(section, subsection, name);
if (n == null) {
return defaultValue;
}
if (Config.isMissing(n)) {
- return true;
+ return Boolean.TRUE;
}
try {
- return StringUtils.toBoolean(n);
+ return Boolean.valueOf(StringUtils.toBoolean(n));
} catch (IllegalArgumentException err) {
throw new IllegalArgumentException(MessageFormat.format(
JGitText.get().invalidBooleanValue, section, name, n), err);
}
}
+ @Nullable
@Override
public <T extends Enum<?>> T getEnum(Config config, T[] all, String section,
- String subsection, String name, T defaultValue) {
+ String subsection, String name, @Nullable T defaultValue) {
String value = config.getString(section, subsection, name);
if (value == null) {
return defaultValue;
@@ -107,9 +118,27 @@ public <T extends Enum<?>> T getEnum(Config config, T[] all, String section,
@Override
public int getInt(Config config, String section, String subsection,
String name, int defaultValue) {
- long val = config.getLong(section, subsection, name, defaultValue);
+ return neverNull(getInt(config, section, subsection, name,
+ Integer.valueOf(defaultValue)));
+ }
+
+ @Nullable
+ @Override
+ @SuppressWarnings("boxing")
+ public Integer getInt(Config config, String section, String subsection,
+ String name, @Nullable Integer defaultValue) {
+ Long longDefault = defaultValue != null
+ ? Long.valueOf(defaultValue.longValue())
+ : null;
+ Long val = config.getLong(section, subsection, name);
+ if (val == null) {
+ val = longDefault;
+ }
+ if (val == null) {
+ return null;
+ }
if (Integer.MIN_VALUE <= val && val <= Integer.MAX_VALUE) {
- return (int) val;
+ return Integer.valueOf(Math.toIntExact(val));
}
throw new IllegalArgumentException(MessageFormat
.format(JGitText.get().integerValueOutOfRange, section, name));
@@ -118,37 +147,56 @@ public int getInt(Config config, String section, String subsection,
@Override
public int getIntInRange(Config config, String section, String subsection,
String name, int minValue, int maxValue, int defaultValue) {
- int val = getInt(config, section, subsection, name, defaultValue);
+ return neverNull(getIntInRange(config, section, subsection, name,
+ minValue, maxValue, Integer.valueOf(defaultValue)));
+ }
+
+ @Override
+ @SuppressWarnings("boxing")
+ public Integer getIntInRange(Config config, String section,
+ String subsection, String name, int minValue, int maxValue,
+ Integer defaultValue) {
+ Integer val = getInt(config, section, subsection, name, defaultValue);
+ if (val == null) {
+ return null;
+ }
if ((val >= minValue && val <= maxValue) || val == UNSET_INT) {
return val;
}
if (subsection == null) {
- throw new IllegalArgumentException(MessageFormat.format(
- JGitText.get().integerValueNotInRange, section, name,
- Integer.valueOf(val), Integer.valueOf(minValue),
- Integer.valueOf(maxValue)));
+ throw new IllegalArgumentException(
+ MessageFormat.format(JGitText.get().integerValueNotInRange,
+ section, name, val, minValue, maxValue));
}
throw new IllegalArgumentException(MessageFormat.format(
JGitText.get().integerValueNotInRangeSubSection, section,
- subsection, name, Integer.valueOf(val),
- Integer.valueOf(minValue), Integer.valueOf(maxValue)));
+ subsection, name, val, minValue, maxValue));
}
@Override
public long getLong(Config config, String section, String subsection,
String name, long defaultValue) {
- final String str = config.getString(section, subsection, name);
+ return neverNull(getLong(config, section, subsection, name,
+ Long.valueOf(defaultValue)));
+ }
+
+ @Nullable
+ @Override
+ public Long getLong(Config config, String section, String subsection,
+ String name, @Nullable Long defaultValue) {
+ String str = config.getString(section, subsection, name);
if (str == null) {
return defaultValue;
}
try {
- return StringUtils.parseLongWithSuffix(str, false);
+ return Long.valueOf(StringUtils.parseLongWithSuffix(str, false));
} catch (StringIndexOutOfBoundsException e) {
// Empty
return defaultValue;
} catch (NumberFormatException nfe) {
- throw new IllegalArgumentException(MessageFormat.format(
- JGitText.get().invalidIntegerValue, section, name, str),
+ throw new IllegalArgumentException(
+ MessageFormat.format(JGitText.get().invalidIntegerValue,
+ section, name, str),
nfe);
}
}
@@ -156,6 +204,13 @@ public long getLong(Config config, String section, String subsection,
@Override
public long getTimeUnit(Config config, String section, String subsection,
String name, long defaultValue, TimeUnit wantUnit) {
+ return neverNull(getTimeUnit(config, section, subsection, name,
+ Long.valueOf(defaultValue), wantUnit));
+ }
+
+ @Override
+ public Long getTimeUnit(Config config, String section, String subsection,
+ String name, @Nullable Long defaultValue, TimeUnit wantUnit) {
String valueString = config.getString(section, subsection, name);
if (valueString == null) {
@@ -232,8 +287,8 @@ public long getTimeUnit(Config config, String section, String subsection,
}
try {
- return wantUnit.convert(Long.parseLong(digits) * inputMul,
- inputUnit);
+ return Long.valueOf(wantUnit
+ .convert(Long.parseLong(digits) * inputMul, inputUnit));
} catch (NumberFormatException nfe) {
IllegalArgumentException iae = notTimeUnit(section, subsection,
unitName, valueString);
@@ -274,4 +329,14 @@ public List<RefSpec> getRefSpecs(Config config, String section,
}
return result;
}
+
+ // Trick for the checkers. When we use this, one is never null, but
+ // they don't know.
+ @NonNull
+ private static <T> T neverNull(T one) {
+ if (one == null) {
+ throw new IllegalArgumentException();
+ }
+ return one;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgConfig.java
index 427a235..23d16db 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgConfig.java
@@ -24,7 +24,13 @@ public enum GpgFormat implements Config.ConfigEnum {
/** Value for openpgp */
OPENPGP("openpgp"), //$NON-NLS-1$
/** Value for x509 */
- X509("x509"); //$NON-NLS-1$
+ X509("x509"), //$NON-NLS-1$
+ /**
+ * Value for ssh.
+ *
+ * @since 7.0
+ */
+ SSH("ssh"); //$NON-NLS-1$
private final String configValue;
@@ -55,26 +61,11 @@ public String toConfigValue() {
private final boolean forceAnnotated;
- /**
- * Create a {@link GpgConfig} with the given parameters and default
- * {@code true} for signing commits and {@code false} for tags.
- *
- * @param keySpec
- * to use
- * @param format
- * to use
- * @param gpgProgram
- * to use
- * @since 5.11
- */
- public GpgConfig(String keySpec, GpgFormat format, String gpgProgram) {
- keyFormat = format;
- signingKey = keySpec;
- program = gpgProgram;
- signCommits = true;
- signAllTags = false;
- forceAnnotated = false;
- }
+ private final String sshDefaultKeyCommand;
+
+ private final String sshAllowedSignersFile;
+
+ private final String sshRevocationFile;
/**
* Create a new GPG config that reads the configuration from config.
@@ -83,18 +74,18 @@ public GpgConfig(String keySpec, GpgFormat format, String gpgProgram) {
* the config to read from
*/
public GpgConfig(Config config) {
- keyFormat = config.getEnum(GpgFormat.values(),
- ConfigConstants.CONFIG_GPG_SECTION, null,
+ keyFormat = config.getEnum(ConfigConstants.CONFIG_GPG_SECTION, null,
ConfigConstants.CONFIG_KEY_FORMAT, GpgFormat.OPENPGP);
signingKey = config.getString(ConfigConstants.CONFIG_USER_SECTION, null,
ConfigConstants.CONFIG_KEY_SIGNINGKEY);
String exe = config.getString(ConfigConstants.CONFIG_GPG_SECTION,
keyFormat.toConfigValue(), ConfigConstants.CONFIG_KEY_PROGRAM);
- if (exe == null) {
+ if (exe == null && GpgFormat.OPENPGP.equals(keyFormat)) {
exe = config.getString(ConfigConstants.CONFIG_GPG_SECTION, null,
ConfigConstants.CONFIG_KEY_PROGRAM);
}
+
program = exe;
signCommits = config.getBoolean(ConfigConstants.CONFIG_COMMIT_SECTION,
ConfigConstants.CONFIG_KEY_GPGSIGN, false);
@@ -102,6 +93,17 @@ public GpgConfig(Config config) {
ConfigConstants.CONFIG_KEY_GPGSIGN, false);
forceAnnotated = config.getBoolean(ConfigConstants.CONFIG_TAG_SECTION,
ConfigConstants.CONFIG_KEY_FORCE_SIGN_ANNOTATED, false);
+ sshDefaultKeyCommand = config.getString(
+ ConfigConstants.CONFIG_GPG_SECTION,
+ ConfigConstants.CONFIG_SSH_SUBSECTION,
+ ConfigConstants.CONFIG_KEY_SSH_DEFAULT_KEY_COMMAND);
+ sshAllowedSignersFile = config.getString(
+ ConfigConstants.CONFIG_GPG_SECTION,
+ ConfigConstants.CONFIG_SSH_SUBSECTION,
+ ConfigConstants.CONFIG_KEY_SSH_ALLOWED_SIGNERS_FILE);
+ sshRevocationFile = config.getString(ConfigConstants.CONFIG_GPG_SECTION,
+ ConfigConstants.CONFIG_SSH_SUBSECTION,
+ ConfigConstants.CONFIG_KEY_SSH_REVOCATION_FILE);
}
/**
@@ -165,4 +167,37 @@ public boolean isSignAllTags() {
public boolean isSignAnnotated() {
return forceAnnotated;
}
+
+ /**
+ * Retrieves the value of git config {@code gpg.ssh.defaultKeyCommand}.
+ *
+ * @return the value of {@code gpg.ssh.defaultKeyCommand}
+ *
+ * @since 7.1
+ */
+ public String getSshDefaultKeyCommand() {
+ return sshDefaultKeyCommand;
+ }
+
+ /**
+ * Retrieves the value of git config {@code gpg.ssh.allowedSignersFile}.
+ *
+ * @return the value of {@code gpg.ssh.allowedSignersFile}
+ *
+ * @since 7.1
+ */
+ public String getSshAllowedSignersFile() {
+ return sshAllowedSignersFile;
+ }
+
+ /**
+ * Retrieves the value of git config {@code gpg.ssh.revocationFile}.
+ *
+ * @return the value of {@code gpg.ssh.revocationFile}
+ *
+ * @since 7.1
+ */
+ public String getSshRevocationFile() {
+ return sshRevocationFile;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgObjectSigner.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgObjectSigner.java
deleted file mode 100644
index 074f465..0000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgObjectSigner.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2020 Thomas Wolf <thomas.wolf@paranor.ch> and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-package org.eclipse.jgit.lib;
-
-import org.eclipse.jgit.annotations.NonNull;
-import org.eclipse.jgit.annotations.Nullable;
-import org.eclipse.jgit.api.errors.CanceledException;
-import org.eclipse.jgit.api.errors.UnsupportedSigningFormatException;
-import org.eclipse.jgit.transport.CredentialsProvider;
-
-/**
- * Creates GPG signatures for Git objects.
- *
- * @since 5.11
- */
-public interface GpgObjectSigner {
-
- /**
- * Signs the specified object.
- *
- * <p>
- * Implementors should obtain the payload for signing from the specified
- * object via {@link ObjectBuilder#build()} and create a proper
- * {@link GpgSignature}. The generated signature must be set on the
- * specified {@code object} (see
- * {@link ObjectBuilder#setGpgSignature(GpgSignature)}).
- * </p>
- * <p>
- * Any existing signature on the object must be discarded prior obtaining
- * the payload via {@link ObjectBuilder#build()}.
- * </p>
- *
- * @param object
- * the object to sign (must not be {@code null} and must be
- * complete to allow proper calculation of payload)
- * @param gpgSigningKey
- * the signing key to locate (passed as is to the GPG signing
- * tool as is; eg., value of <code>user.signingkey</code>)
- * @param committer
- * the signing identity (to help with key lookup in case signing
- * key is not specified)
- * @param credentialsProvider
- * provider to use when querying for signing key credentials (eg.
- * passphrase)
- * @param config
- * GPG settings from the git config
- * @throws CanceledException
- * when signing was canceled (eg., user aborted when entering
- * passphrase)
- * @throws UnsupportedSigningFormatException
- * if a config is given and the wanted key format is not
- * supported
- */
- void signObject(@NonNull ObjectBuilder object,
- @Nullable String gpgSigningKey, @NonNull PersonIdent committer,
- CredentialsProvider credentialsProvider, GpgConfig config)
- throws CanceledException, UnsupportedSigningFormatException;
-
- /**
- * Indicates if a signing key is available for the specified committer
- * and/or signing key.
- *
- * @param gpgSigningKey
- * the signing key to locate (passed as is to the GPG signing
- * tool as is; eg., value of <code>user.signingkey</code>)
- * @param committer
- * the signing identity (to help with key lookup in case signing
- * key is not specified)
- * @param credentialsProvider
- * provider to use when querying for signing key credentials (eg.
- * passphrase)
- * @param config
- * GPG settings from the git config
- * @return <code>true</code> if a signing key is available,
- * <code>false</code> otherwise
- * @throws CanceledException
- * when signing was canceled (eg., user aborted when entering
- * passphrase)
- * @throws UnsupportedSigningFormatException
- * if a config is given and the wanted key format is not
- * supported
- */
- public abstract boolean canLocateSigningKey(@Nullable String gpgSigningKey,
- @NonNull PersonIdent committer,
- CredentialsProvider credentialsProvider, GpgConfig config)
- throws CanceledException, UnsupportedSigningFormatException;
-
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSignatureVerifier.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSignatureVerifier.java
deleted file mode 100644
index 91c9bab..0000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSignatureVerifier.java
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Copyright (C) 2021, 2024 Thomas Wolf <twolf@apache.org> and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-package org.eclipse.jgit.lib;
-
-import java.io.IOException;
-import java.util.Date;
-
-import org.eclipse.jgit.annotations.NonNull;
-import org.eclipse.jgit.annotations.Nullable;
-import org.eclipse.jgit.api.errors.JGitInternalException;
-import org.eclipse.jgit.revwalk.RevObject;
-
-/**
- * A {@code GpgSignatureVerifier} can verify GPG signatures on git commits and
- * tags.
- *
- * @since 5.11
- */
-public interface GpgSignatureVerifier {
-
- /**
- * Verifies the signature on a signed commit or tag.
- *
- * @param object
- * to verify
- * @param config
- * the {@link GpgConfig} to use
- * @return a {@link SignatureVerification} describing the outcome of the
- * verification, or {@code null} if the object was not signed
- * @throws IOException
- * if an error occurs getting a public key
- * @throws org.eclipse.jgit.api.errors.JGitInternalException
- * if signature verification fails
- */
- @Nullable
- SignatureVerification verifySignature(@NonNull RevObject object,
- @NonNull GpgConfig config) throws IOException;
-
- /**
- * Verifies a given signature for given data.
- *
- * @param config
- * the {@link GpgConfig}
- * @param data
- * the signature is for
- * @param signatureData
- * the ASCII-armored signature
- * @return a {@link SignatureVerification} describing the outcome
- * @throws IOException
- * if the signature cannot be parsed
- * @throws JGitInternalException
- * if signature verification fails
- * @since 6.9
- */
- default SignatureVerification verify(@NonNull GpgConfig config, byte[] data,
- byte[] signatureData) throws IOException {
- // Default implementation for backwards compatibility; override as
- // appropriate
- return verify(data, signatureData);
- }
-
- /**
- * Verifies a given signature for given data.
- *
- * @param data
- * the signature is for
- * @param signatureData
- * the ASCII-armored signature
- * @return a {@link SignatureVerification} describing the outcome
- * @throws IOException
- * if the signature cannot be parsed
- * @throws JGitInternalException
- * if signature verification fails
- * @deprecated since 6.9, use {@link #verify(GpgConfig, byte[], byte[])}
- * instead
- */
- @Deprecated
- public SignatureVerification verify(byte[] data, byte[] signatureData)
- throws IOException;
-
- /**
- * Retrieves the name of this verifier. This should be a short string
- * identifying the engine that verified the signature, like "gpg" if GPG is
- * used, or "bc" for a BouncyCastle implementation.
- *
- * @return the name
- */
- @NonNull
- String getName();
-
- /**
- * A {@link GpgSignatureVerifier} may cache public keys to speed up
- * verifying signatures on multiple objects. This clears this cache, if any.
- */
- void clear();
-
- /**
- * A {@code SignatureVerification} returns data about a (positively or
- * negatively) verified signature.
- */
- interface SignatureVerification {
-
- // Data about the signature.
-
- @NonNull
- Date getCreationDate();
-
- // Data from the signature used to find a public key.
-
- /**
- * Obtains the signer as stored in the signature, if known.
- *
- * @return the signer, or {@code null} if unknown
- */
- String getSigner();
-
- /**
- * Obtains the short or long fingerprint of the public key as stored in
- * the signature, if known.
- *
- * @return the fingerprint, or {@code null} if unknown
- */
- String getKeyFingerprint();
-
- // Some information about the found public key.
-
- /**
- * Obtains the OpenPGP user ID associated with the key.
- *
- * @return the user id, or {@code null} if unknown
- */
- String getKeyUser();
-
- /**
- * Tells whether the public key used for this signature verification was
- * expired when the signature was created.
- *
- * @return {@code true} if the key was expired already, {@code false}
- * otherwise
- */
- boolean isExpired();
-
- /**
- * Obtains the trust level of the public key used to verify the
- * signature.
- *
- * @return the trust level
- */
- @NonNull
- TrustLevel getTrustLevel();
-
- // The verification result.
-
- /**
- * Tells whether the signature verification was successful.
- *
- * @return {@code true} if the signature was verified successfully;
- * {@code false} if not.
- */
- boolean getVerified();
-
- /**
- * Obtains a human-readable message giving additional information about
- * the outcome of the verification.
- *
- * @return the message, or {@code null} if none set.
- */
- String getMessage();
- }
-
- /**
- * The owner's trust in a public key.
- */
- enum TrustLevel {
- UNKNOWN, NEVER, MARGINAL, FULL, ULTIMATE
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSignatureVerifierFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSignatureVerifierFactory.java
deleted file mode 100644
index 59775c4..0000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSignatureVerifierFactory.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2021, 2022 Thomas Wolf <thomas.wolf@paranor.ch> and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-package org.eclipse.jgit.lib;
-
-import java.util.Iterator;
-import java.util.ServiceConfigurationError;
-import java.util.ServiceLoader;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * A {@code GpgSignatureVerifierFactory} creates {@link GpgSignatureVerifier} instances.
- *
- * @since 5.11
- */
-public abstract class GpgSignatureVerifierFactory {
-
- private static final Logger LOG = LoggerFactory
- .getLogger(GpgSignatureVerifierFactory.class);
-
- private static class DefaultFactory {
-
- private static volatile GpgSignatureVerifierFactory defaultFactory = loadDefault();
-
- private static GpgSignatureVerifierFactory loadDefault() {
- try {
- ServiceLoader<GpgSignatureVerifierFactory> loader = ServiceLoader
- .load(GpgSignatureVerifierFactory.class);
- Iterator<GpgSignatureVerifierFactory> iter = loader.iterator();
- if (iter.hasNext()) {
- return iter.next();
- }
- } catch (ServiceConfigurationError e) {
- LOG.error(e.getMessage(), e);
- }
- return null;
- }
-
- private DefaultFactory() {
- // No instantiation
- }
-
- public static GpgSignatureVerifierFactory getDefault() {
- return defaultFactory;
- }
-
- /**
- * Sets the default factory.
- *
- * @param factory
- * the new default factory
- */
- public static void setDefault(GpgSignatureVerifierFactory factory) {
- defaultFactory = factory;
- }
- }
-
- /**
- * Retrieves the default factory.
- *
- * @return the default factory or {@code null} if none set
- */
- public static GpgSignatureVerifierFactory getDefault() {
- return DefaultFactory.getDefault();
- }
-
- /**
- * Sets the default factory.
- *
- * @param factory
- * the new default factory
- */
- public static void setDefault(GpgSignatureVerifierFactory factory) {
- DefaultFactory.setDefault(factory);
- }
-
- /**
- * Creates a new {@link GpgSignatureVerifier}.
- *
- * @return the new {@link GpgSignatureVerifier}
- */
- public abstract GpgSignatureVerifier getVerifier();
-
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSigner.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSigner.java
deleted file mode 100644
index b25a61b..0000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSigner.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (C) 2018, 2022 Salesforce and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-package org.eclipse.jgit.lib;
-
-import java.util.Iterator;
-import java.util.ServiceConfigurationError;
-import java.util.ServiceLoader;
-
-import org.eclipse.jgit.annotations.NonNull;
-import org.eclipse.jgit.annotations.Nullable;
-import org.eclipse.jgit.api.errors.CanceledException;
-import org.eclipse.jgit.transport.CredentialsProvider;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Creates GPG signatures for Git objects.
- *
- * @since 5.3
- */
-public abstract class GpgSigner {
-
- private static final Logger LOG = LoggerFactory.getLogger(GpgSigner.class);
-
- private static class DefaultSigner {
-
- private static volatile GpgSigner defaultSigner = loadGpgSigner();
-
- private static GpgSigner loadGpgSigner() {
- try {
- ServiceLoader<GpgSigner> loader = ServiceLoader
- .load(GpgSigner.class);
- Iterator<GpgSigner> iter = loader.iterator();
- if (iter.hasNext()) {
- return iter.next();
- }
- } catch (ServiceConfigurationError e) {
- LOG.error(e.getMessage(), e);
- }
- return null;
- }
-
- private DefaultSigner() {
- // No instantiation
- }
-
- public static GpgSigner getDefault() {
- return defaultSigner;
- }
-
- public static void setDefault(GpgSigner signer) {
- defaultSigner = signer;
- }
- }
-
- /**
- * Get the default signer, or <code>null</code>.
- *
- * @return the default signer, or <code>null</code>.
- */
- public static GpgSigner getDefault() {
- return DefaultSigner.getDefault();
- }
-
- /**
- * Set the default signer.
- *
- * @param signer
- * the new default signer, may be <code>null</code> to select no
- * default.
- */
- public static void setDefault(GpgSigner signer) {
- DefaultSigner.setDefault(signer);
- }
-
- /**
- * Signs the specified commit.
- *
- * <p>
- * Implementors should obtain the payload for signing from the specified
- * commit via {@link CommitBuilder#build()} and create a proper
- * {@link GpgSignature}. The generated signature must be set on the
- * specified {@code commit} (see
- * {@link CommitBuilder#setGpgSignature(GpgSignature)}).
- * </p>
- * <p>
- * Any existing signature on the commit must be discarded prior obtaining
- * the payload via {@link CommitBuilder#build()}.
- * </p>
- *
- * @param commit
- * the commit to sign (must not be <code>null</code> and must be
- * complete to allow proper calculation of payload)
- * @param gpgSigningKey
- * the signing key to locate (passed as is to the GPG signing
- * tool as is; eg., value of <code>user.signingkey</code>)
- * @param committer
- * the signing identity (to help with key lookup in case signing
- * key is not specified)
- * @param credentialsProvider
- * provider to use when querying for signing key credentials (eg.
- * passphrase)
- * @throws CanceledException
- * when signing was canceled (eg., user aborted when entering
- * passphrase)
- */
- public abstract void sign(@NonNull CommitBuilder commit,
- @Nullable String gpgSigningKey, @NonNull PersonIdent committer,
- CredentialsProvider credentialsProvider) throws CanceledException;
-
- /**
- * Indicates if a signing key is available for the specified committer
- * and/or signing key.
- *
- * @param gpgSigningKey
- * the signing key to locate (passed as is to the GPG signing
- * tool as is; eg., value of <code>user.signingkey</code>)
- * @param committer
- * the signing identity (to help with key lookup in case signing
- * key is not specified)
- * @param credentialsProvider
- * provider to use when querying for signing key credentials (eg.
- * passphrase)
- * @return <code>true</code> if a signing key is available,
- * <code>false</code> otherwise
- * @throws CanceledException
- * when signing was canceled (eg., user aborted when entering
- * passphrase)
- */
- public abstract boolean canLocateSigningKey(@Nullable String gpgSigningKey,
- @NonNull PersonIdent committer,
- CredentialsProvider credentialsProvider) throws CanceledException;
-
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java
index 8e965c5..a99c647 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java
@@ -639,7 +639,7 @@ public boolean diff(ProgressMonitor monitor, int estWorkTreeSize,
// submodule repository in .git/modules doesn't
// exist yet it isn't "missing".
File gitDir = new File(
- new File(repository.getDirectory(),
+ new File(repository.getCommonDirectory(),
Constants.MODULES),
subRepoPath);
if (!gitDir.isDirectory()) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectId.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectId.java
index 1c31263..1b455b9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectId.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectId.java
@@ -15,6 +15,7 @@
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
+import java.nio.ByteBuffer;
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.errors.InvalidObjectIdException;
@@ -152,6 +153,22 @@ public static final ObjectId fromRaw(byte[] bs, int p) {
}
/**
+ * Convert an ObjectId from raw binary representation
+ *
+ * @param bb
+ * a bytebuffer with the objectid encoded as 5 consecutive ints.
+ * This is the reverse of {@link ObjectId#copyRawTo(ByteBuffer)}
+ *
+ * @return the converted object id.
+ *
+ * @since 7.0
+ */
+ public static final ObjectId fromRaw(ByteBuffer bb) {
+ return new ObjectId(bb.getInt(), bb.getInt(), bb.getInt(), bb.getInt(),
+ bb.getInt());
+ }
+
+ /**
* Convert an ObjectId from raw binary representation.
*
* @param is
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java
index 3ba055a..50f4a83 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java
@@ -12,10 +12,15 @@
package org.eclipse.jgit.lib;
+import static java.time.ZoneOffset.UTC;
+
import java.io.Serializable;
-import java.text.SimpleDateFormat;
+import java.time.DateTimeException;
import java.time.Instant;
import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
@@ -40,7 +45,9 @@ public class PersonIdent implements Serializable {
* timezone offset as in {@link #getTimeZoneOffset()}.
* @return time zone object for the given offset.
* @since 4.1
+ * @deprecated use {@link #getZoneId(int)} instead
*/
+ @Deprecated(since = "7.2")
public static TimeZone getTimeZone(int tzOffset) {
StringBuilder tzId = new StringBuilder(8);
tzId.append("GMT"); //$NON-NLS-1$
@@ -49,6 +56,21 @@ public static TimeZone getTimeZone(int tzOffset) {
}
/**
+ * Translate a minutes offset into a ZoneId
+ *
+ * @param tzOffset as minutes east of UTC
+ * @return a ZoneId for this offset (UTC if invalid)
+ * @since 7.1
+ */
+ public static ZoneId getZoneId(int tzOffset) {
+ try {
+ return ZoneOffset.ofHoursMinutes(tzOffset / 60, tzOffset % 60);
+ } catch (DateTimeException e) {
+ return UTC;
+ }
+ }
+
+ /**
* Format a timezone offset.
*
* @param r
@@ -121,13 +143,17 @@ public static void appendSanitized(StringBuilder r, String str) {
}
}
+ // Write offsets as [+-]HHMM
+ private static final DateTimeFormatter OFFSET_FORMATTER = DateTimeFormatter
+ .ofPattern("Z", Locale.US); //$NON-NLS-1$
+
private final String name;
private final String emailAddress;
- private final long when;
+ private final Instant when;
- private final int tzOffset;
+ private final ZoneId tzOffset;
/**
* Creates new PersonIdent from config info in repository, with current time.
@@ -160,7 +186,7 @@ public PersonIdent(PersonIdent pi) {
* a {@link java.lang.String} object.
*/
public PersonIdent(String aName, String aEmailAddress) {
- this(aName, aEmailAddress, SystemReader.getInstance().getCurrentTime());
+ this(aName, aEmailAddress, SystemReader.getInstance().now());
}
/**
@@ -177,7 +203,7 @@ public PersonIdent(String aName, String aEmailAddress) {
*/
public PersonIdent(String aName, String aEmailAddress,
ProposedTimestamp when) {
- this(aName, aEmailAddress, when.millis());
+ this(aName, aEmailAddress, when.instant());
}
/**
@@ -189,8 +215,25 @@ public PersonIdent(String aName, String aEmailAddress,
* local time
* @param tz
* time zone
+ * @deprecated Use {@link #PersonIdent(PersonIdent, Instant, ZoneId)} instead.
*/
+ @Deprecated(since = "7.1")
public PersonIdent(PersonIdent pi, Date when, TimeZone tz) {
+ this(pi.getName(), pi.getEmailAddress(), when.toInstant(), tz.toZoneId());
+ }
+
+ /**
+ * Copy a PersonIdent, but alter the clone's time stamp
+ *
+ * @param pi
+ * original {@link org.eclipse.jgit.lib.PersonIdent}
+ * @param when
+ * local time
+ * @param tz
+ * time zone offset
+ * @since 7.1
+ */
+ public PersonIdent(PersonIdent pi, Instant when, ZoneId tz) {
this(pi.getName(), pi.getEmailAddress(), when, tz);
}
@@ -202,9 +245,12 @@ public PersonIdent(PersonIdent pi, Date when, TimeZone tz) {
* original {@link org.eclipse.jgit.lib.PersonIdent}
* @param aWhen
* local time
+ * @deprecated Use the variant with an Instant instead
*/
+ @Deprecated(since = "7.1")
public PersonIdent(PersonIdent pi, Date aWhen) {
- this(pi.getName(), pi.getEmailAddress(), aWhen.getTime(), pi.tzOffset);
+ this(pi.getName(), pi.getEmailAddress(), aWhen.toInstant(),
+ pi.tzOffset);
}
/**
@@ -218,7 +264,7 @@ public PersonIdent(PersonIdent pi, Date aWhen) {
* @since 6.1
*/
public PersonIdent(PersonIdent pi, Instant aWhen) {
- this(pi.getName(), pi.getEmailAddress(), aWhen.toEpochMilli(), pi.tzOffset);
+ this(pi.getName(), pi.getEmailAddress(), aWhen, pi.tzOffset);
}
/**
@@ -230,11 +276,12 @@ public PersonIdent(PersonIdent pi, Instant aWhen) {
* local time stamp
* @param aTZ
* time zone
+ * @deprecated Use the variant with Instant and ZoneId instead
*/
+ @Deprecated(since = "7.1")
public PersonIdent(final String aName, final String aEmailAddress,
final Date aWhen, final TimeZone aTZ) {
- this(aName, aEmailAddress, aWhen.getTime(), aTZ.getOffset(aWhen
- .getTime()) / (60 * 1000));
+ this(aName, aEmailAddress, aWhen.toInstant(), aTZ.toZoneId());
}
/**
@@ -252,10 +299,16 @@ public PersonIdent(final String aName, final String aEmailAddress,
*/
public PersonIdent(final String aName, String aEmailAddress, Instant aWhen,
ZoneId zoneId) {
- this(aName, aEmailAddress, aWhen.toEpochMilli(),
- TimeZone.getTimeZone(zoneId)
- .getOffset(aWhen
- .toEpochMilli()) / (60 * 1000));
+ if (aName == null)
+ throw new IllegalArgumentException(
+ JGitText.get().personIdentNameNonNull);
+ if (aEmailAddress == null)
+ throw new IllegalArgumentException(
+ JGitText.get().personIdentEmailNonNull);
+ name = aName;
+ emailAddress = aEmailAddress;
+ when = aWhen;
+ tzOffset = zoneId;
}
/**
@@ -267,15 +320,18 @@ public PersonIdent(final String aName, String aEmailAddress, Instant aWhen,
* local time stamp
* @param aTZ
* time zone
+ * @deprecated Use the variant with Instant and ZoneId instead
*/
+ @Deprecated(since = "7.1")
public PersonIdent(PersonIdent pi, long aWhen, int aTZ) {
- this(pi.getName(), pi.getEmailAddress(), aWhen, aTZ);
+ this(pi.getName(), pi.getEmailAddress(), Instant.ofEpochMilli(aWhen),
+ getZoneId(aTZ));
}
private PersonIdent(final String aName, final String aEmailAddress,
- long when) {
+ Instant when) {
this(aName, aEmailAddress, when, SystemReader.getInstance()
- .getTimezone(when));
+ .getTimeZoneAt(when));
}
private PersonIdent(UserConfig config) {
@@ -298,19 +354,12 @@ private PersonIdent(UserConfig config) {
* local time stamp
* @param aTZ
* time zone
+ * @deprecated Use the variant with Instant and ZoneId instead
*/
+ @Deprecated(since = "7.1")
public PersonIdent(final String aName, final String aEmailAddress,
final long aWhen, final int aTZ) {
- if (aName == null)
- throw new IllegalArgumentException(
- JGitText.get().personIdentNameNonNull);
- if (aEmailAddress == null)
- throw new IllegalArgumentException(
- JGitText.get().personIdentEmailNonNull);
- name = aName;
- emailAddress = aEmailAddress;
- when = aWhen;
- tzOffset = aTZ;
+ this(aName, aEmailAddress, Instant.ofEpochMilli(aWhen), getZoneId(aTZ));
}
/**
@@ -335,9 +384,12 @@ public String getEmailAddress() {
* Get timestamp
*
* @return timestamp
+ *
+ * @deprecated Use getWhenAsInstant instead
*/
+ @Deprecated(since = "7.1")
public Date getWhen() {
- return new Date(when);
+ return Date.from(when);
}
/**
@@ -347,16 +399,19 @@ public Date getWhen() {
* @since 6.1
*/
public Instant getWhenAsInstant() {
- return Instant.ofEpochMilli(when);
+ return when;
}
/**
* Get this person's declared time zone
*
* @return this person's declared time zone; null if time zone is unknown.
+ *
+ * @deprecated Use getZoneId instead
*/
+ @Deprecated(since = "7.1")
public TimeZone getTimeZone() {
- return getTimeZone(tzOffset);
+ return TimeZone.getTimeZone(tzOffset);
}
/**
@@ -366,7 +421,17 @@ public TimeZone getTimeZone() {
* @since 6.1
*/
public ZoneId getZoneId() {
- return getTimeZone().toZoneId();
+ return tzOffset;
+ }
+
+ /**
+ * Return the offset in this timezone at the specific time
+ *
+ * @return the offset
+ * @since 7.1
+ */
+ public ZoneOffset getZoneOffset() {
+ return tzOffset.getRules().getOffset(when);
}
/**
@@ -374,9 +439,11 @@ public ZoneId getZoneId() {
*
* @return this person's declared time zone as minutes east of UTC. If the
* timezone is to the west of UTC it is negative.
+ * @deprecated Use {@link #getZoneOffset()} and read minutes from there
*/
+ @Deprecated(since = "7.1")
public int getTimeZoneOffset() {
- return tzOffset;
+ return getZoneOffset().getTotalSeconds() / 60;
}
/**
@@ -388,7 +455,7 @@ public int getTimeZoneOffset() {
public int hashCode() {
int hc = getEmailAddress().hashCode();
hc *= 31;
- hc += (int) (when / 1000L);
+ hc += when.hashCode();
return hc;
}
@@ -398,7 +465,9 @@ public boolean equals(Object o) {
final PersonIdent p = (PersonIdent) o;
return getName().equals(p.getName())
&& getEmailAddress().equals(p.getEmailAddress())
- && when / 1000L == p.when / 1000L;
+ // commmit timestamps are stored with 1 second precision
+ && when.truncatedTo(ChronoUnit.SECONDS)
+ .equals(p.when.truncatedTo(ChronoUnit.SECONDS));
}
return false;
}
@@ -414,9 +483,9 @@ public String toExternalString() {
r.append(" <"); //$NON-NLS-1$
appendSanitized(r, getEmailAddress());
r.append("> "); //$NON-NLS-1$
- r.append(when / 1000);
+ r.append(when.toEpochMilli() / 1000);
r.append(' ');
- appendTimezone(r, tzOffset);
+ r.append(OFFSET_FORMATTER.format(getZoneOffset()));
return r.toString();
}
@@ -424,19 +493,16 @@ public String toExternalString() {
@SuppressWarnings("nls")
public String toString() {
final StringBuilder r = new StringBuilder();
- final SimpleDateFormat dtfmt;
- dtfmt = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy Z", Locale.US);
- dtfmt.setTimeZone(getTimeZone());
-
+ DateTimeFormatter dtfmt = DateTimeFormatter
+ .ofPattern("EEE MMM d HH:mm:ss yyyy Z", Locale.US) //$NON-NLS-1$
+ .withZone(tzOffset);
r.append("PersonIdent[");
r.append(getName());
r.append(", ");
r.append(getEmailAddress());
r.append(", ");
- r.append(dtfmt.format(Long.valueOf(when)));
+ r.append(dtfmt.format(when));
r.append("]");
-
return r.toString();
}
}
-
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
index 9e05a39..49d5224 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
@@ -26,6 +26,7 @@
import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.api.PackRefsCommand;
/**
* Abstraction of name to {@link org.eclipse.jgit.lib.ObjectId} mapping.
@@ -160,7 +161,7 @@ public Collection<String> getConflictingNames(String name)
if (existing.startsWith(prefix))
conflicting.add(existing);
- return conflicting;
+ return Collections.unmodifiableList(conflicting);
}
/**
@@ -238,23 +239,6 @@ public boolean performsAtomicTransactions() {
}
/**
- * Compatibility synonym for {@link #findRef(String)}.
- *
- * @param name
- * the name of the reference. May be a short name which must be
- * searched for using the standard {@link #SEARCH_PATH}.
- * @return the reference (if it exists); else {@code null}.
- * @throws IOException
- * the reference space cannot be accessed.
- * @deprecated Use {@link #findRef(String)} instead.
- */
- @Deprecated
- @Nullable
- public final Ref getRef(String name) throws IOException {
- return findRef(name);
- }
-
- /**
* Read a single reference.
* <p>
* Aside from taking advantage of {@link #SEARCH_PATH}, this method may be
@@ -372,6 +356,40 @@ public List<Ref> getRefs() throws IOException {
}
/**
+ * Get the reflog reader
+ *
+ * @param refName
+ * a {@link java.lang.String} object.
+ * @return a {@link org.eclipse.jgit.lib.ReflogReader} for the supplied
+ * refname, or {@code null} if the named ref does not exist.
+ * @throws java.io.IOException
+ * the ref could not be accessed.
+ * @since 7.2
+ */
+ @Nullable
+ public ReflogReader getReflogReader(String refName) throws IOException {
+ Ref ref = exactRef(refName);
+ if (ref == null) {
+ return null;
+ }
+ return getReflogReader(ref);
+ }
+
+ /**
+ * Get the reflog reader.
+ *
+ * @param ref
+ * a Ref
+ * @return a {@link org.eclipse.jgit.lib.ReflogReader} for the supplied ref.
+ * @throws IOException
+ * if an IO error occurred
+ * @since 7.2
+ */
+ @NonNull
+ public abstract ReflogReader getReflogReader(@NonNull Ref ref)
+ throws IOException;
+
+ /**
* Get a section of the reference namespace.
*
* @param prefix
@@ -610,4 +628,22 @@ public static Ref findRef(Map<String, Ref> map, String name) {
}
return null;
}
+
+ /**
+ * Optimize pack ref storage.
+ *
+ * @param pm
+ * a progress monitor
+ *
+ * @param packRefs
+ * {@link PackRefsCommand} to control ref packing behavior
+ *
+ * @throws java.io.IOException
+ * if an IO error occurred
+ * @since 7.1
+ */
+ public void packRefs(ProgressMonitor pm, PackRefsCommand packRefs)
+ throws IOException {
+ // nothing
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
index 4722e29..c9dc6da 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
@@ -26,6 +26,8 @@
import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.net.URISyntaxException;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
@@ -33,10 +35,12 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Pattern;
import org.eclipse.jgit.annotations.NonNull;
@@ -113,9 +117,12 @@ public static ListenerList getGlobalListenerList() {
final AtomicLong closedAt = new AtomicLong();
- /** Metadata directory holding the repository's critical files. */
+ /** $GIT_DIR: metadata directory holding the repository's critical files. */
private final File gitDir;
+ /** $GIT_COMMON_DIR: metadata directory holding the common repository's critical files. */
+ private final File gitCommonDir;
+
/** File abstraction used to resolve paths. */
private final FS fs;
@@ -129,6 +136,8 @@ public static ListenerList getGlobalListenerList() {
private final String initialBranch;
+ private final AtomicReference<Boolean> caseInsensitiveWorktree = new AtomicReference<>();
+
/**
* Initialize a new repository instance.
*
@@ -137,6 +146,7 @@ public static ListenerList getGlobalListenerList() {
*/
protected Repository(BaseRepositoryBuilder options) {
gitDir = options.getGitDir();
+ gitCommonDir = options.getGitCommonDir();
fs = options.getFS();
workTree = options.getWorkTree();
indexFile = options.getIndexFile();
@@ -220,6 +230,16 @@ public File getDirectory() {
public abstract String getIdentifier();
/**
+ * Get common dir.
+ *
+ * @return $GIT_COMMON_DIR: local common metadata directory;
+ * @since 7.0
+ */
+ public File getCommonDirectory() {
+ return gitCommonDir;
+ }
+
+ /**
* Get the object database which stores this repository's data.
*
* @return the object database which stores this repository's data.
@@ -293,25 +313,6 @@ public FS getFS() {
}
/**
- * Whether the specified object is stored in this repo or any of the known
- * shared repositories.
- *
- * @param objectId
- * a {@link org.eclipse.jgit.lib.AnyObjectId} object.
- * @return true if the specified object is stored in this repo or any of the
- * known shared repositories.
- * @deprecated use {@code getObjectDatabase().has(objectId)}
- */
- @Deprecated
- public boolean hasObject(AnyObjectId objectId) {
- try {
- return getObjectDatabase().has(objectId);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- /**
* Open an object from this repository.
* <p>
* This is a one-shot call interface which may be faster than allocating a
@@ -1150,11 +1151,9 @@ public Map<String, Ref> getTags() {
* new Ref object representing the same data as Ref, but isPeeled()
* will be true and getPeeledObjectId will contain the peeled object
* (or null).
- * @deprecated use {@code getRefDatabase().peel(ref)} instead.
*/
- @Deprecated
@NonNull
- public Ref peel(Ref ref) {
+ private Ref peel(Ref ref) {
try {
return getRefDatabase().peel(ref);
} catch (IOException e) {
@@ -1584,6 +1583,40 @@ public File getWorkTree() throws NoWorkTreeException {
}
/**
+ * Tells whether the work tree is on a case-insensitive file system.
+ *
+ * @return {@code true} if the work tree is case-insensitive; {@code false}
+ * otherwise
+ * @throws NoWorkTreeException
+ * if the repository is bare
+ * @since 7.2
+ */
+ public boolean isWorkTreeCaseInsensitive() throws NoWorkTreeException {
+ Boolean flag = caseInsensitiveWorktree.get();
+ if (flag == null) {
+ File directory = getWorkTree();
+ // See if we can find ".git" also as ".GIT".
+ File dotGit = new File(directory, Constants.DOT_GIT);
+ if (Files.exists(dotGit.toPath(), LinkOption.NOFOLLOW_LINKS)) {
+ dotGit = new File(directory,
+ Constants.DOT_GIT.toUpperCase(Locale.ROOT));
+ flag = Boolean.valueOf(Files.exists(dotGit.toPath(),
+ LinkOption.NOFOLLOW_LINKS));
+ } else {
+ // Fall back to a mostly sane default. On Mac, HFS+ and APFS
+ // partitions are case-insensitive by default but can be
+ // configured to be case-sensitive.
+ SystemReader system = SystemReader.getInstance();
+ flag = Boolean.valueOf(system.isWindows() || system.isMacOS());
+ }
+ if (!caseInsensitiveWorktree.compareAndSet(null, flag)) {
+ flag = caseInsensitiveWorktree.get();
+ }
+ }
+ return flag.booleanValue();
+ }
+
+ /**
* Force a scan for changed refs. Fires an IndexChangedEvent(false) if
* changes are detected.
*
@@ -1699,10 +1732,13 @@ public void setGitwebDescription(@Nullable String description)
* @throws java.io.IOException
* the ref could not be accessed.
* @since 3.0
+ * @deprecated use {@code #getRefDatabase().getReflogReader(String)} instead
*/
+ @Deprecated(since = "7.2")
@Nullable
- public abstract ReflogReader getReflogReader(String refName)
- throws IOException;
+ public ReflogReader getReflogReader(String refName) throws IOException {
+ return getRefDatabase().getReflogReader(refName);
+ }
/**
* Get the reflog reader. Subclasses should override this method and provide
@@ -1710,15 +1746,17 @@ public abstract ReflogReader getReflogReader(String refName)
*
* @param ref
* a Ref
- * @return a {@link org.eclipse.jgit.lib.ReflogReader} for the supplied ref,
- * or {@code null} if the ref does not exist.
+ * @return a {@link org.eclipse.jgit.lib.ReflogReader} for the supplied ref.
* @throws IOException
* if an IO error occurred
* @since 5.13.2
+ * @deprecated use {@code #getRefDatabase().getReflogReader(Ref)} instead
*/
- public @Nullable ReflogReader getReflogReader(@NonNull Ref ref)
+ @Deprecated(since = "7.2")
+ @NonNull
+ public ReflogReader getReflogReader(@NonNull Ref ref)
throws IOException {
- return getReflogReader(ref.getName());
+ return getRefDatabase().getReflogReader(ref);
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java
index 6288447..1836654 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java
@@ -450,10 +450,21 @@ public String toString() {
* Git directory.
*/
public static boolean isGitRepository(File dir, FS fs) {
- return fs.resolve(dir, Constants.OBJECTS).exists()
- && fs.resolve(dir, "refs").exists() //$NON-NLS-1$
- && (fs.resolve(dir, Constants.REFTABLE).exists()
- || isValidHead(new File(dir, Constants.HEAD)));
+ // check if common-dir available or fallback to git-dir
+ File commonDir;
+ try {
+ commonDir = fs.getCommonDir(dir);
+ } catch (IOException e) {
+ commonDir = null;
+ }
+ if (commonDir == null) {
+ commonDir = dir;
+ }
+ return fs.resolve(commonDir, Constants.OBJECTS).exists()
+ && fs.resolve(commonDir, "refs").exists() //$NON-NLS-1$
+ && (fs.resolve(commonDir, Constants.REFTABLE).exists()
+ || isValidHead(
+ new File(commonDir, Constants.HEAD)));
}
private static boolean isValidHead(File head) {
@@ -496,15 +507,31 @@ private static String readFirstLine(File head) {
* null if there is no suitable match.
*/
public static File resolve(File directory, FS fs) {
- if (isGitRepository(directory, fs))
+ // the folder itself
+ if (isGitRepository(directory, fs)) {
return directory;
- if (isGitRepository(new File(directory, Constants.DOT_GIT), fs))
- return new File(directory, Constants.DOT_GIT);
-
- final String name = directory.getName();
- final File parent = directory.getParentFile();
- if (isGitRepository(new File(parent, name + Constants.DOT_GIT_EXT), fs))
- return new File(parent, name + Constants.DOT_GIT_EXT);
+ }
+ // the .git subfolder or file (reference)
+ File dotDir = new File(directory, Constants.DOT_GIT);
+ if (dotDir.isFile()) {
+ try {
+ File refDir = BaseRepositoryBuilder.getSymRef(directory,
+ dotDir, fs);
+ if (refDir != null && isGitRepository(refDir, fs)) {
+ return refDir;
+ }
+ } catch (IOException ignored) {
+ // Continue searching if gitdir ref isn't found
+ }
+ } else if (isGitRepository(dotDir, fs)) {
+ return dotDir;
+ }
+ // the folder extended with .git (bare)
+ File bareDir = new File(directory.getParentFile(),
+ directory.getName() + Constants.DOT_GIT_EXT);
+ if (isGitRepository(bareDir, fs)) {
+ return bareDir;
+ }
return null;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/SignatureVerifier.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/SignatureVerifier.java
new file mode 100644
index 0000000..2ce2708
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/SignatureVerifier.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2021, 2024 Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.lib;
+
+import java.io.IOException;
+import java.util.Date;
+
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+
+/**
+ * A {@code SignatureVerifier} can verify signatures on git commits and tags.
+ *
+ * @since 7.0
+ */
+public interface SignatureVerifier {
+
+ /**
+ * Verifies a given signature for given data.
+ *
+ * @param repository
+ * the {@link Repository} the data comes from.
+ * @param config
+ * the {@link GpgConfig}
+ * @param data
+ * the signature is for
+ * @param signatureData
+ * the ASCII-armored signature
+ * @return a {@link SignatureVerification} describing the outcome
+ * @throws IOException
+ * if the signature cannot be parsed
+ * @throws JGitInternalException
+ * if signature verification fails
+ */
+ SignatureVerification verify(@NonNull Repository repository,
+ @NonNull GpgConfig config, byte[] data, byte[] signatureData)
+ throws IOException;
+
+ /**
+ * Retrieves the name of this verifier. This should be a short string
+ * identifying the engine that verified the signature, like "gpg" if GPG is
+ * used, or "bc" for a BouncyCastle implementation.
+ *
+ * @return the name
+ */
+ @NonNull
+ String getName();
+
+ /**
+ * A {@link SignatureVerifier} may cache public keys to speed up
+ * verifying signatures on multiple objects. This clears this cache, if any.
+ */
+ void clear();
+
+ /**
+ * A {@code SignatureVerification} returns data about a (positively or
+ * negatively) verified signature.
+ *
+ * @param verifierName
+ * the name of the verifier that created this verification result
+ * @param creationDate
+ * date and time the signature was created
+ * @param signer
+ * the signer as stored in the signature, or {@code null} if
+ * unknown
+ * @param keyFingerprint
+ * fingerprint of the public key, or {@code null} if unknown
+ * @param keyUser
+ * user associated with the key, or {@code null} if unknown
+ * @param verified
+ * whether the signature verification was successful
+ * @param expired
+ * whether the public key used for this signature verification
+ * was expired when the signature was created
+ * @param trustLevel
+ * the trust level of the public key used to verify the signature
+ * @param message
+ * human-readable message giving additional information about the
+ * outcome of the verification, possibly {@code null}
+ */
+ record SignatureVerification(
+ String verifierName,
+ Date creationDate,
+ String signer,
+ String keyFingerprint,
+ String keyUser,
+ boolean verified,
+ boolean expired,
+ @NonNull TrustLevel trustLevel,
+ String message) {
+ }
+
+ /**
+ * The owner's trust in a public key.
+ */
+ enum TrustLevel {
+ UNKNOWN, NEVER, MARGINAL, FULL, ULTIMATE
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/SignatureVerifierFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/SignatureVerifierFactory.java
new file mode 100644
index 0000000..7844aba
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/SignatureVerifierFactory.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.lib;
+
+import org.eclipse.jgit.annotations.NonNull;
+
+/**
+ * A factory for {@link SignatureVerifier}s.
+ *
+ * @since 7.0
+ */
+public interface SignatureVerifierFactory {
+
+ /**
+ * Tells what kind of {@link SignatureVerifier} this factory creates.
+ *
+ * @return the {@link GpgConfig.GpgFormat} of the signer
+ */
+ @NonNull
+ GpgConfig.GpgFormat getType();
+
+ /**
+ * Creates a new instance of a {@link SignatureVerifier} that can produce
+ * signatures of type {@link #getType()}.
+ *
+ * @return a new {@link SignatureVerifier}
+ */
+ @NonNull
+ SignatureVerifier create();
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/SignatureVerifiers.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/SignatureVerifiers.java
new file mode 100644
index 0000000..01c8422
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/SignatureVerifiers.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2024 Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.lib;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.EnumMap;
+import java.util.Map;
+import java.util.ServiceConfigurationError;
+import java.util.ServiceLoader;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.revwalk.RevTag;
+import org.eclipse.jgit.util.RawParseUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Manages the available signers.
+ *
+ * @since 7.0
+ */
+public final class SignatureVerifiers {
+
+ private static final Logger LOG = LoggerFactory.getLogger(SignatureVerifiers.class);
+
+ private static final byte[] PGP_PREFIX = Constants.GPG_SIGNATURE_PREFIX
+ .getBytes(StandardCharsets.US_ASCII);
+
+ private static final byte[] X509_PREFIX = Constants.CMS_SIGNATURE_PREFIX
+ .getBytes(StandardCharsets.US_ASCII);
+
+ private static final byte[] SSH_PREFIX = Constants.SSH_SIGNATURE_PREFIX
+ .getBytes(StandardCharsets.US_ASCII);
+
+ private static final Map<GpgConfig.GpgFormat, SignatureVerifierFactory> FACTORIES = loadSignatureVerifiers();
+
+ private static final Map<GpgConfig.GpgFormat, SignatureVerifier> VERIFIERS = new ConcurrentHashMap<>();
+
+ private static Map<GpgConfig.GpgFormat, SignatureVerifierFactory> loadSignatureVerifiers() {
+ Map<GpgConfig.GpgFormat, SignatureVerifierFactory> result = new EnumMap<>(
+ GpgConfig.GpgFormat.class);
+ try {
+ for (SignatureVerifierFactory factory : ServiceLoader
+ .load(SignatureVerifierFactory.class)) {
+ GpgConfig.GpgFormat format = factory.getType();
+ SignatureVerifierFactory existing = result.get(format);
+ if (existing != null) {
+ LOG.warn("{}", //$NON-NLS-1$
+ MessageFormat.format(
+ JGitText.get().signatureServiceConflict,
+ "SignatureVerifierFactory", format, //$NON-NLS-1$
+ existing.getClass().getCanonicalName(),
+ factory.getClass().getCanonicalName()));
+ } else {
+ result.put(format, factory);
+ }
+ }
+ } catch (ServiceConfigurationError e) {
+ LOG.error(e.getMessage(), e);
+ }
+ return result;
+ }
+
+ private SignatureVerifiers() {
+ // No instantiation
+ }
+
+ /**
+ * Retrieves a {@link Signer} that can produce signatures of the given type
+ * {@code format}.
+ *
+ * @param format
+ * {@link GpgConfig.GpgFormat} the signer must support
+ * @return a {@link Signer}, or {@code null} if none is available
+ */
+ public static SignatureVerifier get(@NonNull GpgConfig.GpgFormat format) {
+ return VERIFIERS.computeIfAbsent(format, f -> {
+ SignatureVerifierFactory factory = FACTORIES.get(format);
+ if (factory == null) {
+ return null;
+ }
+ return factory.create();
+ });
+ }
+
+ /**
+ * Sets a specific signature verifier to use for a specific signature type.
+ *
+ * @param format
+ * signature type to set the {@code verifier} for
+ * @param verifier
+ * the {@link SignatureVerifier} to use for signatures of type
+ * {@code format}; if {@code null}, a default implementation, if
+ * available, may be used.
+ */
+ public static void set(@NonNull GpgConfig.GpgFormat format,
+ SignatureVerifier verifier) {
+ SignatureVerifier previous;
+ if (verifier == null) {
+ previous = VERIFIERS.remove(format);
+ } else {
+ previous = VERIFIERS.put(format, verifier);
+ }
+ if (previous != null) {
+ previous.clear();
+ }
+ }
+
+ /**
+ * Verifies the signature on a signed commit or tag.
+ *
+ * @param repository
+ * the {@link Repository} the object is from
+ * @param config
+ * the {@link GpgConfig} to use
+ * @param object
+ * to verify
+ * @return a {@link SignatureVerifier.SignatureVerification} describing the
+ * outcome of the verification, or {@code null} if the object does
+ * not have a signature of a known type
+ * @throws IOException
+ * if an error occurs getting a public key
+ * @throws org.eclipse.jgit.api.errors.JGitInternalException
+ * if signature verification fails
+ */
+ @Nullable
+ public static SignatureVerifier.SignatureVerification verify(
+ @NonNull Repository repository, @NonNull GpgConfig config,
+ @NonNull RevObject object) throws IOException {
+ if (object instanceof RevCommit) {
+ RevCommit commit = (RevCommit) object;
+ byte[] signatureData = commit.getRawGpgSignature();
+ if (signatureData == null) {
+ return null;
+ }
+ byte[] raw = commit.getRawBuffer();
+ // Now remove the GPG signature
+ byte[] header = { 'g', 'p', 'g', 's', 'i', 'g' };
+ int start = RawParseUtils.headerStart(header, raw, 0);
+ if (start < 0) {
+ return null;
+ }
+ int end = RawParseUtils.nextLfSkippingSplitLines(raw, start);
+ // start is at the beginning of the header's content
+ start -= header.length + 1;
+ // end is on the terminating LF; we need to skip that, too
+ if (end < raw.length) {
+ end++;
+ }
+ byte[] data = new byte[raw.length - (end - start)];
+ System.arraycopy(raw, 0, data, 0, start);
+ System.arraycopy(raw, end, data, start, raw.length - end);
+ return verify(repository, config, data, signatureData);
+ } else if (object instanceof RevTag) {
+ RevTag tag = (RevTag) object;
+ byte[] signatureData = tag.getRawGpgSignature();
+ if (signatureData == null) {
+ return null;
+ }
+ byte[] raw = tag.getRawBuffer();
+ // The signature is just tacked onto the end of the message, which
+ // is last in the buffer.
+ byte[] data = Arrays.copyOfRange(raw, 0,
+ raw.length - signatureData.length);
+ return verify(repository, config, data, signatureData);
+ }
+ return null;
+ }
+
+ /**
+ * Verifies a given signature for some give data.
+ *
+ * @param repository
+ * the {@link Repository} the object is from
+ * @param config
+ * the {@link GpgConfig} to use
+ * @param data
+ * to verify the signature of
+ * @param signature
+ * the given signature of the {@code data}
+ * @return a {@link SignatureVerifier.SignatureVerification} describing the
+ * outcome of the verification, or {@code null} if the signature
+ * type is unknown
+ * @throws IOException
+ * if an error occurs getting a public key
+ * @throws org.eclipse.jgit.api.errors.JGitInternalException
+ * if signature verification fails
+ */
+ @Nullable
+ public static SignatureVerifier.SignatureVerification verify(
+ @NonNull Repository repository, @NonNull GpgConfig config,
+ byte[] data, byte[] signature) throws IOException {
+ GpgConfig.GpgFormat format = getFormat(signature);
+ if (format == null) {
+ return null;
+ }
+ SignatureVerifier verifier = get(format);
+ if (verifier == null) {
+ return null;
+ }
+ return verifier.verify(repository, config, data, signature);
+ }
+
+ /**
+ * Determines the type of a given signature.
+ *
+ * @param signature
+ * to get the type of
+ * @return the signature type, or {@code null} if unknown
+ */
+ @Nullable
+ public static GpgConfig.GpgFormat getFormat(byte[] signature) {
+ if (RawParseUtils.match(signature, 0, PGP_PREFIX) > 0) {
+ return GpgConfig.GpgFormat.OPENPGP;
+ }
+ if (RawParseUtils.match(signature, 0, X509_PREFIX) > 0) {
+ return GpgConfig.GpgFormat.X509;
+ }
+ if (RawParseUtils.match(signature, 0, SSH_PREFIX) > 0) {
+ return GpgConfig.GpgFormat.SSH;
+ }
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Signer.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Signer.java
new file mode 100644
index 0000000..3bb7464
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Signer.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2024 Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.lib;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.api.errors.CanceledException;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.api.errors.UnsupportedSigningFormatException;
+import org.eclipse.jgit.transport.CredentialsProvider;
+
+/**
+ * Creates signatures for Git objects.
+ *
+ * @since 7.0
+ */
+public interface Signer {
+
+ /**
+ * Signs the specified object.
+ *
+ * <p>
+ * Implementors should obtain the payload for signing from the specified
+ * object via {@link ObjectBuilder#build()} and create a proper
+ * {@link GpgSignature}. The generated signature is set on the specified
+ * {@code object} (see {@link ObjectBuilder#setGpgSignature(GpgSignature)}).
+ * </p>
+ * <p>
+ * Any existing signature on the object must be discarded prior obtaining
+ * the payload via {@link ObjectBuilder#build()}.
+ * </p>
+ *
+ * @param repository
+ * {@link Repository} the object belongs to
+ * @param config
+ * GPG settings from the git config
+ * @param object
+ * the object to sign (must not be {@code null} and must be
+ * complete to allow proper calculation of payload)
+ * @param committer
+ * the signing identity (to help with key lookup in case signing
+ * key is not specified)
+ * @param signingKey
+ * if non-{@code null} overrides the signing key from the config
+ * @param credentialsProvider
+ * provider to use when querying for signing key credentials (eg.
+ * passphrase)
+ * @throws CanceledException
+ * when signing was canceled (eg., user aborted when entering
+ * passphrase)
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws UnsupportedSigningFormatException
+ * if a config is given and the wanted key format is not
+ * supported
+ */
+ default void signObject(@NonNull Repository repository,
+ @NonNull GpgConfig config, @NonNull ObjectBuilder object,
+ @NonNull PersonIdent committer, String signingKey,
+ CredentialsProvider credentialsProvider)
+ throws CanceledException, IOException,
+ UnsupportedSigningFormatException {
+ try {
+ object.setGpgSignature(sign(repository, config, object.build(),
+ committer, signingKey, credentialsProvider));
+ } catch (UnsupportedEncodingException e) {
+ throw new JGitInternalException(e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Signs arbitrary data.
+ *
+ * @param repository
+ * {@link Repository} the signature is created in
+ * @param config
+ * GPG settings from the git config
+ * @param data
+ * the data to sign
+ * @param committer
+ * the signing identity (to help with key lookup in case signing
+ * key is not specified)
+ * @param signingKey
+ * if non-{@code null} overrides the signing key from the config
+ * @param credentialsProvider
+ * provider to use when querying for signing key credentials (eg.
+ * passphrase)
+ * @return the signature for {@code data}
+ * @throws CanceledException
+ * when signing was canceled (eg., user aborted when entering
+ * passphrase)
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws UnsupportedSigningFormatException
+ * if a config is given and the wanted key format is not
+ * supported
+ */
+ GpgSignature sign(@NonNull Repository repository, @NonNull GpgConfig config,
+ byte[] data, @NonNull PersonIdent committer, String signingKey,
+ CredentialsProvider credentialsProvider) throws CanceledException,
+ IOException, UnsupportedSigningFormatException;
+
+ /**
+ * Indicates if a signing key is available for the specified committer
+ * and/or signing key.
+ *
+ * @param repository
+ * the current {@link Repository}
+ * @param config
+ * GPG settings from the git config
+ * @param committer
+ * the signing identity (to help with key lookup in case signing
+ * key is not specified)
+ * @param signingKey
+ * if non-{@code null} overrides the signing key from the config
+ * @param credentialsProvider
+ * provider to use when querying for signing key credentials (eg.
+ * passphrase)
+ * @return {@code true} if a signing key is available, {@code false}
+ * otherwise
+ * @throws CanceledException
+ * when signing was canceled (eg., user aborted when entering
+ * passphrase)
+ */
+ boolean canLocateSigningKey(@NonNull Repository repository,
+ @NonNull GpgConfig config, @NonNull PersonIdent committer,
+ String signingKey, CredentialsProvider credentialsProvider)
+ throws CanceledException;
+
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/SignerFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/SignerFactory.java
new file mode 100644
index 0000000..125d25e
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/SignerFactory.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.lib;
+
+import org.eclipse.jgit.annotations.NonNull;
+
+/**
+ * A factory for {@link Signer}s.
+ *
+ * @since 7.0
+ */
+public interface SignerFactory {
+
+ /**
+ * Tells what kind of {@link Signer} this factory creates.
+ *
+ * @return the {@link GpgConfig.GpgFormat} of the signer
+ */
+ @NonNull
+ GpgConfig.GpgFormat getType();
+
+ /**
+ * Creates a new instance of a {@link Signer} that can produce signatures of
+ * type {@link #getType()}.
+ *
+ * @return a new {@link Signer}
+ */
+ @NonNull
+ Signer create();
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Signers.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Signers.java
new file mode 100644
index 0000000..7771b07
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Signers.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2024 Thomas Wolf <twolf@apache.org> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.lib;
+
+import java.text.MessageFormat;
+import java.util.EnumMap;
+import java.util.Map;
+import java.util.ServiceConfigurationError;
+import java.util.ServiceLoader;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.internal.JGitText;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Manages the available signers.
+ *
+ * @since 7.0
+ */
+public final class Signers {
+
+ private static final Logger LOG = LoggerFactory.getLogger(Signers.class);
+
+ private static final Map<GpgConfig.GpgFormat, SignerFactory> SIGNER_FACTORIES = loadSigners();
+
+ private static final Map<GpgConfig.GpgFormat, Signer> SIGNERS = new ConcurrentHashMap<>();
+
+ private static Map<GpgConfig.GpgFormat, SignerFactory> loadSigners() {
+ Map<GpgConfig.GpgFormat, SignerFactory> result = new EnumMap<>(
+ GpgConfig.GpgFormat.class);
+ try {
+ for (SignerFactory factory : ServiceLoader
+ .load(SignerFactory.class)) {
+ GpgConfig.GpgFormat format = factory.getType();
+ SignerFactory existing = result.get(format);
+ if (existing != null) {
+ LOG.warn("{}", //$NON-NLS-1$
+ MessageFormat.format(
+ JGitText.get().signatureServiceConflict,
+ "SignerFactory", format, //$NON-NLS-1$
+ existing.getClass().getCanonicalName(),
+ factory.getClass().getCanonicalName()));
+ } else {
+ result.put(format, factory);
+ }
+ }
+ } catch (ServiceConfigurationError e) {
+ LOG.error(e.getMessage(), e);
+ }
+ return result;
+ }
+
+ private Signers() {
+ // No instantiation
+ }
+
+ /**
+ * Retrieves a {@link Signer} that can produce signatures of the given type
+ * {@code format}.
+ *
+ * @param format
+ * {@link GpgConfig.GpgFormat} the signer must support
+ * @return a {@link Signer}, or {@code null} if none is available
+ */
+ public static Signer get(@NonNull GpgConfig.GpgFormat format) {
+ return SIGNERS.computeIfAbsent(format, f -> {
+ SignerFactory factory = SIGNER_FACTORIES.get(format);
+ if (factory == null) {
+ return null;
+ }
+ return factory.create();
+ });
+ }
+
+ /**
+ * Sets a specific signer to use for a specific signature type.
+ *
+ * @param format
+ * signature type to set the {@code signer} for
+ * @param signer
+ * the {@link Signer} to use for signatures of type
+ * {@code format}; if {@code null}, a default implementation, if
+ * available, may be used.
+ */
+ public static void set(@NonNull GpgConfig.GpgFormat format, Signer signer) {
+ if (signer == null) {
+ SIGNERS.remove(format);
+ } else {
+ SIGNERS.put(format, signer);
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/TagBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/TagBuilder.java
index bbc6144..ea73d95 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/TagBuilder.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/TagBuilder.java
@@ -206,23 +206,6 @@ public void setTagger(PersonIdent taggerIdent) {
return os.toByteArray();
}
- /**
- * Format this builder's state as an annotated tag object.
- *
- * @return this object in the canonical annotated tag format, suitable for
- * storage in a repository, or {@code null} if the tag cannot be
- * encoded
- * @deprecated since 5.11; use {@link #build()} instead
- */
- @Deprecated
- public byte[] toByteArray() {
- try {
- return build();
- } catch (UnsupportedEncodingException e) {
- return null;
- }
- }
-
@SuppressWarnings("nls")
@Override
public String toString() {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/TypedConfigGetter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/TypedConfigGetter.java
index 0c03adc..3d4e0d1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/TypedConfigGetter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/TypedConfigGetter.java
@@ -17,6 +17,7 @@
import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.util.FS;
@@ -50,11 +51,36 @@ public interface TypedConfigGetter {
* default value to return if no value was present.
* @return true if any value or defaultValue is true, false for missing or
* explicit false
+ * @deprecated use
+ * {@link #getBoolean(Config, String, String, String, Boolean)}
+ * instead
*/
+ @Deprecated
boolean getBoolean(Config config, String section, String subsection,
String name, boolean defaultValue);
/**
+ * Get a boolean value from a git {@link Config}.
+ *
+ * @param config
+ * to get the value from
+ * @param section
+ * section the key is grouped within.
+ * @param subsection
+ * subsection name, such a remote or branch name.
+ * @param name
+ * name of the key to get.
+ * @param defaultValue
+ * default value to return if no value was present.
+ * @return true if any value or defaultValue is true, false for missing or
+ * explicit false
+ * @since 7.2
+ */
+ @Nullable
+ Boolean getBoolean(Config config, String section, String subsection,
+ String name, @Nullable Boolean defaultValue);
+
+ /**
* Parse an enumeration from a git {@link Config}.
*
* @param <T>
@@ -74,8 +100,9 @@ boolean getBoolean(Config config, String section, String subsection,
* default value to return if no value was present.
* @return the selected enumeration value, or {@code defaultValue}.
*/
+ @Nullable
<T extends Enum<?>> T getEnum(Config config, T[] all, String section,
- String subsection, String name, T defaultValue);
+ String subsection, String name, @Nullable T defaultValue);
/**
* Obtain an integer value from a git {@link Config}.
@@ -91,11 +118,34 @@ <T extends Enum<?>> T getEnum(Config config, T[] all, String section,
* @param defaultValue
* default value to return if no value was present.
* @return an integer value from the configuration, or defaultValue.
+ * @deprecated use {@link #getInt(Config, String, String, String, Integer)}
+ * instead
*/
+ @Deprecated
int getInt(Config config, String section, String subsection, String name,
int defaultValue);
/**
+ * Obtain an integer value from a git {@link Config}.
+ *
+ * @param config
+ * to get the value from
+ * @param section
+ * section the key is grouped within.
+ * @param subsection
+ * subsection name, such a remote or branch name.
+ * @param name
+ * name of the key to get.
+ * @param defaultValue
+ * default value to return if no value was present.
+ * @return an integer value from the configuration, or defaultValue.
+ * @since 7.2
+ */
+ @Nullable
+ Integer getInt(Config config, String section, String subsection,
+ String name, @Nullable Integer defaultValue);
+
+ /**
* Obtain an integer value from a git {@link Config} which must be in given
* range.
*
@@ -117,11 +167,43 @@ int getInt(Config config, String section, String subsection, String name,
* @return an integer value from the configuration, or defaultValue.
* {@code #UNSET_INT} if unset.
* @since 6.1
+ * @deprecated use
+ * {@link #getIntInRange(Config, String, String, String, int, int, Integer)}
+ * instead
*/
+ @Deprecated
int getIntInRange(Config config, String section, String subsection,
String name, int minValue, int maxValue, int defaultValue);
/**
+ * Obtain an integer value from a git {@link Config} which must be in given
+ * range.
+ *
+ * @param config
+ * to get the value from
+ * @param section
+ * section the key is grouped within.
+ * @param subsection
+ * subsection name, such a remote or branch name.
+ * @param name
+ * name of the key to get.
+ * @param minValue
+ * minimal value
+ * @param maxValue
+ * maximum value
+ * @param defaultValue
+ * default value to return if no value was present. Use
+ * {@code #UNSET_INT} to set the default to unset.
+ * @return an integer value from the configuration, or defaultValue.
+ * {@code #UNSET_INT} if unset.
+ * @since 7.2
+ */
+ @Nullable
+ Integer getIntInRange(Config config, String section, String subsection,
+ String name, int minValue, int maxValue,
+ @Nullable Integer defaultValue);
+
+ /**
* Obtain a long value from a git {@link Config}.
*
* @param config
@@ -135,11 +217,34 @@ int getIntInRange(Config config, String section, String subsection,
* @param defaultValue
* default value to return if no value was present.
* @return a long value from the configuration, or defaultValue.
+ * @deprecated use {@link #getLong(Config, String, String, String, Long)}
+ * instead
*/
+ @Deprecated
long getLong(Config config, String section, String subsection, String name,
long defaultValue);
/**
+ * Obtain a long value from a git {@link Config}.
+ *
+ * @param config
+ * to get the value from
+ * @param section
+ * section the key is grouped within.
+ * @param subsection
+ * subsection name, such a remote or branch name.
+ * @param name
+ * name of the key to get.
+ * @param defaultValue
+ * default value to return if no value was present.
+ * @return a long value from the configuration, or defaultValue.
+ * @since 7.2
+ */
+ @Nullable
+ Long getLong(Config config, String section, String subsection, String name,
+ @Nullable Long defaultValue);
+
+ /**
* Parse a numerical time unit, such as "1 minute", from a git
* {@link Config}.
*
@@ -159,11 +264,41 @@ long getLong(Config config, String section, String subsection, String name,
* indication of the units.
* @return the value, or {@code defaultValue} if not set, expressed in
* {@code units}.
+ * @deprecated use
+ * {@link #getTimeUnit(Config, String, String, String, Long, TimeUnit)}
+ * instead
*/
+ @Deprecated
long getTimeUnit(Config config, String section, String subsection,
String name, long defaultValue, TimeUnit wantUnit);
/**
+ * Parse a numerical time unit, such as "1 minute", from a git
+ * {@link Config}.
+ *
+ * @param config
+ * to get the value from
+ * @param section
+ * section the key is in.
+ * @param subsection
+ * subsection the key is in, or null if not in a subsection.
+ * @param name
+ * the key name.
+ * @param defaultValue
+ * default value to return if no value was present.
+ * @param wantUnit
+ * the units of {@code defaultValue} and the return value, as
+ * well as the units to assume if the value does not contain an
+ * indication of the units.
+ * @return the value, or {@code defaultValue} if not set, expressed in
+ * {@code units}.
+ * @since 7.2
+ */
+ @Nullable
+ Long getTimeUnit(Config config, String section, String subsection,
+ String name, @Nullable Long defaultValue, TimeUnit wantUnit);
+
+ /**
* Parse a string value from a git {@link Config} and treat it as a file
* path, replacing a ~/ prefix by the user's home directory.
* <p>
@@ -189,9 +324,10 @@ long getTimeUnit(Config config, String section, String subsection,
* @return the {@link Path}, or {@code defaultValue} if not set
* @since 5.10
*/
+ @Nullable
default Path getPath(Config config, String section, String subsection,
String name, @NonNull FS fs, File resolveAgainst,
- Path defaultValue) {
+ @Nullable Path defaultValue) {
String value = config.getString(section, subsection, name);
if (value == null) {
return defaultValue;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ContentMergeStrategy.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ContentMergeStrategy.java
index 6d56864..a835a1d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ContentMergeStrategy.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ContentMergeStrategy.java
@@ -23,5 +23,12 @@ public enum ContentMergeStrategy {
OURS,
/** Resolve the conflict hunk using the theirs version. */
- THEIRS
-}
\ No newline at end of file
+ THEIRS,
+
+ /**
+ * Resolve the conflict hunk using a union of both ours and theirs versions.
+ *
+ * @since 6.10.1
+ */
+ UNION
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeAlgorithm.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeAlgorithm.java
index 5734a25..d0d4d36 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeAlgorithm.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeAlgorithm.java
@@ -120,6 +120,7 @@ public <S extends Sequence> MergeResult<S> merge(
result.add(1, 0, 0, ConflictState.NO_CONFLICT);
break;
case THEIRS:
+ case UNION:
result.add(2, 0, theirs.size(),
ConflictState.NO_CONFLICT);
break;
@@ -148,6 +149,7 @@ public <S extends Sequence> MergeResult<S> merge(
// we modified, they deleted
switch (strategy) {
case OURS:
+ case UNION:
result.add(1, 0, ours.size(), ConflictState.NO_CONFLICT);
break;
case THEIRS:
@@ -158,7 +160,7 @@ public <S extends Sequence> MergeResult<S> merge(
result.add(1, 0, ours.size(),
ConflictState.FIRST_CONFLICTING_RANGE);
result.add(0, 0, base.size(),
- ConflictState.BASE_CONFLICTING_RANGE);
+ ConflictState.BASE_CONFLICTING_RANGE);
result.add(2, 0, 0, ConflictState.NEXT_CONFLICTING_RANGE);
break;
}
@@ -333,6 +335,15 @@ public <S extends Sequence> MergeResult<S> merge(
theirsEndB - commonSuffix,
ConflictState.NO_CONFLICT);
break;
+ case UNION:
+ result.add(1, oursBeginB + commonPrefix,
+ oursEndB - commonSuffix,
+ ConflictState.NO_CONFLICT);
+
+ result.add(2, theirsBeginB + commonPrefix,
+ theirsEndB - commonSuffix,
+ ConflictState.NO_CONFLICT);
+ break;
default:
result.add(1, oursBeginB + commonPrefix,
oursEndB - commonSuffix,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatter.java
index a35b30e..079db4a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatter.java
@@ -22,37 +22,6 @@
* A class to convert merge results into a Git conformant textual presentation
*/
public class MergeFormatter {
- /**
- * Formats the results of a merge of {@link org.eclipse.jgit.diff.RawText}
- * objects in a Git conformant way. This method also assumes that the
- * {@link org.eclipse.jgit.diff.RawText} objects being merged are line
- * oriented files which use LF as delimiter. This method will also use LF to
- * separate chunks and conflict metadata, therefore it fits only to texts
- * that are LF-separated lines.
- *
- * @param out
- * the output stream where to write the textual presentation
- * @param res
- * the merge result which should be presented
- * @param seqName
- * When a conflict is reported each conflicting range will get a
- * name. This name is following the "<<<<<<<
- * " or ">>>>>>> " conflict markers. The
- * names for the sequences are given in this list
- * @param charsetName
- * the name of the character set used when writing conflict
- * metadata
- * @throws java.io.IOException
- * if an IO error occurred
- * @deprecated Use
- * {@link #formatMerge(OutputStream, MergeResult, List, Charset)}
- * instead.
- */
- @Deprecated
- public void formatMerge(OutputStream out, MergeResult<RawText> res,
- List<String> seqName, String charsetName) throws IOException {
- formatMerge(out, res, seqName, Charset.forName(charsetName));
- }
/**
* Formats the results of a merge of {@link org.eclipse.jgit.diff.RawText}
@@ -129,40 +98,6 @@ public void formatMergeDiff3(OutputStream out,
* the name ranges from ours should get
* @param theirsName
* the name ranges from theirs should get
- * @param charsetName
- * the name of the character set used when writing conflict
- * metadata
- * @throws java.io.IOException
- * if an IO error occurred
- * @deprecated use
- * {@link #formatMerge(OutputStream, MergeResult, String, String, String, Charset)}
- * instead.
- */
- @Deprecated
- public void formatMerge(OutputStream out, MergeResult res, String baseName,
- String oursName, String theirsName, String charsetName) throws IOException {
- formatMerge(out, res, baseName, oursName, theirsName,
- Charset.forName(charsetName));
- }
-
- /**
- * Formats the results of a merge of exactly two
- * {@link org.eclipse.jgit.diff.RawText} objects in a Git conformant way.
- * This convenience method accepts the names for the three sequences (base
- * and the two merged sequences) as explicit parameters and doesn't require
- * the caller to specify a List
- *
- * @param out
- * the {@link java.io.OutputStream} where to write the textual
- * presentation
- * @param res
- * the merge result which should be presented
- * @param baseName
- * the name ranges from the base should get
- * @param oursName
- * the name ranges from ours should get
- * @param theirsName
- * the name ranges from theirs should get
* @param charset
* the character set used when writing conflict metadata
* @throws java.io.IOException
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeMessageFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeMessageFormatter.java
index e0c083f..039d7d8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeMessageFormatter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeMessageFormatter.java
@@ -92,24 +92,6 @@ public String format(List<Ref> refsToMerge, Ref target) {
}
/**
- * Add section with conflicting paths to merge message. Lines are prefixed
- * with a hash.
- *
- * @param message
- * the original merge message
- * @param conflictingPaths
- * the paths with conflicts
- * @return merge message with conflicting paths added
- * @deprecated since 6.1; use
- * {@link #formatWithConflicts(String, Iterable, char)} instead
- */
- @Deprecated
- public String formatWithConflicts(String message,
- List<String> conflictingPaths) {
- return formatWithConflicts(message, conflictingPaths, '#');
- }
-
- /**
* Add section with conflicting paths to merge message.
*
* @param message
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java
index 1162a61..f58ef4f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java
@@ -18,10 +18,11 @@
import java.io.IOException;
import java.text.MessageFormat;
+import java.time.Instant;
+import java.time.ZoneOffset;
import java.util.ArrayList;
-import java.util.Date;
import java.util.List;
-import java.util.TimeZone;
+import java.util.stream.Collectors;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
@@ -185,12 +186,15 @@ protected RevCommit getBaseCommit(RevCommit a, RevCommit b, int callDepth)
if (mergeTrees(bcTree, currentBase.getTree(),
nextBase.getTree(), true))
currentBase = createCommitForTree(resultTree, parents);
- else
+ else {
+ String failedPaths = failingPathsMessage();
throw new NoMergeBaseException(
NoMergeBaseException.MergeBaseFailureReason.CONFLICTS_DURING_MERGE_BASE_CALCULATION,
MessageFormat.format(
JGitText.get().mergeRecursiveConflictsWhenMergingCommonAncestors,
- currentBase.getName(), nextBase.getName()));
+ currentBase.getName(), nextBase.getName(),
+ failedPaths));
+ }
}
} finally {
inCore = oldIncore;
@@ -229,11 +233,23 @@ private RevCommit createCommitForTree(ObjectId tree, List<RevCommit> parents)
private static PersonIdent mockAuthor(List<RevCommit> parents) {
String name = RecursiveMerger.class.getSimpleName();
int time = 0;
- for (RevCommit p : parents)
+ for (RevCommit p : parents) {
time = Math.max(time, p.getCommitTime());
- return new PersonIdent(
- name, name + "@JGit", //$NON-NLS-1$
- new Date((time + 1) * 1000L),
- TimeZone.getTimeZone("GMT+0000")); //$NON-NLS-1$
+ }
+ return new PersonIdent(name, name + "@JGit", //$NON-NLS-1$
+ Instant.ofEpochSecond(time+1), ZoneOffset.UTC);
+ }
+
+ private String failingPathsMessage() {
+ int max = 25;
+ String failedPaths = failingPaths.entrySet().stream().limit(max)
+ .map(entry -> entry.getKey() + ":" + entry.getValue()) //$NON-NLS-1$
+ .collect(Collectors.joining("\n")); //$NON-NLS-1$
+
+ if (failingPaths.size() > max) {
+ failedPaths = String.format("%s\n... (%s failing paths omitted)", //$NON-NLS-1$
+ failedPaths, Integer.valueOf(failingPaths.size() - max));
+ }
+ return failedPaths;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
index 1ad41be..dc96f65 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
@@ -41,6 +41,7 @@
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.attributes.Attribute;
import org.eclipse.jgit.attributes.Attributes;
+import org.eclipse.jgit.attributes.AttributesNodeProvider;
import org.eclipse.jgit.diff.DiffAlgorithm;
import org.eclipse.jgit.diff.DiffAlgorithm.SupportedAlgorithm;
import org.eclipse.jgit.diff.RawText;
@@ -837,6 +838,13 @@ public enum MergeFailureReason {
@NonNull
private ContentMergeStrategy contentStrategy = ContentMergeStrategy.CONFLICT;
+ /**
+ * The {@link AttributesNodeProvider} to use while merging trees.
+ *
+ * @since 6.10.1
+ */
+ protected AttributesNodeProvider attributesNodeProvider;
+
private static MergeAlgorithm getMergeAlgorithm(Config config) {
SupportedAlgorithm diffAlg = config.getEnum(
CONFIG_DIFF_SECTION, null, CONFIG_KEY_ALGORITHM,
@@ -1273,6 +1281,13 @@ protected boolean processEntry(CanonicalTreeParser base,
default:
break;
}
+ if (ignoreConflicts) {
+ // If the path is selected to be treated as binary via attributes, we do not perform
+ // content merge. When ignoreConflicts = true, we simply keep OURS to allow virtual commit
+ // to be built.
+ keep(ourDce);
+ return true;
+ }
// add the conflicting path to merge result
String currentPath = tw.getPathString();
MergeResult<RawText> result = new MergeResult<>(
@@ -1312,8 +1327,12 @@ protected boolean processEntry(CanonicalTreeParser base,
addToCheckout(currentPath, null, attributes);
return true;
} catch (BinaryBlobException e) {
- // if the file is binary in either OURS, THEIRS or BASE
- // here, we don't have an option to ignore conflicts
+ // The file is binary in either OURS, THEIRS or BASE
+ if (ignoreConflicts) {
+ // When ignoreConflicts = true, we simply keep OURS to allow virtual commit to be built.
+ keep(ourDce);
+ return true;
+ }
}
}
switch (getContentMergeStrategy()) {
@@ -1354,6 +1373,8 @@ protected boolean processEntry(CanonicalTreeParser base,
}
}
} else {
+ // This is reachable if contentMerge() call above threw BinaryBlobException, so we don't
+ // need to check ignoreConflicts here, since it's already handled above.
result.setContainsConflicts(true);
addConflict(base, ours, theirs);
unmergedPaths.add(currentPath);
@@ -1489,11 +1510,26 @@ private MergeResult<RawText> contentMerge(CanonicalTreeParser base,
: getRawText(ours.getEntryObjectId(), attributes[T_OURS]);
RawText theirsText = theirs == null ? RawText.EMPTY_TEXT
: getRawText(theirs.getEntryObjectId(), attributes[T_THEIRS]);
- mergeAlgorithm.setContentMergeStrategy(strategy);
+ mergeAlgorithm.setContentMergeStrategy(
+ getAttributesContentMergeStrategy(attributes[T_OURS],
+ strategy));
return mergeAlgorithm.merge(RawTextComparator.DEFAULT, baseText,
ourText, theirsText);
}
+ private ContentMergeStrategy getAttributesContentMergeStrategy(
+ Attributes attributes, ContentMergeStrategy strategy) {
+ Attribute attr = attributes.get(Constants.ATTR_MERGE);
+ if (attr != null) {
+ String attrValue = attr.getValue();
+ if (attrValue != null && attrValue
+ .equals(Constants.ATTR_BUILTIN_UNION_MERGE_DRIVER)) {
+ return ContentMergeStrategy.UNION;
+ }
+ }
+ return strategy;
+ }
+
private boolean isIndexDirty() {
if (inCore) {
return false;
@@ -1824,6 +1860,18 @@ public void setWorkingTreeIterator(WorkingTreeIterator workingTreeIterator) {
this.workingTreeIterator = workingTreeIterator;
}
+ /**
+ * Sets the {@link AttributesNodeProvider} to be used by this merger.
+ *
+ * @param attributesNodeProvider
+ * the attributeNodeProvider to set
+ * @since 6.10.1
+ */
+ public void setAttributesNodeProvider(
+ AttributesNodeProvider attributesNodeProvider) {
+ this.attributesNodeProvider = attributesNodeProvider;
+ }
+
/**
* The resolve conflict way of three way merging
@@ -1868,6 +1916,9 @@ protected boolean mergeTrees(AbstractTreeIterator baseTree,
WorkTreeUpdater.createWorkTreeUpdater(db, dircache);
dircache = workTreeUpdater.getLockedDirCache();
tw = new NameConflictTreeWalk(db, reader);
+ if (attributesNodeProvider != null) {
+ tw.setAttributesNodeProvider(attributesNodeProvider);
+ }
tw.addTree(baseTree);
tw.setHead(tw.addTree(headTree));
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteMapMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteMapMerger.java
index 79ceb13..30512c1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteMapMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteMapMerger.java
@@ -199,7 +199,7 @@ private void addIfNotNull(FanoutBucket b, int cell, NoteBucket child)
if (child == null)
return;
if (child instanceof InMemoryNoteBucket)
- b.setBucket(cell, ((InMemoryNoteBucket) child).writeTree(inserter));
+ b.setBucket(cell, child.writeTree(inserter));
else
b.setBucket(cell, child.getTreeId());
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/patch/PatchApplier.java b/org.eclipse.jgit/src/org/eclipse/jgit/patch/PatchApplier.java
index a327095..23e09b9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/patch/PatchApplier.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/patch/PatchApplier.java
@@ -23,6 +23,7 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
@@ -33,12 +34,13 @@
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.zip.InflaterInputStream;
+
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.api.errors.FilterFailedException;
-import org.eclipse.jgit.api.errors.PatchFormatException;
import org.eclipse.jgit.attributes.Attribute;
import org.eclipse.jgit.attributes.Attributes;
import org.eclipse.jgit.attributes.FilterCommand;
@@ -101,11 +103,12 @@
* @since 6.4
*/
public class PatchApplier {
-
private static final byte[] NO_EOL = "\\ No newline at end of file" //$NON-NLS-1$
.getBytes(StandardCharsets.US_ASCII);
- /** The tree before applying the patch. Only non-null for inCore operation. */
+ /**
+ * The tree before applying the patch. Only non-null for inCore operation.
+ */
@Nullable
private final RevTree beforeTree;
@@ -115,10 +118,14 @@ public class PatchApplier {
private final ObjectReader reader;
+ private final Charset charset;
+
private WorkingTreeOptions workingTreeOptions;
private int inCoreSizeLimit;
+ private boolean allowConflicts;
+
/**
* @param repo
* repository to apply the patch in
@@ -128,7 +135,8 @@ public PatchApplier(Repository repo) {
inserter = repo.newObjectInserter();
reader = inserter.newReader();
beforeTree = null;
-
+ allowConflicts = false;
+ charset = StandardCharsets.UTF_8;
Config config = repo.getConfig();
workingTreeOptions = config.get(WorkingTreeOptions.KEY);
inCoreSizeLimit = config.getInt(ConfigConstants.CONFIG_MERGE_SECTION,
@@ -143,11 +151,14 @@ public PatchApplier(Repository repo) {
* @param oi
* to be used for modifying objects
*/
- public PatchApplier(Repository repo, RevTree beforeTree, ObjectInserter oi) {
+ public PatchApplier(Repository repo, RevTree beforeTree,
+ ObjectInserter oi) {
this.repo = repo;
this.beforeTree = beforeTree;
inserter = oi;
reader = oi.newReader();
+ allowConflicts = false;
+ charset = StandardCharsets.UTF_8;
}
/**
@@ -157,7 +168,6 @@ public PatchApplier(Repository repo, RevTree beforeTree, ObjectInserter oi) {
* @since 6.3
*/
public static class Result {
-
/**
* A wrapper for a patch applying error that affects a given file.
*
@@ -166,28 +176,68 @@ public static class Result {
// TODO(ms): rename this class in next major release
@SuppressWarnings("JavaLangClash")
public static class Error {
+ final String msg;
- private String msg;
- private String oldFileName;
- private @Nullable HunkHeader hh;
+ final String oldFileName;
- private Error(String msg, String oldFileName,
- @Nullable HunkHeader hh) {
+ @Nullable
+ final HunkHeader hh;
+
+ final boolean isGitConflict;
+
+ Error(String msg, String oldFileName, @Nullable HunkHeader hh,
+ boolean isGitConflict) {
this.msg = msg;
this.oldFileName = oldFileName;
this.hh = hh;
+ this.isGitConflict = isGitConflict;
+ }
+
+ /**
+ * Signals if as part of encountering this error, conflict markers
+ * were added to the file.
+ *
+ * @return {@code true} if conflict markers were added for this
+ * error.
+ *
+ * @since 6.10
+ */
+ public boolean isGitConflict() {
+ return isGitConflict;
}
@Override
public String toString() {
if (hh != null) {
- return MessageFormat.format(JGitText.get().patchApplyErrorWithHunk,
- oldFileName, hh, msg);
+ return MessageFormat.format(
+ JGitText.get().patchApplyErrorWithHunk, oldFileName,
+ hh, msg);
}
- return MessageFormat.format(JGitText.get().patchApplyErrorWithoutHunk,
- oldFileName, msg);
+ return MessageFormat.format(
+ JGitText.get().patchApplyErrorWithoutHunk, oldFileName,
+ msg);
}
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || !(o instanceof Error)) {
+ return false;
+ }
+ Error error = (Error) o;
+ return Objects.equals(msg, error.msg)
+ && Objects.equals(oldFileName, error.oldFileName)
+ && Objects.equals(hh, error.hh)
+ && isGitConflict == error.isGitConflict;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(msg, oldFileName, hh,
+ Boolean.valueOf(isGitConflict));
+ }
}
private ObjectId treeId;
@@ -225,35 +275,15 @@ public List<Error> getErrors() {
return errors;
}
- private void addError(String msg,String oldFileName, @Nullable HunkHeader hh) {
- errors.add(new Error(msg, oldFileName, hh));
+ private void addError(String msg, String oldFileName,
+ @Nullable HunkHeader hh) {
+ errors.add(new Error(msg, oldFileName, hh, false));
}
- }
- /**
- * Applies the given patch
- *
- * @param patchInput
- * the patch to apply.
- * @return the result of the patch
- * @throws PatchFormatException
- * if the patch cannot be parsed
- * @throws IOException
- * if the patch read fails
- * @deprecated use {@link #applyPatch(Patch)} instead
- */
- @Deprecated
- public Result applyPatch(InputStream patchInput)
- throws PatchFormatException, IOException {
- Patch p = new Patch();
- try (InputStream inStream = patchInput) {
- p.parse(inStream);
-
- if (!p.getErrors().isEmpty()) {
- throw new PatchFormatException(p.getErrors());
- }
+ private void addErrorWithGitConflict(String msg, String oldFileName,
+ @Nullable HunkHeader hh) {
+ errors.add(new Error(msg, oldFileName, hh, true));
}
- return applyPatch(p);
}
/**
@@ -357,6 +387,17 @@ else if (!dirCacheBuilder.commit()) {
return result;
}
+ /**
+ * Sets up the {@link PatchApplier} to apply patches even if they conflict.
+ *
+ * @return the {@link PatchApplier} to apply any patches
+ * @since 6.10
+ */
+ public PatchApplier allowConflicts() {
+ allowConflicts = true;
+ return this;
+ }
+
private File getFile(String path) {
return inCore() ? null : new File(repo.getWorkTree(), path);
}
@@ -439,6 +480,7 @@ private boolean validGitPath(String path) {
return false;
}
}
+
private static final int FILE_TREE_INDEX = 1;
/**
@@ -539,7 +581,9 @@ private void apply(String pathWithOriginalContent, DirCache dirCache,
convertCrLf);
resultStreamLoader = applyText(raw, fh, result);
}
- if (resultStreamLoader == null || !result.getErrors().isEmpty()) {
+ if (resultStreamLoader == null
+ || (!result.getErrors().isEmpty() && result.getErrors().stream()
+ .anyMatch(e -> !e.msg.equals("cannot apply hunk")))) { //$NON-NLS-1$
return;
}
@@ -961,9 +1005,51 @@ && canApplyAt(hunkLines, newLines, 0)) {
}
}
if (!applies) {
- result.addError(JGitText.get().applyTextPatchCannotApplyHunk,
- fh.getOldPath(), hh);
- return null;
+ if (!allowConflicts) {
+ result.addError(
+ JGitText.get().applyTextPatchCannotApplyHunk,
+ fh.getOldPath(), hh);
+ return null;
+ }
+ // Insert conflict markers. This is best-guess because the
+ // file might have changed completely. But at least we give
+ // the user a graceful state that they can resolve manually.
+ // An alternative to this is using the 3-way merger. This
+ // only works if the pre-image SHA is contained in the repo.
+ // If that was the case, cherry-picking the original commit
+ // should be preferred to apply a patch.
+ result.addErrorWithGitConflict("cannot apply hunk", fh.getOldPath(), hh); //$NON-NLS-1$
+ newLines.add(Math.min(applyAt++, newLines.size()),
+ asBytes("<<<<<<< HEAD")); //$NON-NLS-1$
+ applyAt += hh.getOldImage().lineCount;
+ newLines.add(Math.min(applyAt++, newLines.size()),
+ asBytes("=======")); //$NON-NLS-1$
+
+ int sz = hunkLines.size();
+ for (int j = 1; j < sz; j++) {
+ ByteBuffer hunkLine = hunkLines.get(j);
+ if (!hunkLine.hasRemaining()) {
+ // Completely empty line; accept as empty context
+ // line
+ applyAt++;
+ lastWasRemoval = false;
+ continue;
+ }
+ switch (hunkLine.array()[hunkLine.position()]) {
+ case ' ':
+ case '+':
+ newLines.add(Math.min(applyAt++, newLines.size()),
+ slice(hunkLine, 1));
+ break;
+ case '-':
+ case '\\':
+ default:
+ break;
+ }
+ }
+ newLines.add(Math.min(applyAt++, newLines.size()),
+ asBytes(">>>>>>> PATCH")); //$NON-NLS-1$
+ continue;
}
// Hunk applies at applyAt. Apply it, and update afterLastHunk and
// lineNumberShift
@@ -1010,7 +1096,11 @@ && canApplyAt(hunkLines, newLines, 0)) {
} else if (!rt.isMissingNewlineAtEnd()) {
newLines.add(null);
}
+ return toContentStreamLoader(newLines);
+ }
+ private static ContentStreamLoader toContentStreamLoader(
+ List<ByteBuffer> newLines) throws IOException {
// We could check if old == new, but the short-circuiting complicates
// logic for inCore patching, so just write the new thing regardless.
TemporaryBuffer buffer = new TemporaryBuffer.LocalFile(null);
@@ -1034,6 +1124,10 @@ && canApplyAt(hunkLines, newLines, 0)) {
}
}
+ private ByteBuffer asBytes(String str) {
+ return ByteBuffer.wrap(str.getBytes(charset));
+ }
+
@SuppressWarnings("ByteBufferBackingArray")
private boolean canApplyAt(List<ByteBuffer> hunkLines,
List<ByteBuffer> newLines, int line) {
@@ -1123,4 +1217,4 @@ public void close() throws IOException {
in.close();
}
}
-}
+}
\ No newline at end of file
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java
index 82671d9..7c763bc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java
@@ -43,7 +43,7 @@
* Tree and blob objects reachable from interesting commits are automatically
* scheduled for inclusion in the results of {@link #nextObject()}, returning
* each object exactly once. Objects are sorted and returned according to the
- * the commits that reference them and the order they appear within a tree.
+ * commits that reference them and the order they appear within a tree.
* Ordering can be affected by changing the
* {@link org.eclipse.jgit.revwalk.RevSort} used to order the commits that are
* returned first.
@@ -164,29 +164,6 @@ private ObjectWalk(ObjectReader or, boolean closeReader) {
}
/**
- * Create an object reachability checker that will use bitmaps if possible.
- *
- * This reachability checker accepts any object as target. For checks
- * exclusively between commits, see
- * {@link RevWalk#createReachabilityChecker()}.
- *
- * @return an object reachability checker, using bitmaps if possible.
- *
- * @throws IOException
- * when the index fails to load.
- *
- * @since 5.8
- * @deprecated use
- * {@code ObjectReader#createObjectReachabilityChecker(ObjectWalk)}
- * instead.
- */
- @Deprecated
- public final ObjectReachabilityChecker createObjectReachabilityChecker()
- throws IOException {
- return reader.createObjectReachabilityChecker(this);
- }
-
- /**
* Mark an object or commit to start graph traversal from.
* <p>
* Callers are encouraged to use
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ReachabilityChecker.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ReachabilityChecker.java
index 1a869a0..5afb669 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ReachabilityChecker.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ReachabilityChecker.java
@@ -26,40 +26,6 @@
* @since 5.4
*/
public interface ReachabilityChecker {
-
- /**
- * Check if all targets are reachable from the {@code starters} commits.
- * <p>
- * Caller should parse the objectIds (preferably with
- * {@code walk.parseCommit()} and handle missing/incorrect type objects
- * before calling this method.
- *
- * @param targets
- * commits to reach.
- * @param starters
- * known starting points.
- * @return An unreachable target if at least one of the targets is
- * unreachable. An empty optional if all targets are reachable from
- * the starters.
- *
- * @throws MissingObjectException
- * if any of the incoming objects doesn't exist in the
- * repository.
- * @throws IncorrectObjectTypeException
- * if any of the incoming objects is not a commit or a tag.
- * @throws IOException
- * if any of the underlying indexes or readers can not be
- * opened.
- *
- * @deprecated see {{@link #areAllReachable(Collection, Stream)}
- */
- @Deprecated
- default Optional<RevCommit> areAllReachable(Collection<RevCommit> targets,
- Collection<RevCommit> starters) throws MissingObjectException,
- IncorrectObjectTypeException, IOException {
- return areAllReachable(targets, starters.stream());
- }
-
/**
* Check if all targets are reachable from the {@code starters} commits.
* <p>
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java
index 743a8cc..871545f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java
@@ -1,6 +1,6 @@
/*
- * Copyright (C) 2008-2009, Google Inc.
- * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
+ * Copyright (C) 2008, 2009 Google Inc.
+ * Copyright (C) 2008, 2024 Shawn O. Pearce <spearce@spearce.org> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -401,13 +401,13 @@ public RevCommit getParent(int nth) {
* @since 5.1
*/
public final byte[] getRawGpgSignature() {
- final byte[] raw = buffer;
- final byte[] header = { 'g', 'p', 'g', 's', 'i', 'g' };
- final int start = RawParseUtils.headerStart(header, raw, 0);
+ byte[] raw = buffer;
+ byte[] header = { 'g', 'p', 'g', 's', 'i', 'g' };
+ int start = RawParseUtils.headerStart(header, raw, 0);
if (start < 0) {
return null;
}
- final int end = RawParseUtils.headerEnd(raw, start);
+ int end = RawParseUtils.nextLfSkippingSplitLines(raw, start);
return RawParseUtils.headerValue(raw, start, end);
}
@@ -524,6 +524,30 @@ static boolean hasLF(byte[] r, int b, int e) {
}
/**
+ * Parse the commit message and return its first line, i.e., everything up
+ * to but not including the first newline, if any.
+ *
+ * @return the first line of the decoded commit message as a string; never
+ * {@code null}.
+ * @since 7.2
+ */
+ public final String getFirstMessageLine() {
+ int msgB = RawParseUtils.commitMessage(buffer, 0);
+ if (msgB < 0) {
+ return ""; //$NON-NLS-1$
+ }
+ int msgE = msgB;
+ byte[] raw = buffer;
+ while (msgE < raw.length && raw[msgE] != '\n') {
+ msgE++;
+ }
+ if (msgE > msgB && msgE > 0 && raw[msgE - 1] == '\r') {
+ msgE--;
+ }
+ return RawParseUtils.decode(guessEncoding(buffer), buffer, msgB, msgE);
+ }
+
+ /**
* Determine the encoding of the commit message buffer.
* <p>
* Locates the "encoding" header (if present) and returns its value. Due to
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevTag.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevTag.java
index 75dbd57..0737a78 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevTag.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevTag.java
@@ -38,8 +38,17 @@
*/
public class RevTag extends RevObject {
- private static final byte[] hSignature = Constants
- .encodeASCII("-----BEGIN PGP SIGNATURE-----"); //$NON-NLS-1$
+ private static final byte[] SIGNATURE_START = Constants
+ .encodeASCII("-----BEGIN"); //$NON-NLS-1$
+
+ private static final byte[] GPG_SIGNATURE_START = Constants
+ .encodeASCII(Constants.GPG_SIGNATURE_PREFIX);
+
+ private static final byte[] CMS_SIGNATURE_START = Constants
+ .encodeASCII(Constants.CMS_SIGNATURE_PREFIX);
+
+ private static final byte[] SSH_SIGNATURE_START = Constants
+ .encodeASCII(Constants.SSH_SIGNATURE_PREFIX);
/**
* Parse an annotated tag from its canonical format.
@@ -208,20 +217,27 @@ private int getSignatureStart() {
return msgB;
}
// Find the last signature start and return the rest
- int start = nextStart(hSignature, raw, msgB);
+ int start = nextStart(SIGNATURE_START, raw, msgB);
if (start < 0) {
return start;
}
int next = RawParseUtils.nextLF(raw, start);
while (next < raw.length) {
- int newStart = nextStart(hSignature, raw, next);
+ int newStart = nextStart(SIGNATURE_START, raw, next);
if (newStart < 0) {
break;
}
start = newStart;
next = RawParseUtils.nextLF(raw, start);
}
- return start;
+ // SIGNATURE_START is just a prefix. Check that it is one of the known
+ // full signature start tags.
+ if (RawParseUtils.match(raw, start, GPG_SIGNATURE_START) > 0
+ || RawParseUtils.match(raw, start, CMS_SIGNATURE_START) > 0
+ || RawParseUtils.match(raw, start, SSH_SIGNATURE_START) > 0) {
+ return start;
+ }
+ return -1;
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
index 76c14e9..41f98ba 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
@@ -19,9 +19,14 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
-import java.util.Optional;
+import java.util.Map;
+import java.util.
+Optional;
+import java.util.Set;
import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.annotations.Nullable;
@@ -31,9 +36,9 @@
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.RevWalkException;
import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.commitgraph.CommitGraph;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.AsyncObjectLoaderQueue;
-import org.eclipse.jgit.internal.storage.commitgraph.CommitGraph;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.MutableObjectId;
import org.eclipse.jgit.lib.NullProgressMonitor;
@@ -279,23 +284,6 @@ public ObjectReader getObjectReader() {
}
/**
- * Get a reachability checker for commits over this revwalk.
- *
- * @return the most efficient reachability checker for this repository.
- * @throws IOException
- * if it cannot open any of the underlying indices.
- *
- * @since 5.4
- * @deprecated use {@code ObjectReader#createReachabilityChecker(RevWalk)}
- * instead.
- */
- @Deprecated
- public final ReachabilityChecker createReachabilityChecker()
- throws IOException {
- return reader.createReachabilityChecker(this);
- }
-
- /**
* {@inheritDoc}
* <p>
* Release any resources used by this walker's reader.
@@ -540,6 +528,27 @@ public boolean isMergedIntoAny(RevCommit commit, Collection<Ref> refs)
}
/**
+ * Determine if a <code>commit</code> is merged into any of the given
+ * <code>revs</code>.
+ *
+ * @param commit
+ * commit the caller thinks is reachable from <code>revs</code>.
+ * @param revs
+ * commits to start iteration from, and which is most likely a
+ * descendant (child) of <code>commit</code>.
+ * @return true if commit is merged into any of the revs; false otherwise.
+ * @throws java.io.IOException
+ * a pack file or loose object could not be read.
+ * @since 6.10.1
+ */
+ public boolean isMergedIntoAnyCommit(RevCommit commit, Collection<RevCommit> revs)
+ throws IOException {
+ return getCommitsMergedInto(commit, revs,
+ GetMergedIntoStrategy.RETURN_ON_FIRST_FOUND,
+ NullProgressMonitor.INSTANCE).size() > 0;
+ }
+
+ /**
* Determine if a <code>commit</code> is merged into all of the given
* <code>refs</code>.
*
@@ -562,7 +571,26 @@ public boolean isMergedIntoAll(RevCommit commit, Collection<Ref> refs)
private List<Ref> getMergedInto(RevCommit needle, Collection<Ref> haystacks,
Enum returnStrategy, ProgressMonitor monitor) throws IOException {
+ Map<RevCommit, List<Ref>> refsByCommit = new HashMap<>();
+ for (Ref r : haystacks) {
+ RevObject o = peel(parseAny(r.getObjectId()));
+ if (!(o instanceof RevCommit)) {
+ continue;
+ }
+ refsByCommit.computeIfAbsent((RevCommit) o, c -> new ArrayList<>()).add(r);
+ }
+ monitor.update(1);
List<Ref> result = new ArrayList<>();
+ for (RevCommit c : getCommitsMergedInto(needle, refsByCommit.keySet(),
+ returnStrategy, monitor)) {
+ result.addAll(refsByCommit.get(c));
+ }
+ return result;
+ }
+
+ private Set<RevCommit> getCommitsMergedInto(RevCommit needle, Collection<RevCommit> haystacks,
+ Enum returnStrategy, ProgressMonitor monitor) throws IOException {
+ Set<RevCommit> result = new HashSet<>();
List<RevCommit> uninteresting = new ArrayList<>();
List<RevCommit> marked = new ArrayList<>();
RevFilter oldRF = filter;
@@ -578,16 +606,11 @@ private List<Ref> getMergedInto(RevCommit needle, Collection<Ref> haystacks,
needle.parseHeaders(this);
}
int cutoff = needle.getGeneration();
- for (Ref r : haystacks) {
+ for (RevCommit c : haystacks) {
if (monitor.isCancelled()) {
return result;
}
monitor.update(1);
- RevObject o = peel(parseAny(r.getObjectId()));
- if (!(o instanceof RevCommit)) {
- continue;
- }
- RevCommit c = (RevCommit) o;
reset(UNINTERESTING | TEMP_MARK);
markStart(c);
boolean commitFound = false;
@@ -599,7 +622,7 @@ private List<Ref> getMergedInto(RevCommit needle, Collection<Ref> haystacks,
}
if (References.isSameObject(next, needle)
|| (next.flags & TEMP_MARK) != 0) {
- result.add(r);
+ result.add(c);
if (returnStrategy == GetMergedIntoStrategy.RETURN_ON_FIRST_FOUND) {
return result;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/filter/CommitTimeRevFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/filter/CommitTimeRevFilter.java
index 4100e87..c9186b5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/filter/CommitTimeRevFilter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/filter/CommitTimeRevFilter.java
@@ -12,6 +12,7 @@
package org.eclipse.jgit.revwalk.filter;
import java.io.IOException;
+import java.time.Instant;
import java.util.Date;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
@@ -30,9 +31,24 @@ public abstract class CommitTimeRevFilter extends RevFilter {
* @param ts
* the point in time to cut on.
* @return a new filter to select commits on or before <code>ts</code>.
+ *
+ * @deprecated Use {@link #before(Instant)} instead.
*/
+ @Deprecated(since="7.2")
public static final RevFilter before(Date ts) {
- return before(ts.getTime());
+ return before(ts.toInstant());
+ }
+
+ /**
+ * Create a new filter to select commits before a given date/time.
+ *
+ * @param ts
+ * the point in time to cut on.
+ * @return a new filter to select commits on or before <code>ts</code>.
+ * @since 7.2
+ */
+ public static RevFilter before(Instant ts) {
+ return new Before(ts);
}
/**
@@ -43,7 +59,7 @@ public static final RevFilter before(Date ts) {
* @return a new filter to select commits on or before <code>ts</code>.
*/
public static final RevFilter before(long ts) {
- return new Before(ts);
+ return new Before(Instant.ofEpochMilli(ts));
}
/**
@@ -52,9 +68,24 @@ public static final RevFilter before(long ts) {
* @param ts
* the point in time to cut on.
* @return a new filter to select commits on or after <code>ts</code>.
+ *
+ * @deprecated Use {@link #after(Instant)} instead.
*/
+ @Deprecated(since="7.2")
public static final RevFilter after(Date ts) {
- return after(ts.getTime());
+ return after(ts.toInstant());
+ }
+
+ /**
+ * Create a new filter to select commits after a given date/time.
+ *
+ * @param ts
+ * the point in time to cut on.
+ * @return a new filter to select commits on or after <code>ts</code>.
+ * @since 7.2
+ */
+ public static RevFilter after(Instant ts) {
+ return new After(ts);
}
/**
@@ -65,7 +96,7 @@ public static final RevFilter after(Date ts) {
* @return a new filter to select commits on or after <code>ts</code>.
*/
public static final RevFilter after(long ts) {
- return new After(ts);
+ return after(Instant.ofEpochMilli(ts));
}
/**
@@ -75,9 +106,28 @@ public static final RevFilter after(long ts) {
* @param since the point in time to cut on.
* @param until the point in time to cut off.
* @return a new filter to select commits between the given date/times.
+ *
+ * @deprecated Use {@link #between(Instant, Instant)} instead.
*/
+ @Deprecated(since="7.2")
public static final RevFilter between(Date since, Date until) {
- return between(since.getTime(), until.getTime());
+ return between(since.toInstant(), until.toInstant());
+ }
+
+ /**
+ * Create a new filter to select commits after or equal a given date/time
+ * <code>since</code> and before or equal a given date/time
+ * <code>until</code>.
+ *
+ * @param since
+ * the point in time to cut on.
+ * @param until
+ * the point in time to cut off.
+ * @return a new filter to select commits between the given date/times.
+ * @since 7.2
+ */
+ public static RevFilter between(Instant since, Instant until) {
+ return new Between(since, until);
}
/**
@@ -87,9 +137,12 @@ public static final RevFilter between(Date since, Date until) {
* @param since the point in time to cut on, in milliseconds.
* @param until the point in time to cut off, in millisconds.
* @return a new filter to select commits between the given date/times.
+ *
+ * @deprecated Use {@link #between(Instant, Instant)} instead.
*/
+ @Deprecated(since="7.2")
public static final RevFilter between(long since, long until) {
- return new Between(since, until);
+ return new Between(Instant.ofEpochMilli(since), Instant.ofEpochMilli(until));
}
final int when;
@@ -98,6 +151,10 @@ public static final RevFilter between(long since, long until) {
when = (int) (ts / 1000);
}
+ CommitTimeRevFilter(Instant t) {
+ when = (int) t.getEpochSecond();
+ }
+
@Override
public RevFilter clone() {
return this;
@@ -109,8 +166,8 @@ public boolean requiresCommitBody() {
}
private static class Before extends CommitTimeRevFilter {
- Before(long ts) {
- super(ts);
+ Before(Instant t) {
+ super(t);
}
@Override
@@ -123,14 +180,12 @@ public boolean include(RevWalk walker, RevCommit cmit)
@SuppressWarnings("nls")
@Override
public String toString() {
- return super.toString() + "(" + new Date(when * 1000L) + ")";
+ return super.toString() + "(" + Instant.ofEpochSecond(when) + ")";
}
}
private static class After extends CommitTimeRevFilter {
- After(long ts) {
- super(ts);
- }
+ After(Instant t) { super(t); }
@Override
public boolean include(RevWalk walker, RevCommit cmit)
@@ -148,16 +203,16 @@ public boolean include(RevWalk walker, RevCommit cmit)
@SuppressWarnings("nls")
@Override
public String toString() {
- return super.toString() + "(" + new Date(when * 1000L) + ")";
+ return super.toString() + "(" + Instant.ofEpochSecond(when) + ")";
}
}
private static class Between extends CommitTimeRevFilter {
private final int until;
- Between(long since, long until) {
+ Between(Instant since, Instant until) {
super(since);
- this.until = (int) (until / 1000);
+ this.until = (int) until.getEpochSecond();
}
@Override
@@ -170,8 +225,8 @@ public boolean include(RevWalk walker, RevCommit cmit)
@SuppressWarnings("nls")
@Override
public String toString() {
- return super.toString() + "(" + new Date(when * 1000L) + " - "
- + new Date(until * 1000L) + ")";
+ return super.toString() + "(" + Instant.ofEpochSecond(when) + " - "
+ + Instant.ofEpochSecond(until) + ")";
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheStats.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheStats.java
index 7cb8618..668b92c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheStats.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheStats.java
@@ -22,27 +22,6 @@
*/
@MXBean
public interface WindowCacheStats {
- /**
- * Get number of open files
- *
- * @return the number of open files.
- * @deprecated use {@link #getOpenFileCount()} instead
- */
- @Deprecated
- public static int getOpenFiles() {
- return (int) WindowCache.getInstance().getStats().getOpenFileCount();
- }
-
- /**
- * Get number of open bytes
- *
- * @return the number of open bytes.
- * @deprecated use {@link #getOpenByteCount()} instead
- */
- @Deprecated
- public static long getOpenBytes() {
- return WindowCache.getInstance().getStats().getOpenByteCount();
- }
/**
* Get cache statistics
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java
index 8373d68..863b794 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java
@@ -50,7 +50,7 @@
import java.util.concurrent.TimeUnit;
import java.util.zip.Deflater;
-import org.eclipse.jgit.internal.storage.file.PackIndexWriter;
+import org.eclipse.jgit.internal.storage.file.BasePackIndexWriter;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Repository;
@@ -995,7 +995,7 @@ public void setExecutor(Executor executor) {
*
* @return the index version, the special version 0 designates the oldest
* (most compatible) format available for the objects.
- * @see PackIndexWriter
+ * @see BasePackIndexWriter
*/
public int getIndexVersion() {
return indexVersion;
@@ -1009,7 +1009,7 @@ public int getIndexVersion() {
* @param version
* the version to write. The special version 0 designates the
* oldest (most compatible) format available for the objects.
- * @see PackIndexWriter
+ * @see BasePackIndexWriter
*/
public void setIndexVersion(int version) {
indexVersion = version;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java
index becc808..105cba7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java
@@ -787,14 +787,14 @@ public IgnoreSubmoduleMode getModulesIgnore() throws IOException,
IgnoreSubmoduleMode mode = repoConfig.getEnum(
IgnoreSubmoduleMode.values(),
ConfigConstants.CONFIG_SUBMODULE_SECTION, getModuleName(),
- ConfigConstants.CONFIG_KEY_IGNORE, null);
+ ConfigConstants.CONFIG_KEY_IGNORE);
if (mode != null) {
return mode;
}
lazyLoadModulesConfig();
- return modulesConfig.getEnum(IgnoreSubmoduleMode.values(),
- ConfigConstants.CONFIG_SUBMODULE_SECTION, getModuleName(),
- ConfigConstants.CONFIG_KEY_IGNORE, IgnoreSubmoduleMode.NONE);
+ return modulesConfig.getEnum(ConfigConstants.CONFIG_SUBMODULE_SECTION,
+ getModuleName(), ConfigConstants.CONFIG_KEY_IGNORE,
+ IgnoreSubmoduleMode.NONE);
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java
index b873925..aaf9f8a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java
@@ -757,8 +757,10 @@ void list() throws IOException {
final XMLReader xr;
try {
- xr = SAXParserFactory.newInstance().newSAXParser()
- .getXMLReader();
+ SAXParserFactory saxParserFactory = SAXParserFactory
+ .newInstance();
+ saxParserFactory.setNamespaceAware(true);
+ xr = saxParserFactory.newSAXParser().getXMLReader();
} catch (SAXException | ParserConfigurationException e) {
throw new IOException(
JGitText.get().noXMLParserAvailable, e);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java
index 469a3d6..be0d37b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java
@@ -12,10 +12,10 @@
package org.eclipse.jgit.transport;
-import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DELIM;
import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DEEPEN;
import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DEEPEN_NOT;
import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DEEPEN_SINCE;
+import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DELIM;
import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DONE;
import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_END;
import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_ERR;
@@ -32,7 +32,6 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
-import java.util.Date;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
@@ -715,7 +714,7 @@ private void markReachable(Collection<Ref> want, Set<ObjectId> have,
// wind up later matching up against things we want and we
// can avoid asking for something we already happen to have.
//
- final Date maxWhen = new Date(maxTime * 1000L);
+ Instant maxWhen = Instant.ofEpochSecond(maxTime);
walk.sort(RevSort.COMMIT_TIME_DESC);
walk.markStart(reachableCommits);
walk.setRevFilter(CommitTimeRevFilter.after(maxWhen));
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpConfig.java
index 73eddb8..f10b7bf 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpConfig.java
@@ -302,8 +302,7 @@ private void init(Config config, URIish uri) {
int postBufferSize = config.getInt(HTTP, POST_BUFFER_KEY,
1 * 1024 * 1024);
boolean sslVerifyFlag = config.getBoolean(HTTP, SSL_VERIFY_KEY, true);
- HttpRedirectMode followRedirectsMode = config.getEnum(
- HttpRedirectMode.values(), HTTP, null,
+ HttpRedirectMode followRedirectsMode = config.getEnum(HTTP, null,
FOLLOW_REDIRECTS_KEY, HttpRedirectMode.INITIAL);
int redirectLimit = config.getInt(HTTP, MAX_REDIRECTS_KEY,
MAX_REDIRECTS);
@@ -335,8 +334,8 @@ private void init(Config config, URIish uri) {
postBufferSize);
sslVerifyFlag = config.getBoolean(HTTP, match, SSL_VERIFY_KEY,
sslVerifyFlag);
- followRedirectsMode = config.getEnum(HttpRedirectMode.values(),
- HTTP, match, FOLLOW_REDIRECTS_KEY, followRedirectsMode);
+ followRedirectsMode = config.getEnum(HTTP, match,
+ FOLLOW_REDIRECTS_KEY, followRedirectsMode);
int newMaxRedirects = config.getInt(HTTP, match, MAX_REDIRECTS_KEY,
redirectLimit);
if (newMaxRedirects >= 0) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java
index 7224405..e1f2b19 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java
@@ -530,7 +530,7 @@ public PackLock parse(ProgressMonitor receiving, ProgressMonitor resolving)
receiving.beginTask(JGitText.get().receivingObjects,
(int) expectedObjectCount);
try {
- for (int done = 0; done < expectedObjectCount; done++) {
+ for (long done = 0; done < expectedObjectCount; done++) {
indexOneObject();
receiving.update(1);
if (receiving.isCancelled())
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineIn.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineIn.java
index ed33eae..614ad88 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineIn.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineIn.java
@@ -43,24 +43,13 @@ public class PacketLineIn {
/**
* Magic return from {@link #readString()} when a flush packet is found.
- *
- * @deprecated Callers should use {@link #isEnd(String)} to check if a
- * string is the end marker, or
- * {@link PacketLineIn#readStrings()} to iterate over all
- * strings in the input stream until the marker is reached.
*/
- @Deprecated
- public static final String END = new String(); /* must not string pool */
+ private static final String END = new String(); /* must not string pool */
/**
* Magic return from {@link #readString()} when a delim packet is found.
- *
- * @since 5.0
- * @deprecated Callers should use {@link #isDelimiter(String)} to check if a
- * string is the delimiter.
*/
- @Deprecated
- public static final String DELIM = new String(); /* must not string pool */
+ private static final String DELIM = new String(); /* must not string pool */
enum AckNackResult {
/** NAK */
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java
index a9e93b6..6bdaf0e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java
@@ -24,6 +24,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -329,7 +330,7 @@ public RefUpdate.Result save() throws IOException {
if (newId == null) {
return RefUpdate.Result.NO_CHANGE;
}
- try (ObjectInserter inserter = db.newObjectInserter()) {
+ try {
RefUpdate.Result result = updateRef(newId);
switch (result) {
case FAST_FORWARD:
@@ -404,8 +405,8 @@ private ObjectId write() throws IOException {
}
private static void sortPending(List<PendingCert> pending) {
- Collections.sort(pending, (PendingCert a, PendingCert b) -> Long.signum(
- a.ident.getWhen().getTime() - b.ident.getWhen().getTime()));
+ Collections.sort(pending,
+ Comparator.comparing((PendingCert a) -> a.ident.getWhenAsInstant()));
}
private DirCache newDirCache() throws IOException {
@@ -503,7 +504,7 @@ private static String buildMessage(PushCertificate cert) {
} else {
sb.append(MessageFormat.format(
JGitText.get().storePushCertMultipleRefs,
- Integer.valueOf(cert.getCommands().size())));
+ cert.getCommands().size()));
}
return sb.append('\n').toString();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
index ddde603..6f211e0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
@@ -88,52 +88,6 @@
* Implements the server side of a push connection, receiving objects.
*/
public class ReceivePack {
- /**
- * Data in the first line of a request, the line itself plus capabilities.
- *
- * @deprecated Use {@link FirstCommand} instead.
- * @since 5.6
- */
- @Deprecated
- public static class FirstLine {
- private final FirstCommand command;
-
- /**
- * Parse the first line of a receive-pack request.
- *
- * @param line
- * line from the client.
- */
- public FirstLine(String line) {
- command = FirstCommand.fromLine(line);
- }
-
- /**
- * Get non-capabilities part of the line
- *
- * @return non-capabilities part of the line.
- */
- public String getLine() {
- return command.getLine();
- }
-
- /**
- * Get capabilities parsed from the line
- *
- * @return capabilities parsed from the line.
- */
- public Set<String> getCapabilities() {
- Set<String> reconstructedCapabilites = new HashSet<>();
- for (Map.Entry<String, String> e : command.getCapabilities()
- .entrySet()) {
- String cap = e.getValue() == null ? e.getKey()
- : e.getKey() + "=" + e.getValue(); //$NON-NLS-1$
- reconstructedCapabilites.add(cap);
- }
-
- return reconstructedCapabilites;
- }
- }
/** Database we write the stored objects into. */
private final Repository db;
@@ -2149,22 +2103,6 @@ public void setUnpackErrorHandler(UnpackErrorHandler unpackErrorHandler) {
}
/**
- * Set whether this class will report command failures as warning messages
- * before sending the command results.
- *
- * @param echo
- * if true this class will report command failures as warning
- * messages before sending the command results. This is usually
- * not necessary, but may help buggy Git clients that discard the
- * errors when all branches fail.
- * @deprecated no widely used Git versions need this any more
- */
- @Deprecated
- public void setEchoCommandFailures(boolean echo) {
- // No-op.
- }
-
- /**
* Get the client session-id
*
* @return The client session-id.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java
index f72c421..3d4bea2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java
@@ -178,7 +178,6 @@ public void setUseProtocolV2(boolean b) {
*
* This method must be invoked prior to any of the following:
* <ul>
- * <li>{@link #send(Map)}</li>
* <li>{@link #send(Collection)}</li>
* </ul>
*
@@ -195,7 +194,6 @@ public void setDerefTags(boolean deref) {
* <p>
* This method must be invoked prior to any of the following:
* <ul>
- * <li>{@link #send(Map)}</li>
* <li>{@link #send(Collection)}</li>
* <li>{@link #advertiseHave(AnyObjectId)}</li>
* </ul>
@@ -230,7 +228,6 @@ public void advertiseCapability(String name, String value) {
* <p>
* This method must be invoked prior to any of the following:
* <ul>
- * <li>{@link #send(Map)}</li>
* <li>{@link #send(Collection)}</li>
* <li>{@link #advertiseHave(AnyObjectId)}</li>
* </ul>
@@ -260,24 +257,6 @@ public void addSymref(String from, String to) {
* @throws java.io.IOException
* the underlying output stream failed to write out an
* advertisement record.
- * @deprecated use {@link #send(Collection)} instead.
- */
- @Deprecated
- public Set<ObjectId> send(Map<String, Ref> refs) throws IOException {
- return send(refs.values());
- }
-
- /**
- * Format an advertisement for the supplied refs.
- *
- * @param refs
- * zero or more refs to format for the client. The collection is
- * sorted before display if necessary, and therefore may appear
- * in any order.
- * @return set of ObjectIds that were advertised to the client.
- * @throws java.io.IOException
- * the underlying output stream failed to write out an
- * advertisement record.
* @since 5.0
*/
public Set<ObjectId> send(Collection<Ref> refs) throws IOException {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java
index a0194ea..8120df0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java
@@ -11,8 +11,6 @@
package org.eclipse.jgit.transport;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
import java.util.Iterator;
import java.util.ServiceLoader;
@@ -99,9 +97,8 @@ public static void setInstance(SshSessionFactory newFactory) {
* @since 5.2
*/
public static String getLocalUserName() {
- return AccessController
- .doPrivileged((PrivilegedAction<String>) () -> SystemReader
- .getInstance().getProperty(Constants.OS_USER_NAME_KEY));
+ return SystemReader.getInstance()
+ .getProperty(Constants.OS_USER_NAME_KEY);
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
index b335675..ac76e83 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
@@ -1121,28 +1121,6 @@ public void setRemoveDeletedRefs(boolean remove) {
}
/**
- * @return the blob limit value set with {@link #setFilterBlobLimit} or
- * {@link #setFilterSpec(FilterSpec)}, or -1 if no blob limit value
- * was set
- * @since 5.0
- * @deprecated Use {@link #getFilterSpec()} instead
- */
- @Deprecated
- public final long getFilterBlobLimit() {
- return filterSpec.getBlobLimit();
- }
-
- /**
- * @param bytes exclude blobs of size greater than this
- * @since 5.0
- * @deprecated Use {@link #setFilterSpec(FilterSpec)} instead
- */
- @Deprecated
- public final void setFilterBlobLimit(long bytes) {
- setFilterSpec(FilterSpec.withBlobLimit(bytes));
- }
-
- /**
* Get filter spec
*
* @return the last filter spec set with {@link #setFilterSpec(FilterSpec)},
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java
index 0fc9710..f77b041 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java
@@ -254,6 +254,12 @@ private ProcessBuilder createProcess(List<String> args,
pb.environment().put(Constants.GIT_DIR_KEY,
directory.getPath());
}
+ File commonDirectory = local != null ? local.getCommonDirectory()
+ : null;
+ if (commonDirectory != null) {
+ pb.environment().put(Constants.GIT_COMMON_DIR_KEY,
+ commonDirectory.getPath());
+ }
return pb;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java
index 3a06ce5..1b9431c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java
@@ -225,6 +225,7 @@ private Process spawn(String cmd,
env.remove("GIT_CONFIG"); //$NON-NLS-1$
env.remove("GIT_CONFIG_PARAMETERS"); //$NON-NLS-1$
env.remove("GIT_DIR"); //$NON-NLS-1$
+ env.remove("GIT_COMMON_DIR"); //$NON-NLS-1$
env.remove("GIT_WORK_TREE"); //$NON-NLS-1$
env.remove("GIT_GRAFT_FILE"); //$NON-NLS-1$
env.remove("GIT_INDEX_FILE"); //$NON-NLS-1$
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java
index 4de6ff8..7b5842b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java
@@ -82,7 +82,7 @@ public class URIish implements Serializable {
* Part of a pattern which matches a relative path. Relative paths don't
* start with slash or drive letters. Defines no capturing group.
*/
- private static final String RELATIVE_PATH_P = "(?:(?:[^\\\\/]+[\\\\/]+)*[^\\\\/]+[\\\\/]*)"; //$NON-NLS-1$
+ private static final String RELATIVE_PATH_P = "(?:(?:[^\\\\/]+[\\\\/]+)*+[^\\\\/]*)"; //$NON-NLS-1$
/**
* Part of a pattern which matches a relative or absolute path. Defines no
@@ -120,7 +120,7 @@ public class URIish implements Serializable {
* path (maybe even containing windows drive-letters) or a relative path.
*/
private static final Pattern LOCAL_FILE = Pattern.compile("^" // //$NON-NLS-1$
- + "([\\\\/]?" + PATH_P + ")" // //$NON-NLS-1$ //$NON-NLS-2$
+ + "([\\\\/]?+" + PATH_P + ")" // //$NON-NLS-1$ //$NON-NLS-2$
+ "$"); //$NON-NLS-1$
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
index 9318871..41ab8ac 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
@@ -30,11 +30,11 @@
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_NO_DONE;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_NO_PROGRESS;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_OFS_DELTA;
+import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SESSION_ID;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SHALLOW;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDEBAND_ALL;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND_64K;
-import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SESSION_ID;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_THIN_PACK;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_WAIT_FOR_DONE;
import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_ACK;
@@ -80,7 +80,6 @@
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.pack.CachedPackUriProvider;
import org.eclipse.jgit.internal.storage.pack.PackWriter;
-import org.eclipse.jgit.internal.transport.parser.FirstWant;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
@@ -118,13 +117,13 @@ public class UploadPack implements Closeable {
/** Policy the server uses to validate client requests */
public enum RequestPolicy {
/** Client may only ask for objects the server advertised a reference for. */
- ADVERTISED,
+ ADVERTISED(0x08),
/**
* Client may ask for any commit reachable from a reference advertised by
* the server.
*/
- REACHABLE_COMMIT,
+ REACHABLE_COMMIT(0x02),
/**
* Client may ask for objects that are the tip of any reference, even if not
@@ -134,18 +133,36 @@ public enum RequestPolicy {
*
* @since 3.1
*/
- TIP,
+ TIP(0x01),
/**
* Client may ask for any commit reachable from any reference, even if that
- * reference wasn't advertised.
+ * reference wasn't advertised, implies REACHABLE_COMMIT and TIP.
*
* @since 3.1
*/
- REACHABLE_COMMIT_TIP,
+ REACHABLE_COMMIT_TIP(0x03),
- /** Client may ask for any SHA-1 in the repository. */
- ANY;
+ /** Client may ask for any SHA-1 in the repository, implies REACHABLE_COMMIT_TIP. */
+ ANY(0x07);
+
+ private final int bitmask;
+
+ RequestPolicy(int bitmask) {
+ this.bitmask = bitmask;
+ }
+
+ /**
+ * Check if the current policy implies another, based on its bitmask.
+ *
+ * @param implied
+ * the implied policy based on its bitmask.
+ * @return true if the policy is implied.
+ * @since 6.10.1
+ */
+ public boolean implies(RequestPolicy implied) {
+ return (bitmask & implied.bitmask) != 0;
+ }
}
/**
@@ -172,52 +189,6 @@ void checkWants(UploadPack up, List<ObjectId> wants)
throws PackProtocolException, IOException;
}
- /**
- * Data in the first line of a want-list, the line itself plus options.
- *
- * @deprecated Use {@link FirstWant} instead
- */
- @Deprecated
- public static class FirstLine {
-
- private final FirstWant firstWant;
-
- /**
- * @param line
- * line from the client.
- */
- public FirstLine(String line) {
- try {
- firstWant = FirstWant.fromLine(line);
- } catch (PackProtocolException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- /**
- * Get non-capabilities part of the line
- *
- * @return non-capabilities part of the line.
- */
- public String getLine() {
- return firstWant.getLine();
- }
-
- /**
- * Get capabilities parsed from the line
- *
- * @return capabilities parsed from the line.
- */
- public Set<String> getOptions() {
- if (firstWant.getAgent() != null) {
- Set<String> caps = new HashSet<>(firstWant.getCapabilities());
- caps.add(OPTION_AGENT + '=' + firstWant.getAgent());
- return caps;
- }
- return firstWant.getCapabilities();
- }
- }
-
/*
* {@link java.util.function.Consumer} doesn't allow throwing checked
* exceptions. Define our own to propagate IOExceptions.
@@ -1423,6 +1394,7 @@ private List<String> getV2CapabilityAdvertisement() {
if (transferConfig.isAdvertiseObjectInfo()) {
caps.add(COMMAND_OBJECT_INFO);
}
+ caps.add(OPTION_AGENT + "=" + UserAgent.get());
return caps;
}
@@ -1629,13 +1601,9 @@ public void sendAdvertisedRefs(RefAdvertiser adv,
if (!biDirectionalPipe)
adv.advertiseCapability(OPTION_NO_DONE);
RequestPolicy policy = getRequestPolicy();
- if (policy == RequestPolicy.TIP
- || policy == RequestPolicy.REACHABLE_COMMIT_TIP
- || policy == null)
+ if (policy == null || policy.implies(RequestPolicy.TIP))
adv.advertiseCapability(OPTION_ALLOW_TIP_SHA1_IN_WANT);
- if (policy == RequestPolicy.REACHABLE_COMMIT
- || policy == RequestPolicy.REACHABLE_COMMIT_TIP
- || policy == null)
+ if (policy == null || policy.implies(RequestPolicy.REACHABLE_COMMIT))
adv.advertiseCapability(OPTION_ALLOW_REACHABLE_SHA1_IN_WANT);
adv.advertiseCapability(OPTION_AGENT, UserAgent.get());
if (transferConfig.isAllowFilter()) {
@@ -1693,18 +1661,6 @@ public int getDepth() {
}
/**
- * Deprecated synonym for {@code getFilterSpec().getBlobLimit()}.
- *
- * @return filter blob limit requested by the client, or -1 if no limit
- * @since 5.3
- * @deprecated Use {@link #getFilterSpec()} instead
- */
- @Deprecated
- public final long getFilterBlobLimit() {
- return getFilterSpec().getBlobLimit();
- }
-
- /**
* Returns the filter spec for the current request. Valid only after
* calling recvWants(). This may be a no-op filter spec, but it won't be
* null.
@@ -1996,10 +1952,9 @@ public static final class AdvertisedRequestValidator
@Override
public void checkWants(UploadPack up, List<ObjectId> wants)
throws PackProtocolException, IOException {
- if (!up.isBiDirectionalPipe())
+ if (!up.isBiDirectionalPipe() || !wants.isEmpty()) {
new ReachableCommitRequestValidator().checkWants(up, wants);
- else if (!wants.isEmpty())
- throw new WantNotValidException(wants.iterator().next());
+ }
}
}
@@ -2271,7 +2226,7 @@ private boolean wantSatisfied(RevObject want) throws IOException {
walk.resetRetain(SAVE);
walk.markStart((RevCommit) want);
if (oldestTime != 0)
- walk.setRevFilter(CommitTimeRevFilter.after(oldestTime * 1000L));
+ walk.setRevFilter(CommitTimeRevFilter.after(Instant.ofEpochSecond(oldestTime)));
for (;;) {
final RevCommit c = walk.next();
if (c == null)
@@ -2429,7 +2384,8 @@ else if (ref.getName().startsWith(Constants.R_HEADS))
: req.getDepth() - 1;
pw.setShallowPack(req.getDepth(), unshallowCommits);
- // Ownership is transferred below
+ // dw borrows the reader from walk which is closed by #close
+ @SuppressWarnings("resource")
DepthWalk.RevWalk dw = new DepthWalk.RevWalk(
walk.getObjectReader(), walkDepth);
dw.setDeepenSince(req.getDeepenSince());
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UserAgent.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UserAgent.java
index 7b052ad..b23ee97 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UserAgent.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UserAgent.java
@@ -10,10 +10,6 @@
package org.eclipse.jgit.transport;
-import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT;
-
-import java.util.Set;
-
import org.eclipse.jgit.util.StringUtils;
/**
@@ -91,43 +87,6 @@ public static void set(String agent) {
userAgent = StringUtils.isEmptyOrNull(agent) ? null : clean(agent);
}
- /**
- *
- * @param options
- * options
- * @param transportAgent
- * name of transport agent
- * @return The transport agent.
- * @deprecated Capabilities with <key>=<value> shape are now
- * parsed alongside other capabilities in the ReceivePack flow.
- */
- @Deprecated
- static String getAgent(Set<String> options, String transportAgent) {
- if (options == null || options.isEmpty()) {
- return transportAgent;
- }
- for (String o : options) {
- if (o.startsWith(OPTION_AGENT)
- && o.length() > OPTION_AGENT.length()
- && o.charAt(OPTION_AGENT.length()) == '=') {
- return o.substring(OPTION_AGENT.length() + 1);
- }
- }
- return transportAgent;
- }
-
- /**
- *
- * @param options
- * options
- * @return True if the transport agent is set. False otherwise.
- * @deprecated Capabilities with <key>=<value> shape are now
- * parsed alongside other capabilities in the ReceivePack flow.
- */
- @Deprecated
- static boolean hasAgent(Set<String> options) {
- return getAgent(options, null) != null;
- }
private UserAgent() {
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java
index 3da76f3..b7bb0cb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java
@@ -22,8 +22,10 @@
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.Iterator;
+import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import org.eclipse.jgit.errors.CompoundException;
@@ -122,7 +124,7 @@ class WalkFetchConnection extends BaseFetchConnection {
private final Deque<WalkRemoteObjectDatabase> noAlternatesYet;
/** Packs we have discovered, but have not yet fetched locally. */
- private final Deque<RemotePack> unfetchedPacks;
+ private final Map<String, RemotePack> unfetchedPacks;
/**
* Packs whose indexes we have looked at in {@link #unfetchedPacks}.
@@ -164,7 +166,7 @@ class WalkFetchConnection extends BaseFetchConnection {
remotes = new ArrayList<>();
remotes.add(w);
- unfetchedPacks = new ArrayDeque<>();
+ unfetchedPacks = new LinkedHashMap<>();
packsConsidered = new HashSet<>();
noPacksYet = new ArrayDeque<>();
@@ -227,7 +229,7 @@ public void setPackLockMessage(String message) {
public void close() {
inserter.close();
reader.close();
- for (RemotePack p : unfetchedPacks) {
+ for (RemotePack p : unfetchedPacks.values()) {
if (p.tmpIdx != null)
p.tmpIdx.delete();
}
@@ -422,8 +424,9 @@ private void downloadObject(ProgressMonitor pm, AnyObjectId id)
if (packNameList == null || packNameList.isEmpty())
continue;
for (String packName : packNameList) {
- if (packsConsidered.add(packName))
- unfetchedPacks.add(new RemotePack(wrr, packName));
+ if (packsConsidered.add(packName)) {
+ unfetchedPacks.put(packName, new RemotePack(wrr, packName));
+ }
}
if (downloadPackedObject(pm, id))
return;
@@ -466,15 +469,27 @@ private boolean alreadyHave(AnyObjectId id) throws TransportException {
}
}
+ private boolean downloadPackedObject(ProgressMonitor monitor,
+ AnyObjectId id) throws TransportException {
+ Set<String> brokenPacks = new HashSet<>();
+ try {
+ return downloadPackedObject(monitor, id, brokenPacks);
+ } finally {
+ brokenPacks.forEach(unfetchedPacks::remove);
+ }
+ }
+
@SuppressWarnings("Finally")
private boolean downloadPackedObject(final ProgressMonitor monitor,
- final AnyObjectId id) throws TransportException {
+ final AnyObjectId id, Set<String> brokenPacks) throws TransportException {
// Search for the object in a remote pack whose index we have,
// but whose pack we do not yet have.
//
- final Iterator<RemotePack> packItr = unfetchedPacks.iterator();
- while (packItr.hasNext() && !monitor.isCancelled()) {
- final RemotePack pack = packItr.next();
+ for (Entry<String, RemotePack> entry : unfetchedPacks.entrySet()) {
+ if (monitor.isCancelled()) {
+ break;
+ }
+ final RemotePack pack = entry.getValue();
try {
pack.openIndex(monitor);
} catch (IOException err) {
@@ -484,7 +499,7 @@ private boolean downloadPackedObject(final ProgressMonitor monitor,
// another source, so don't consider it a failure.
//
recordError(id, err);
- packItr.remove();
+ brokenPacks.add(entry.getKey());
continue;
}
@@ -535,7 +550,7 @@ private boolean downloadPackedObject(final ProgressMonitor monitor,
}
throw new TransportException(e.getMessage(), e);
}
- packItr.remove();
+ brokenPacks.add(entry.getKey());
}
if (!alreadyHave(id)) {
@@ -550,11 +565,9 @@ private boolean downloadPackedObject(final ProgressMonitor monitor,
// Complete any other objects that we can.
//
- final Iterator<ObjectId> pending = swapFetchQueue();
- while (pending.hasNext()) {
- final ObjectId p = pending.next();
+ final Deque<ObjectId> pending = swapFetchQueue();
+ for (ObjectId p : pending) {
if (pack.index.hasObject(p)) {
- pending.remove();
process(p);
} else {
workQueue.add(p);
@@ -566,8 +579,8 @@ private boolean downloadPackedObject(final ProgressMonitor monitor,
return false;
}
- private Iterator<ObjectId> swapFetchQueue() {
- final Iterator<ObjectId> r = workQueue.iterator();
+ private Deque<ObjectId> swapFetchQueue() {
+ final Deque<ObjectId> r = workQueue;
workQueue = new ArrayDeque<>();
return r;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnection.java
index 125ee6c..95b8221 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnection.java
@@ -36,29 +36,39 @@
*/
public interface HttpConnection {
/**
+ * HttpURLConnection#HTTP_OK
+ *
* @see HttpURLConnection#HTTP_OK
*/
int HTTP_OK = java.net.HttpURLConnection.HTTP_OK;
/**
+ * HttpURLConnection#HTTP_NOT_AUTHORITATIVE
+ *
* @see HttpURLConnection#HTTP_NOT_AUTHORITATIVE
* @since 5.8
*/
int HTTP_NOT_AUTHORITATIVE = java.net.HttpURLConnection.HTTP_NOT_AUTHORITATIVE;
/**
+ * HttpURLConnection#HTTP_MOVED_PERM
+ *
* @see HttpURLConnection#HTTP_MOVED_PERM
* @since 4.7
*/
int HTTP_MOVED_PERM = java.net.HttpURLConnection.HTTP_MOVED_PERM;
/**
+ * HttpURLConnection#HTTP_MOVED_TEMP
+ *
* @see HttpURLConnection#HTTP_MOVED_TEMP
* @since 4.9
*/
int HTTP_MOVED_TEMP = java.net.HttpURLConnection.HTTP_MOVED_TEMP;
/**
+ * HttpURLConnection#HTTP_SEE_OTHER
+ *
* @see HttpURLConnection#HTTP_SEE_OTHER
* @since 4.9
*/
@@ -85,16 +95,22 @@ public interface HttpConnection {
int HTTP_11_MOVED_PERM = 308;
/**
+ * HttpURLConnection#HTTP_NOT_FOUND
+ *
* @see HttpURLConnection#HTTP_NOT_FOUND
*/
int HTTP_NOT_FOUND = java.net.HttpURLConnection.HTTP_NOT_FOUND;
/**
+ * HttpURLConnection#HTTP_UNAUTHORIZED
+ *
* @see HttpURLConnection#HTTP_UNAUTHORIZED
*/
int HTTP_UNAUTHORIZED = java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
/**
+ * HttpURLConnection#HTTP_FORBIDDEN
+ *
* @see HttpURLConnection#HTTP_FORBIDDEN
*/
int HTTP_FORBIDDEN = java.net.HttpURLConnection.HTTP_FORBIDDEN;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java
index 36fa720..0cac374 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java
@@ -371,12 +371,6 @@ public long getLength() {
return attributes.getLength();
}
- @Override
- @Deprecated
- public long getLastModified() {
- return attributes.getLastModifiedInstant().toEpochMilli();
- }
-
/**
* @since 5.1.9
*/
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java
index aaac2a7..31c216b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java
@@ -38,12 +38,12 @@
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.MutableObjectId;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.treewalk.filter.PathFilter;
import org.eclipse.jgit.treewalk.filter.TreeFilter;
@@ -1587,10 +1587,16 @@ public String getSmudgeCommand(Attributes attributes) throws IOException {
*/
private String getFilterCommandDefinition(String filterDriverName,
String filterCommandType) {
+ if (config == null) {
+ return null;
+ }
String key = filterDriverName + "." + filterCommandType; //$NON-NLS-1$
String filterCommand = filterCommandsByNameDotType.get(key);
if (filterCommand != null)
return filterCommand;
+ if (config == null) {
+ return null;
+ }
filterCommand = config.getString(ConfigConstants.CONFIG_FILTER_SECTION,
filterDriverName, filterCommandType);
boolean useBuiltin = config.getBoolean(
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
index 73a3dda..f16d800 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
@@ -498,6 +498,8 @@ private InputStream filterClean(InputStream in)
filterProcessBuilder.directory(repository.getWorkTree());
filterProcessBuilder.environment().put(Constants.GIT_DIR_KEY,
repository.getDirectory().getAbsolutePath());
+ filterProcessBuilder.environment().put(Constants.GIT_COMMON_DIR_KEY,
+ repository.getCommonDirectory().getAbsolutePath());
ExecutionResult result;
try {
result = fs.execute(filterProcessBuilder, in);
@@ -620,18 +622,6 @@ public long getEntryContentLength() throws IOException {
/**
* Get the last modified time of this entry.
*
- * @return last modified time of this file, in milliseconds since the epoch
- * (Jan 1, 1970 UTC).
- * @deprecated use {@link #getEntryLastModifiedInstant()} instead
- */
- @Deprecated
- public long getEntryLastModified() {
- return current().getLastModified();
- }
-
- /**
- * Get the last modified time of this entry.
- *
* @return last modified time of this file
* @since 5.1.9
*/
@@ -1229,21 +1219,6 @@ public String toString() {
* needs to compute the value they should cache the reference within an
* instance member instead.
*
- * @return time since the epoch (in ms) of the last change.
- * @deprecated use {@link #getLastModifiedInstant()} instead
- */
- @Deprecated
- public abstract long getLastModified();
-
- /**
- * Get the last modified time of this entry.
- * <p>
- * <b>Note: Efficient implementation required.</b>
- * <p>
- * The implementation of this method must be efficient. If a subclass
- * needs to compute the value they should cache the reference within an
- * instance member instead.
- *
* @return time of the last change.
* @since 5.1.9
*/
@@ -1332,7 +1307,7 @@ IgnoreNode load(IgnoreNode parent) throws IOException {
IgnoreNode infoExclude = new IgnoreNodeWithParent(
coreExclude);
- File exclude = fs.resolve(repository.getDirectory(),
+ File exclude = fs.resolve(repository.getCommonDirectory(),
Constants.INFO_EXCLUDE);
if (fs.exists(exclude)) {
loadRulesFromFile(infoExclude, exclude);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/ByteArraySet.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/ByteArraySet.java
index bcf79a2..33db6ea 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/ByteArraySet.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/ByteArraySet.java
@@ -13,12 +13,12 @@
package org.eclipse.jgit.treewalk.filter;
-import org.eclipse.jgit.util.RawParseUtils;
-
import java.util.Arrays;
import java.util.Set;
import java.util.stream.Collectors;
+import org.eclipse.jgit.util.RawParseUtils;
+
/**
* Specialized set for byte arrays, interpreted as strings for use in
* {@link PathFilterGroup.Group}. Most methods assume the hash is already know
@@ -141,13 +141,19 @@ boolean contains(byte[] toFind, int length, int hash) {
}
/**
+ * Returns number of arrays in the set
+ *
* @return number of arrays in the set
*/
int size() {
return size;
}
- /** @return true if {@link #size()} is 0. */
+ /**
+ * Returns true if {@link #size()} is 0
+ *
+ * @return true if {@link #size()} is 0
+ */
boolean isEmpty() {
return size == 0;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/ChangeIdUtil.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/ChangeIdUtil.java
index 12af374..c8421d6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/ChangeIdUtil.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/ChangeIdUtil.java
@@ -86,8 +86,8 @@ public static ObjectId computeChangeId(final ObjectId treeId,
}
}
- private static final Pattern issuePattern = Pattern
- .compile("^(Bug|Issue)[a-zA-Z0-9-]*:.*$"); //$NON-NLS-1$
+ private static final Pattern signedOffByPattern = Pattern
+ .compile("^Signed-off-by:.*$"); //$NON-NLS-1$
private static final Pattern footerPattern = Pattern
.compile("(^[a-zA-Z0-9-]+:(?!//).*$)"); //$NON-NLS-1$
@@ -159,7 +159,7 @@ public static String insertId(String message, ObjectId changeId,
int footerFirstLine = indexOfFirstFooterLine(lines);
int insertAfter = footerFirstLine;
for (int i = footerFirstLine; i < lines.length; ++i) {
- if (issuePattern.matcher(lines[i]).matches()) {
+ if (!signedOffByPattern.matcher(lines[i]).matches()) {
insertAfter = i + 1;
continue;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
index a8e1dae..59bbacf 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
@@ -30,7 +30,6 @@
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
-import java.security.AccessControlException;
import java.text.MessageFormat;
import java.time.Duration;
import java.time.Instant;
@@ -262,31 +261,6 @@ public static final class FileStoreAttributes {
private static final AtomicInteger threadNumber = new AtomicInteger(1);
/**
- * Don't use the default thread factory of the ForkJoinPool for the
- * CompletableFuture; it runs without any privileges, which causes
- * trouble if a SecurityManager is present.
- * <p>
- * Instead use normal daemon threads. They'll belong to the
- * SecurityManager's thread group, or use the one of the calling thread,
- * as appropriate.
- * </p>
- *
- * @see java.util.concurrent.Executors#newCachedThreadPool()
- */
- private static final ExecutorService FUTURE_RUNNER = new ThreadPoolExecutor(
- 5, 5, 30L, TimeUnit.SECONDS,
- new LinkedBlockingQueue<>(),
- runnable -> {
- Thread t = new Thread(runnable,
- "JGit-FileStoreAttributeReader-" //$NON-NLS-1$
- + threadNumber.getAndIncrement());
- // Make sure these threads don't prevent application/JVM
- // shutdown.
- t.setDaemon(true);
- return t;
- });
-
- /**
* Use a separate executor with at most one thread to synchronize
* writing to the config. We write asynchronously since the config
* itself might be on a different file system, which might otherwise
@@ -463,7 +437,7 @@ private static FileStoreAttributes getFileStoreAttributes(Path dir) {
locks.remove(s);
}
return attributes;
- }, FUTURE_RUNNER);
+ });
f = f.exceptionally(e -> {
LOG.error(e.getLocalizedMessage(), e);
return Optional.empty();
@@ -898,21 +872,6 @@ public static FS detect() {
}
/**
- * Whether FileStore attributes should be determined asynchronously
- *
- * @param asynch
- * whether FileStore attributes should be determined
- * asynchronously. If false access to cached attributes may block
- * for some seconds for the first call per FileStore
- * @since 5.1.9
- * @deprecated Use {@link FileStoreAttributes#setBackground} instead
- */
- @Deprecated
- public static void setAsyncFileStoreAttributes(boolean asynch) {
- FileStoreAttributes.setBackground(asynch);
- }
-
- /**
* Auto-detect the appropriate file system abstraction, taking into account
* the presence of a Cygwin installation on the system. Using jgit in
* combination with Cygwin requires a more elaborate (and possibly slower)
@@ -1085,24 +1044,6 @@ private void detectSymlinkSupport() {
* symbolic links, the modification time of the link is returned, rather
* than that of the link target.
*
- * @param f
- * a {@link java.io.File} object.
- * @return last modified time of f
- * @throws java.io.IOException
- * if an IO error occurred
- * @since 3.0
- * @deprecated use {@link #lastModifiedInstant(Path)} instead
- */
- @Deprecated
- public long lastModified(File f) throws IOException {
- return FileUtils.lastModified(f);
- }
-
- /**
- * Get the last modified time of a file system object. If the OS/JRE support
- * symbolic links, the modification time of the link is returned, rather
- * than that of the link target.
- *
* @param p
* a {@link Path} object.
* @return last modified time of p
@@ -1131,25 +1072,6 @@ public Instant lastModifiedInstant(File f) {
* <p>
* For symlinks it sets the modified time of the link target.
*
- * @param f
- * a {@link java.io.File} object.
- * @param time
- * last modified time
- * @throws java.io.IOException
- * if an IO error occurred
- * @since 3.0
- * @deprecated use {@link #setLastModified(Path, Instant)} instead
- */
- @Deprecated
- public void setLastModified(File f, long time) throws IOException {
- FileUtils.setLastModified(f, time);
- }
-
- /**
- * Set the last modified time of a file system object.
- * <p>
- * For symlinks it sets the modified time of the link target.
- *
* @param p
* a {@link Path} object.
* @param time
@@ -1443,13 +1365,6 @@ protected static String readPipe(File dir, String[] command,
}
} catch (IOException e) {
LOG.error("Caught exception in FS.readPipe()", e); //$NON-NLS-1$
- } catch (AccessControlException e) {
- LOG.warn(MessageFormat.format(
- JGitText.get().readPipeIsNotAllowedRequiredPermission,
- command, dir, e.getPermission()));
- } catch (SecurityException e) {
- LOG.warn(MessageFormat.format(JGitText.get().readPipeIsNotAllowed,
- command, dir));
}
if (debug) {
LOG.debug("readpipe returns null"); //$NON-NLS-1$
@@ -1800,25 +1715,6 @@ public void createSymLink(File path, String target) throws IOException {
}
/**
- * Create a new file. See {@link java.io.File#createNewFile()}. Subclasses
- * of this class may take care to provide a safe implementation for this
- * even if {@link #supportsAtomicCreateNewFile()} is <code>false</code>
- *
- * @param path
- * the file to be created
- * @return <code>true</code> if the file was created, <code>false</code> if
- * the file already existed
- * @throws java.io.IOException
- * if an IO error occurred
- * @deprecated use {@link #createNewFileAtomic(File)} instead
- * @since 4.5
- */
- @Deprecated
- public boolean createNewFile(File path) throws IOException {
- return path.createNewFile();
- }
-
- /**
* A token representing a file created by
* {@link #createNewFileAtomic(File)}. The token must be retained until the
* file has been deleted in order to guarantee that the unique file was
@@ -2042,6 +1938,8 @@ protected ProcessResult internalRunHookIfPresent(Repository repository,
environment.put(Constants.GIT_DIR_KEY,
repository.getDirectory().getAbsolutePath());
if (!repository.isBare()) {
+ environment.put(Constants.GIT_COMMON_DIR_KEY,
+ repository.getCommonDirectory().getAbsolutePath());
environment.put(Constants.GIT_WORK_TREE_KEY,
repository.getWorkTree().getAbsolutePath());
}
@@ -2137,7 +2035,7 @@ private File getRunDirectory(Repository repository,
case "post-receive": //$NON-NLS-1$
case "post-update": //$NON-NLS-1$
case "push-to-checkout": //$NON-NLS-1$
- return repository.getDirectory();
+ return repository.getCommonDirectory();
default:
return repository.getWorkTree();
}
@@ -2150,7 +2048,7 @@ private File getHooksDirectory(Repository repository) {
if (hooksDir != null) {
return new File(hooksDir);
}
- File dir = repository.getDirectory();
+ File dir = repository.getCommonDirectory();
return dir == null ? null : new File(dir, Constants.HOOKS);
}
@@ -2424,19 +2322,6 @@ public long getCreationTime() {
}
/**
- * Get the time when the file was last modified in milliseconds since
- * the epoch
- *
- * @return the time (milliseconds since 1970-01-01) when this object was
- * last modified
- * @deprecated use getLastModifiedInstant instead
- */
- @Deprecated
- public long getLastModifiedTime() {
- return lastModifiedInstant.toEpochMilli();
- }
-
- /**
* Get the time when this object was last modified
*
* @return the time when this object was last modified
@@ -2578,6 +2463,33 @@ public String normalize(String name) {
}
/**
+ * Get common dir path.
+ *
+ * @param dir
+ * the .git folder
+ * @return common dir path
+ * @throws IOException
+ * if commondir file can't be read
+ *
+ * @since 7.0
+ */
+ public File getCommonDir(File dir) throws IOException {
+ // first the GIT_COMMON_DIR is same as GIT_DIR
+ File commonDir = dir;
+ // now check if commondir file exists (e.g. worktree repository)
+ File commonDirFile = new File(dir, Constants.COMMONDIR_FILE);
+ if (commonDirFile.isFile()) {
+ String commonDirPath = new String(IO.readFully(commonDirFile))
+ .trim();
+ commonDir = new File(commonDirPath);
+ if (!commonDir.isAbsolute()) {
+ commonDir = new File(dir, commonDirPath).getCanonicalFile();
+ }
+ }
+ return commonDir;
+ }
+
+ /**
* This runnable will consume an input stream's content into an output
* stream as soon as it gets available.
* <p>
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java
index ee907f2..db2b5b4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010, Robin Rosenberg and others
+ * Copyright (C) 2010, 2024, Robin Rosenberg and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -203,7 +203,16 @@ public boolean supportsExecute() {
/** {@inheritDoc} */
@Override
public boolean canExecute(File f) {
- return FileUtils.canExecute(f);
+ if (!isFile(f)) {
+ return false;
+ }
+ try {
+ Path path = FileUtils.toPath(f);
+ Set<PosixFilePermission> pset = Files.getPosixFilePermissions(path);
+ return pset.contains(PosixFilePermission.OWNER_EXECUTE);
+ } catch (IOException ex) {
+ return false;
+ }
}
/** {@inheritDoc} */
@@ -332,73 +341,6 @@ public boolean supportsAtomicCreateNewFile() {
return supportsAtomicFileCreation == AtomicFileCreation.SUPPORTED;
}
- @Override
- @SuppressWarnings("boxing")
- /**
- * {@inheritDoc}
- * <p>
- * An implementation of the File#createNewFile() semantics which works also
- * on NFS. If the config option
- * {@code core.supportsAtomicCreateNewFile = true} (which is the default)
- * then simply File#createNewFile() is called.
- *
- * But if {@code core.supportsAtomicCreateNewFile = false} then after
- * successful creation of the lock file a hard link to that lock file is
- * created and the attribute nlink of the lock file is checked to be 2. If
- * multiple clients manage to create the same lock file nlink would be
- * greater than 2 showing the error.
- *
- * @see "https://www.time-travellers.org/shane/papers/NFS_considered_harmful.html"
- *
- * @deprecated use {@link FS_POSIX#createNewFileAtomic(File)} instead
- * @since 4.5
- */
- @Deprecated
- public boolean createNewFile(File lock) throws IOException {
- if (!lock.createNewFile()) {
- return false;
- }
- if (supportsAtomicCreateNewFile()) {
- return true;
- }
- Path lockPath = lock.toPath();
- Path link = null;
- FileStore store = null;
- try {
- store = Files.getFileStore(lockPath);
- } catch (SecurityException e) {
- return true;
- }
- try {
- Boolean canLink = CAN_HARD_LINK.computeIfAbsent(store,
- s -> Boolean.TRUE);
- if (Boolean.FALSE.equals(canLink)) {
- return true;
- }
- link = Files.createLink(
- Paths.get(lock.getAbsolutePath() + ".lnk"), //$NON-NLS-1$
- lockPath);
- Integer nlink = (Integer) Files.getAttribute(lockPath,
- "unix:nlink"); //$NON-NLS-1$
- if (nlink > 2) {
- LOG.warn(MessageFormat.format(
- JGitText.get().failedAtomicFileCreation, lockPath,
- nlink));
- return false;
- } else if (nlink < 2) {
- CAN_HARD_LINK.put(store, Boolean.FALSE);
- }
- return true;
- } catch (UnsupportedOperationException | IllegalArgumentException e) {
- CAN_HARD_LINK.put(store, Boolean.FALSE);
- return true;
- } finally {
- if (link != null) {
- Files.delete(link);
- }
- }
- }
-
/**
* {@inheritDoc}
* <p>
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java
index 635351a..2378791 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java
@@ -14,8 +14,6 @@
import java.io.File;
import java.io.OutputStream;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -43,10 +41,7 @@ public class FS_Win32_Cygwin extends FS_Win32 {
* @return true if cygwin is found
*/
public static boolean isCygwin() {
- final String path = AccessController
- .doPrivileged((PrivilegedAction<String>) () -> System
- .getProperty("java.library.path") //$NON-NLS-1$
- );
+ final String path = System.getProperty("java.library.path"); //$NON-NLS-1$
if (path == null)
return false;
File found = FS.searchPath(path, "cygpath.exe"); //$NON-NLS-1$
@@ -99,9 +94,7 @@ public File resolve(File dir, String pn) {
@Override
protected File userHomeImpl() {
- final String home = AccessController.doPrivileged(
- (PrivilegedAction<String>) () -> System.getenv("HOME") //$NON-NLS-1$
- );
+ final String home = System.getenv("HOME"); //$NON-NLS-1$
if (home == null || home.length() == 0)
return super.userHomeImpl();
return resolve(new File("."), home); //$NON-NLS-1$
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
index cab0e6a..39c67f1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
@@ -771,24 +771,6 @@ static boolean isSymlink(File file) {
}
/**
- * Get the lastModified attribute for a given file
- *
- * @param file
- * the file
- * @return lastModified attribute for given file, not following symbolic
- * links
- * @throws IOException
- * if an IO error occurred
- * @deprecated use {@link #lastModifiedInstant(Path)} instead which returns
- * FileTime
- */
- @Deprecated
- static long lastModified(File file) throws IOException {
- return Files.getLastModifiedTime(toPath(file), LinkOption.NOFOLLOW_LINKS)
- .toMillis();
- }
-
- /**
* Get last modified timestamp of a file
*
* @param path
@@ -830,21 +812,6 @@ static BasicFileAttributes fileAttributes(File file) throws IOException {
/**
* Set the last modified time of a file system object.
*
- * @param file
- * the file
- * @param time
- * last modified timestamp
- * @throws IOException
- * if an IO error occurred
- */
- @Deprecated
- static void setLastModified(File file, long time) throws IOException {
- Files.setLastModifiedTime(toPath(file), FileTime.fromMillis(time));
- }
-
- /**
- * Set the last modified time of a file system object.
- *
* @param path
* file path
* @param time
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateFormatter.java
index e6bf497..332e659 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateFormatter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateFormatter.java
@@ -10,10 +10,10 @@
package org.eclipse.jgit.util;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.time.format.FormatStyle;
import java.util.Locale;
-import java.util.TimeZone;
import org.eclipse.jgit.lib.PersonIdent;
@@ -26,9 +26,9 @@
*/
public class GitDateFormatter {
- private DateFormat dateTimeInstance;
+ private DateTimeFormatter dateTimeFormat;
- private DateFormat dateTimeInstance2;
+ private DateTimeFormatter dateTimeFormat2;
private final Format format;
@@ -96,30 +96,34 @@ public GitDateFormatter(Format format) {
default:
break;
case DEFAULT: // Not default:
- dateTimeInstance = new SimpleDateFormat(
+ dateTimeFormat = DateTimeFormatter.ofPattern(
"EEE MMM dd HH:mm:ss yyyy Z", Locale.US); //$NON-NLS-1$
break;
case ISO:
- dateTimeInstance = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z", //$NON-NLS-1$
+ dateTimeFormat = DateTimeFormatter.ofPattern(
+ "yyyy-MM-dd HH:mm:ss Z", //$NON-NLS-1$
Locale.US);
break;
case LOCAL:
- dateTimeInstance = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy", //$NON-NLS-1$
+ dateTimeFormat = DateTimeFormatter.ofPattern(
+ "EEE MMM dd HH:mm:ss yyyy", //$NON-NLS-1$
Locale.US);
break;
case RFC:
- dateTimeInstance = new SimpleDateFormat(
+ dateTimeFormat = DateTimeFormatter.ofPattern(
"EEE, dd MMM yyyy HH:mm:ss Z", Locale.US); //$NON-NLS-1$
break;
case SHORT:
- dateTimeInstance = new SimpleDateFormat("yyyy-MM-dd", Locale.US); //$NON-NLS-1$
+ dateTimeFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd", //$NON-NLS-1$
+ Locale.US);
break;
case LOCALE:
case LOCALELOCAL:
- SystemReader systemReader = SystemReader.getInstance();
- dateTimeInstance = systemReader.getDateTimeInstance(
- DateFormat.DEFAULT, DateFormat.DEFAULT);
- dateTimeInstance2 = systemReader.getSimpleDateFormat("Z"); //$NON-NLS-1$
+ dateTimeFormat = DateTimeFormatter
+ .ofLocalizedDateTime(FormatStyle.MEDIUM)
+ .withLocale(Locale.US);
+ dateTimeFormat2 = DateTimeFormatter.ofPattern("Z", //$NON-NLS-1$
+ Locale.US);
break;
}
}
@@ -135,39 +139,45 @@ public GitDateFormatter(Format format) {
@SuppressWarnings("boxing")
public String formatDate(PersonIdent ident) {
switch (format) {
- case RAW:
- int offset = ident.getTimeZoneOffset();
+ case RAW: {
+ int offset = ident.getZoneOffset().getTotalSeconds();
String sign = offset < 0 ? "-" : "+"; //$NON-NLS-1$ //$NON-NLS-2$
int offset2;
- if (offset < 0)
+ if (offset < 0) {
offset2 = -offset;
- else
+ } else {
offset2 = offset;
- int hours = offset2 / 60;
- int minutes = offset2 % 60;
+ }
+ int minutes = (offset2 / 60) % 60;
+ int hours = offset2 / 60 / 60;
return String.format("%d %s%02d%02d", //$NON-NLS-1$
- ident.getWhen().getTime() / 1000, sign, hours, minutes);
+ ident.getWhenAsInstant().getEpochSecond(), sign, hours,
+ minutes);
+ }
case RELATIVE:
- return RelativeDateFormatter.format(ident.getWhen());
+ return RelativeDateFormatter.format(ident.getWhenAsInstant());
case LOCALELOCAL:
case LOCAL:
- dateTimeInstance.setTimeZone(SystemReader.getInstance()
- .getTimeZone());
- return dateTimeInstance.format(ident.getWhen());
- case LOCALE:
- TimeZone tz = ident.getTimeZone();
- if (tz == null)
- tz = SystemReader.getInstance().getTimeZone();
- dateTimeInstance.setTimeZone(tz);
- dateTimeInstance2.setTimeZone(tz);
- return dateTimeInstance.format(ident.getWhen()) + " " //$NON-NLS-1$
- + dateTimeInstance2.format(ident.getWhen());
- default:
- tz = ident.getTimeZone();
- if (tz == null)
- tz = SystemReader.getInstance().getTimeZone();
- dateTimeInstance.setTimeZone(ident.getTimeZone());
- return dateTimeInstance.format(ident.getWhen());
+ return dateTimeFormat
+ .withZone(SystemReader.getInstance().getTimeZoneId())
+ .format(ident.getWhenAsInstant());
+ case LOCALE: {
+ ZoneId tz = ident.getZoneId();
+ if (tz == null) {
+ tz = SystemReader.getInstance().getTimeZoneId();
+ }
+ return dateTimeFormat.withZone(tz).format(ident.getWhenAsInstant())
+ + " " //$NON-NLS-1$
+ + dateTimeFormat2.withZone(tz)
+ .format(ident.getWhenAsInstant());
+ }
+ default: {
+ ZoneId tz = ident.getZoneId();
+ if (tz == null) {
+ tz = SystemReader.getInstance().getTimeZoneId();
+ }
+ return dateTimeFormat.withZone(tz).format(ident.getWhenAsInstant());
+ }
}
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateParser.java
index 6a4b396..f080056 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateParser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateParser.java
@@ -28,7 +28,10 @@
* used. One example is the parsing of the config parameter gc.pruneexpire. The
* parser can handle only subset of what native gits approxidate parser
* understands.
+ *
+ * @deprecated Use {@link GitTimeParser} instead.
*/
+@Deprecated(since = "7.1")
public class GitDateParser {
/**
* The Date representing never. Though this is a concrete value, most
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/GitTimeParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/GitTimeParser.java
new file mode 100644
index 0000000..acaa1ce
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/GitTimeParser.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2024 Christian Halstrick and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.util;
+
+import java.text.MessageFormat;
+import java.text.ParseException;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.time.temporal.ChronoField;
+import java.time.temporal.TemporalAccessor;
+import java.util.EnumMap;
+import java.util.Map;
+
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.internal.JGitText;
+
+/**
+ * Parses strings with time and date specifications into
+ * {@link java.time.Instant}.
+ *
+ * When git needs to parse strings specified by the user this parser can be
+ * used. One example is the parsing of the config parameter gc.pruneexpire. The
+ * parser can handle only subset of what native gits approxidate parser
+ * understands.
+ *
+ * @since 7.1
+ */
+public class GitTimeParser {
+
+ private static final Map<ParseableSimpleDateFormat, DateTimeFormatter> formatCache = new EnumMap<>(
+ ParseableSimpleDateFormat.class);
+
+ // An enum of all those formats which this parser can parse with the help of
+ // a DateTimeFormatter. There are other formats (e.g. the relative formats
+ // like "yesterday" or "1 week ago") which this parser can parse but which
+ // are not listed here because they are parsed without the help of a
+ // DateTimeFormatter.
+ enum ParseableSimpleDateFormat {
+ ISO("yyyy-MM-dd HH:mm:ss Z"), // //$NON-NLS-1$
+ RFC("EEE, dd MMM yyyy HH:mm:ss Z"), // //$NON-NLS-1$
+ SHORT("yyyy-MM-dd"), // //$NON-NLS-1$
+ SHORT_WITH_DOTS_REVERSE("dd.MM.yyyy"), // //$NON-NLS-1$
+ SHORT_WITH_DOTS("yyyy.MM.dd"), // //$NON-NLS-1$
+ SHORT_WITH_SLASH("MM/dd/yyyy"), // //$NON-NLS-1$
+ DEFAULT("EEE MMM dd HH:mm:ss yyyy Z"), // //$NON-NLS-1$
+ LOCAL("EEE MMM dd HH:mm:ss yyyy"); //$NON-NLS-1$
+
+ private final String formatStr;
+
+ ParseableSimpleDateFormat(String formatStr) {
+ this.formatStr = formatStr;
+ }
+ }
+
+ private GitTimeParser() {
+ // This class is not supposed to be instantiated
+ }
+
+ /**
+ * Parses a string into a {@link java.time.LocalDateTime} using the default
+ * locale. Since this parser also supports relative formats (e.g.
+ * "yesterday") the caller can specify the reference date. These types of
+ * strings can be parsed:
+ * <ul>
+ * <li>"never"</li>
+ * <li>"now"</li>
+ * <li>"yesterday"</li>
+ * <li>"(x) years|months|weeks|days|hours|minutes|seconds ago"<br>
+ * Multiple specs can be combined like in "2 weeks 3 days ago". Instead of '
+ * ' one can use '.' to separate the words</li>
+ * <li>"yyyy-MM-dd HH:mm:ss Z" (ISO)</li>
+ * <li>"EEE, dd MMM yyyy HH:mm:ss Z" (RFC)</li>
+ * <li>"yyyy-MM-dd"</li>
+ * <li>"yyyy.MM.dd"</li>
+ * <li>"MM/dd/yyyy",</li>
+ * <li>"dd.MM.yyyy"</li>
+ * <li>"EEE MMM dd HH:mm:ss yyyy Z" (DEFAULT)</li>
+ * <li>"EEE MMM dd HH:mm:ss yyyy" (LOCAL)</li>
+ * </ul>
+ *
+ * @param dateStr
+ * the string to be parsed
+ * @return the parsed {@link java.time.LocalDateTime}
+ * @throws java.text.ParseException
+ * if the given dateStr was not recognized
+ */
+ public static LocalDateTime parse(String dateStr) throws ParseException {
+ return parse(dateStr, SystemReader.getInstance().civilNow());
+ }
+
+ /**
+ * Parses a string into a {@link java.time.Instant} using the default
+ * locale. Since this parser also supports relative formats (e.g.
+ * "yesterday") the caller can specify the reference date. These types of
+ * strings can be parsed:
+ * <ul>
+ * <li>"never"</li>
+ * <li>"now"</li>
+ * <li>"yesterday"</li>
+ * <li>"(x) years|months|weeks|days|hours|minutes|seconds ago"<br>
+ * Multiple specs can be combined like in "2 weeks 3 days ago". Instead of '
+ * ' one can use '.' to separate the words</li>
+ * <li>"yyyy-MM-dd HH:mm:ss Z" (ISO)</li>
+ * <li>"EEE, dd MMM yyyy HH:mm:ss Z" (RFC)</li>
+ * <li>"yyyy-MM-dd"</li>
+ * <li>"yyyy.MM.dd"</li>
+ * <li>"MM/dd/yyyy",</li>
+ * <li>"dd.MM.yyyy"</li>
+ * <li>"EEE MMM dd HH:mm:ss yyyy Z" (DEFAULT)</li>
+ * <li>"EEE MMM dd HH:mm:ss yyyy" (LOCAL)</li>
+ * </ul>
+ *
+ * @param dateStr
+ * the string to be parsed
+ * @return the parsed {@link java.time.Instant}
+ * @throws java.text.ParseException
+ * if the given dateStr was not recognized
+ * @since 7.2
+ */
+ public static Instant parseInstant(String dateStr) throws ParseException {
+ return parse(dateStr).atZone(SystemReader.getInstance().getTimeZoneId())
+ .toInstant();
+ }
+
+ // Only tests seem to use this method
+ static LocalDateTime parse(String dateStr, LocalDateTime now)
+ throws ParseException {
+ dateStr = dateStr.trim();
+
+ if (dateStr.equalsIgnoreCase("never")) { //$NON-NLS-1$
+ return LocalDateTime.MAX;
+ }
+ LocalDateTime ret = parseRelative(dateStr, now);
+ if (ret != null) {
+ return ret;
+ }
+ for (ParseableSimpleDateFormat f : ParseableSimpleDateFormat.values()) {
+ try {
+ return parseSimple(dateStr, f);
+ } catch (DateTimeParseException e) {
+ // simply proceed with the next parser
+ }
+ }
+ ParseableSimpleDateFormat[] values = ParseableSimpleDateFormat.values();
+ StringBuilder allFormats = new StringBuilder("\"") //$NON-NLS-1$
+ .append(values[0].formatStr);
+ for (int i = 1; i < values.length; i++) {
+ allFormats.append("\", \"").append(values[i].formatStr); //$NON-NLS-1$
+ }
+ allFormats.append("\""); //$NON-NLS-1$
+ throw new ParseException(
+ MessageFormat.format(JGitText.get().cannotParseDate, dateStr,
+ allFormats.toString()),
+ 0);
+ }
+
+ // tries to parse a string with the formats supported by DateTimeFormatter
+ private static LocalDateTime parseSimple(String dateStr,
+ ParseableSimpleDateFormat f) throws DateTimeParseException {
+ DateTimeFormatter dateFormat = formatCache.computeIfAbsent(f,
+ format -> DateTimeFormatter
+ .ofPattern(f.formatStr)
+ .withLocale(SystemReader.getInstance().getLocale()));
+ TemporalAccessor parsed = dateFormat.parse(dateStr);
+ return parsed.isSupported(ChronoField.HOUR_OF_DAY)
+ ? LocalDateTime.from(parsed)
+ : LocalDate.from(parsed).atStartOfDay();
+ }
+
+ // tries to parse a string with a relative time specification
+ @SuppressWarnings("nls")
+ @Nullable
+ private static LocalDateTime parseRelative(String dateStr,
+ LocalDateTime now) {
+ // check for the static words "yesterday" or "now"
+ if (dateStr.equals("now")) {
+ return now;
+ }
+
+ if (dateStr.equals("yesterday")) {
+ return now.minusDays(1);
+ }
+
+ // parse constructs like "3 days ago", "5.week.2.day.ago"
+ String[] parts = dateStr.split("\\.| ", -1);
+ int partsLength = parts.length;
+ // check we have an odd number of parts (at least 3) and that the last
+ // part is "ago"
+ if (partsLength < 3 || (partsLength & 1) == 0
+ || !parts[parts.length - 1].equals("ago")) {
+ return null;
+ }
+ int number;
+ for (int i = 0; i < parts.length - 2; i += 2) {
+ try {
+ number = Integer.parseInt(parts[i]);
+ } catch (NumberFormatException e) {
+ return null;
+ }
+ if (parts[i + 1] == null) {
+ return null;
+ }
+ switch (parts[i + 1]) {
+ case "year":
+ case "years":
+ now = now.minusYears(number);
+ break;
+ case "month":
+ case "months":
+ now = now.minusMonths(number);
+ break;
+ case "week":
+ case "weeks":
+ now = now.minusWeeks(number);
+ break;
+ case "day":
+ case "days":
+ now = now.minusDays(number);
+ break;
+ case "hour":
+ case "hours":
+ now = now.minusHours(number);
+ break;
+ case "minute":
+ case "minutes":
+ now = now.minusMinutes(number);
+ break;
+ case "second":
+ case "seconds":
+ now = now.minusSeconds(number);
+ break;
+ default:
+ return null;
+ }
+ }
+ return now;
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java
index 46d0bc8..3ed7251 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java
@@ -13,6 +13,8 @@
import static java.nio.charset.StandardCharsets.ISO_8859_1;
import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.time.Instant.EPOCH;
+import static java.time.ZoneOffset.UTC;
import static org.eclipse.jgit.lib.ObjectChecker.author;
import static org.eclipse.jgit.lib.ObjectChecker.committer;
import static org.eclipse.jgit.lib.ObjectChecker.encoding;
@@ -30,6 +32,10 @@
import java.nio.charset.CodingErrorAction;
import java.nio.charset.IllegalCharsetNameException;
import java.nio.charset.UnsupportedCharsetException;
+import java.time.DateTimeException;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@@ -44,14 +50,6 @@
* Handy utility functions to parse raw object contents.
*/
public final class RawParseUtils {
- /**
- * UTF-8 charset constant.
- *
- * @since 2.2
- * @deprecated use {@link java.nio.charset.StandardCharsets#UTF_8} instead
- */
- @Deprecated
- public static final Charset UTF8_CHARSET = UTF_8;
private static final byte[] digits10;
@@ -467,6 +465,29 @@ public static final int parseTimeZoneOffset(final byte[] b, int ptr,
}
/**
+ * Parse a Git style timezone string in [+-]hhmm format
+ *
+ * @param b
+ * buffer to scan.
+ * @param ptr
+ * position within buffer to start parsing digits at.
+ * @param ptrResult
+ * optional location to return the new ptr value through. If null
+ * the ptr value will be discarded.
+ * @return the ZoneOffset represention of the timezone offset string.
+ * Invalid offsets default to UTC.
+ */
+ private static ZoneId parseZoneOffset(final byte[] b, int ptr,
+ MutableInteger ptrResult) {
+ int hhmm = parseBase10(b, ptr, ptrResult);
+ try {
+ return ZoneOffset.ofHoursMinutes(hhmm / 100, hhmm % 100);
+ } catch (DateTimeException e) {
+ return UTC;
+ }
+ }
+
+ /**
* Locate the first position after a given character.
*
* @param b
@@ -1035,17 +1056,19 @@ public static PersonIdent parsePersonIdent(byte[] raw, int nameB) {
// character if there is no trailing LF.
final int tzBegin = lastIndexOfTrim(raw, ' ',
nextLF(raw, emailE - 1) - 2) + 1;
- if (tzBegin <= emailE) // No time/zone, still valid
- return new PersonIdent(name, email, 0, 0);
+ if (tzBegin <= emailE) { // No time/zone, still valid
+ return new PersonIdent(name, email, EPOCH, UTC);
+ }
final int whenBegin = Math.max(emailE,
lastIndexOfTrim(raw, ' ', tzBegin - 1) + 1);
- if (whenBegin >= tzBegin - 1) // No time/zone, still valid
- return new PersonIdent(name, email, 0, 0);
+ if (whenBegin >= tzBegin - 1) { // No time/zone, still valid
+ return new PersonIdent(name, email, EPOCH, UTC);
+ }
- final long when = parseLongBase10(raw, whenBegin, null);
- final int tz = parseTimeZoneOffset(raw, tzBegin);
- return new PersonIdent(name, email, when * 1000L, tz);
+ long when = parseLongBase10(raw, whenBegin, null);
+ return new PersonIdent(name, email, Instant.ofEpochSecond(when),
+ parseZoneOffset(raw, tzBegin, null));
}
/**
@@ -1083,16 +1106,16 @@ public static PersonIdent parsePersonIdentOnly(final byte[] raw,
name = decode(raw, nameB, stop);
final MutableInteger ptrout = new MutableInteger();
- long when;
- int tz;
+ Instant when;
+ ZoneId tz;
if (emailE < stop) {
- when = parseLongBase10(raw, emailE + 1, ptrout);
- tz = parseTimeZoneOffset(raw, ptrout.value);
+ when = Instant.ofEpochSecond(parseLongBase10(raw, emailE + 1, ptrout));
+ tz = parseZoneOffset(raw, ptrout.value, null);
} else {
- when = 0;
- tz = 0;
+ when = EPOCH;
+ tz = UTC;
}
- return new PersonIdent(name, email, when * 1000L, tz);
+ return new PersonIdent(name, email, when, tz);
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/RelativeDateFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/RelativeDateFormatter.java
index 5611b1e..b6b19e0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/RelativeDateFormatter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/RelativeDateFormatter.java
@@ -10,6 +10,8 @@
package org.eclipse.jgit.util;
import java.text.MessageFormat;
+import java.time.Duration;
+import java.time.Instant;
import java.util.Date;
import org.eclipse.jgit.internal.JGitText;
@@ -42,12 +44,29 @@ public class RelativeDateFormatter {
* @return age of given {@link java.util.Date} compared to now formatted in
* the same relative format as returned by
* {@code git log --relative-date}
+ * @deprecated Use {@link #format(Instant)} instead.
*/
+ @Deprecated(since = "7.2")
@SuppressWarnings("boxing")
public static String format(Date when) {
+ return format(when.toInstant());
+ }
- long ageMillis = SystemReader.getInstance().getCurrentTime()
- - when.getTime();
+ /**
+ * Get age of given {@link java.time.Instant} compared to now formatted in the
+ * same relative format as returned by {@code git log --relative-date}
+ *
+ * @param when
+ * an instant to format
+ * @return age of given instant compared to now formatted in
+ * the same relative format as returned by
+ * {@code git log --relative-date}
+ * @since 7.2
+ */
+ @SuppressWarnings("boxing")
+ public static String format(Instant when) {
+ long ageMillis = Duration
+ .between(when, SystemReader.getInstance().now()).toMillis();
// shouldn't happen in a perfect world
if (ageMillis < 0)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/SignatureUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/SignatureUtils.java
index cf06172..e3e3e04 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/SignatureUtils.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/SignatureUtils.java
@@ -13,8 +13,8 @@
import java.util.Locale;
import org.eclipse.jgit.internal.JGitText;
-import org.eclipse.jgit.lib.GpgSignatureVerifier.SignatureVerification;
-import org.eclipse.jgit.lib.GpgSignatureVerifier.TrustLevel;
+import org.eclipse.jgit.lib.SignatureVerifier.SignatureVerification;
+import org.eclipse.jgit.lib.SignatureVerifier.TrustLevel;
import org.eclipse.jgit.lib.PersonIdent;
/**
@@ -39,29 +39,34 @@ private SignatureUtils() {
* to use for dates
* @return a textual representation of the {@link SignatureVerification},
* using LF as line separator
+ *
+ * @since 7.0
*/
public static String toString(SignatureVerification verification,
PersonIdent creator, GitDateFormatter formatter) {
StringBuilder result = new StringBuilder();
- // Use the creator's timezone for the signature date
- PersonIdent dateId = new PersonIdent(creator,
- verification.getCreationDate());
- result.append(MessageFormat.format(JGitText.get().verifySignatureMade,
- formatter.formatDate(dateId)));
- result.append('\n');
+ if (verification.creationDate() != null) {
+ // Use the creator's timezone for the signature date
+ PersonIdent dateId = new PersonIdent(creator,
+ verification.creationDate().toInstant());
+ result.append(
+ MessageFormat.format(JGitText.get().verifySignatureMade,
+ formatter.formatDate(dateId)));
+ result.append('\n');
+ }
result.append(MessageFormat.format(
JGitText.get().verifySignatureKey,
- verification.getKeyFingerprint().toUpperCase(Locale.ROOT)));
+ verification.keyFingerprint().toUpperCase(Locale.ROOT)));
result.append('\n');
- if (!StringUtils.isEmptyOrNull(verification.getSigner())) {
+ if (!StringUtils.isEmptyOrNull(verification.signer())) {
result.append(
MessageFormat.format(JGitText.get().verifySignatureIssuer,
- verification.getSigner()));
+ verification.signer()));
result.append('\n');
}
String msg;
- if (verification.getVerified()) {
- if (verification.isExpired()) {
+ if (verification.verified()) {
+ if (verification.expired()) {
msg = JGitText.get().verifySignatureExpired;
} else {
msg = JGitText.get().verifySignatureGood;
@@ -69,14 +74,14 @@ public static String toString(SignatureVerification verification,
} else {
msg = JGitText.get().verifySignatureBad;
}
- result.append(MessageFormat.format(msg, verification.getKeyUser()));
- if (!TrustLevel.UNKNOWN.equals(verification.getTrustLevel())) {
+ result.append(MessageFormat.format(msg, verification.keyUser()));
+ if (!TrustLevel.UNKNOWN.equals(verification.trustLevel())) {
result.append(' ' + MessageFormat
.format(JGitText.get().verifySignatureTrust, verification
- .getTrustLevel().name().toLowerCase(Locale.ROOT)));
+ .trustLevel().name().toLowerCase(Locale.ROOT)));
}
result.append('\n');
- msg = verification.getMessage();
+ msg = verification.message();
if (!StringUtils.isEmptyOrNull(msg)) {
result.append(msg);
result.append('\n');
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/Stats.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/Stats.java
index d957deb..efa6e7dd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/Stats.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/Stats.java
@@ -43,14 +43,18 @@ public void add(double x) {
}
/**
- * @return number of the added values
+ * Returns the number of added values
+ *
+ * @return the number of added values
*/
public int count() {
return n;
}
/**
- * @return minimum of the added values
+ * Returns the smallest value added
+ *
+ * @return the smallest value added
*/
public double min() {
if (n < 1) {
@@ -60,7 +64,9 @@ public double min() {
}
/**
- * @return maximum of the added values
+ * Returns the biggest value added
+ *
+ * @return the biggest value added
*/
public double max() {
if (n < 1) {
@@ -70,9 +76,10 @@ public double max() {
}
/**
- * @return average of the added values
+ * Returns the average of the added values
+ *
+ * @return the average of the added values
*/
-
public double avg() {
if (n < 1) {
return Double.NaN;
@@ -81,7 +88,9 @@ public double avg() {
}
/**
- * @return variance of the added values
+ * Returns the variance of the added values
+ *
+ * @return the variance of the added values
*/
public double var() {
if (n < 2) {
@@ -91,7 +100,9 @@ public double var() {
}
/**
- * @return standard deviation of the added values
+ * Returns the standard deviation of the added values
+ *
+ * @return the standard deviation of the added values
*/
public double stddev() {
return Math.sqrt(this.var());
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/StringUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/StringUtils.java
index 2fbd12d..e381a3b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/StringUtils.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/StringUtils.java
@@ -278,6 +278,44 @@ public static String join(Collection<String> parts, String separator,
}
/**
+ * Remove the specified character from beginning and end of a string
+ * <p>
+ * If the character repeats, all copies
+ *
+ * @param str input string
+ * @param c character to remove
+ * @return the input string with c
+ * @since 7.2
+ */
+ public static String trim(String str, char c) {
+ if (str == null || str.length() == 0) {
+ return str;
+ }
+
+ int endPos = str.length()-1;
+ while (endPos >= 0 && str.charAt(endPos) == c) {
+ endPos--;
+ }
+
+ // Whole string is c
+ if (endPos == -1) {
+ return EMPTY;
+ }
+
+ int startPos = 0;
+ while (startPos < endPos && str.charAt(startPos) == c) {
+ startPos++;
+ }
+
+ if (startPos == 0 && endPos == str.length()-1) {
+ // No need to copy
+ return str;
+ }
+
+ return str.substring(startPos, endPos+1);
+ }
+
+ /**
* Appends {@link Constants#DOT_GIT_EXT} unless the given name already ends
* with that suffix.
*
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
index ed62c71..22b82b3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
@@ -23,10 +23,12 @@
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.atomic.AtomicReference;
@@ -169,6 +171,11 @@ public long getCurrentTime() {
}
@Override
+ public Instant now() {
+ return Instant.now();
+ }
+
+ @Override
public int getTimezone(long when) {
return getTimeZone().getOffset(when) / (60 * 1000);
}
@@ -230,9 +237,19 @@ public long getCurrentTime() {
}
@Override
+ public Instant now() {
+ return delegate.now();
+ }
+
+ @Override
public int getTimezone(long when) {
return delegate.getTimezone(when);
}
+
+ @Override
+ public ZoneOffset getTimeZoneAt(Instant when) {
+ return delegate.getTimeZoneAt(when);
+ }
}
private static volatile SystemReader INSTANCE = DEFAULT;
@@ -503,10 +520,37 @@ private void updateAll(Config config)
* Get the current system time
*
* @return the current system time
+ *
+ * @deprecated Use {@link #now()}
*/
+ @Deprecated(since = "7.1")
public abstract long getCurrentTime();
/**
+ * Get the current system time
+ *
+ * @return the current system time
+ *
+ * @since 7.1
+ */
+ public Instant now() {
+ // Subclasses overriding getCurrentTime should keep working
+ // TODO(ifrade): Once we remove getCurrentTime, use Instant.now()
+ return Instant.ofEpochMilli(getCurrentTime());
+ }
+
+ /**
+ * Get "now" as civil time, in the System timezone
+ *
+ * @return the current system time
+ *
+ * @since 7.1
+ */
+ public LocalDateTime civilNow() {
+ return LocalDateTime.ofInstant(now(), getTimeZoneId());
+ }
+
+ /**
* Get clock instance preferred by this system.
*
* @return clock instance preferred by this system.
@@ -522,20 +566,48 @@ public MonotonicClock getClock() {
* @param when
* a system timestamp
* @return the local time zone
+ *
+ * @deprecated Use {@link #getTimeZoneAt(Instant)} instead.
*/
+ @Deprecated(since = "7.1")
public abstract int getTimezone(long when);
/**
+ * Get the local time zone offset at "when" time
+ *
+ * @param when
+ * a system timestamp
+ * @return the local time zone
+ * @since 7.1
+ */
+ public ZoneOffset getTimeZoneAt(Instant when) {
+ return getTimeZoneId().getRules().getOffset(when);
+ }
+
+ /**
* Get system time zone, possibly mocked for testing
*
* @return system time zone, possibly mocked for testing
* @since 1.2
+ *
+ * @deprecated Use {@link #getTimeZoneId()}
*/
+ @Deprecated(since = "7.1")
public TimeZone getTimeZone() {
return TimeZone.getDefault();
}
/**
+ * Get system time zone, possibly mocked for testing
+ *
+ * @return system time zone, possibly mocked for testing
+ * @since 7.1
+ */
+ public ZoneId getTimeZoneId() {
+ return ZoneId.systemDefault();
+ }
+
+ /**
* Get the locale to use
*
* @return the locale to use
@@ -670,9 +742,7 @@ public boolean isPerformanceTraceEnabled() {
}
private String getOsName() {
- return AccessController.doPrivileged(
- (PrivilegedAction<String>) () -> getProperty("os.name") //$NON-NLS-1$
- );
+ return getProperty("os.name"); //$NON-NLS-1$
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFInputStream.java
index 2385865..4b9706a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFInputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFInputStream.java
@@ -147,44 +147,6 @@ public AutoLFInputStream(InputStream in, Set<StreamFlag> flags) {
&& flags.contains(StreamFlag.FOR_CHECKOUT);
}
- /**
- * Creates a new InputStream, wrapping the specified stream.
- *
- * @param in
- * raw input stream
- * @param detectBinary
- * whether binaries should be detected
- * @since 2.0
- * @deprecated since 5.9, use {@link #create(InputStream, StreamFlag...)}
- * instead
- */
- @Deprecated
- public AutoLFInputStream(InputStream in, boolean detectBinary) {
- this(in, detectBinary, false);
- }
-
- /**
- * Creates a new InputStream, wrapping the specified stream.
- *
- * @param in
- * raw input stream
- * @param detectBinary
- * whether binaries should be detected
- * @param abortIfBinary
- * throw an IOException if the file is binary
- * @since 3.3
- * @deprecated since 5.9, use {@link #create(InputStream, StreamFlag...)}
- * instead
- */
- @Deprecated
- public AutoLFInputStream(InputStream in, boolean detectBinary,
- boolean abortIfBinary) {
- this.in = in;
- this.detectBinary = detectBinary;
- this.abortIfBinary = abortIfBinary;
- this.forCheckout = false;
- }
-
@Override
public int read() throws IOException {
final int read = read(single, 0, 1);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/ThrowingPrintWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/ThrowingPrintWriter.java
index 4764676..13982b1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/ThrowingPrintWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/ThrowingPrintWriter.java
@@ -11,8 +11,6 @@
import java.io.IOException;
import java.io.Writer;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
import org.eclipse.jgit.util.SystemReader;
@@ -35,10 +33,7 @@ public class ThrowingPrintWriter extends Writer {
*/
public ThrowingPrintWriter(Writer out) {
this.out = out;
- LF = AccessController
- .doPrivileged((PrivilegedAction<String>) () -> SystemReader
- .getInstance().getProperty("line.separator") //$NON-NLS-1$
- );
+ LF = SystemReader.getInstance().getProperty("line.separator"); //$NON-NLS-1$
}
@Override
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/UnionInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/UnionInputStream.java
index c3a1c4e..7e950f6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/UnionInputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/UnionInputStream.java
@@ -14,7 +14,6 @@
import java.io.InputStream;
import java.util.ArrayDeque;
import java.util.Deque;
-import java.util.Iterator;
/**
* An InputStream which reads from one or more InputStreams.
@@ -164,14 +163,14 @@ public long skip(long count) throws IOException {
public void close() throws IOException {
IOException err = null;
- for (Iterator<InputStream> i = streams.iterator(); i.hasNext();) {
+ for (InputStream stream : streams) {
try {
- i.next().close();
+ stream.close();
} catch (IOException closeError) {
err = closeError;
}
- i.remove();
}
+ streams.clear();
if (err != null)
throw err;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/time/ProposedTimestamp.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/time/ProposedTimestamp.java
index a5ee107..a20eaaf 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/time/ProposedTimestamp.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/time/ProposedTimestamp.java
@@ -138,7 +138,10 @@ public Instant instant() {
* Get time since epoch, with up to microsecond resolution.
*
* @return time since epoch, with up to microsecond resolution.
+ *
+ * @deprecated Use instant() instead
*/
+ @Deprecated(since = "7.2")
public Timestamp timestamp() {
return Timestamp.from(instant());
}
@@ -147,7 +150,10 @@ public Timestamp timestamp() {
* Get time since epoch, with up to millisecond resolution.
*
* @return time since epoch, with up to millisecond resolution.
+ *
+ * @deprecated Use instant() instead
*/
+ @Deprecated(since = "7.2")
public Date date() {
return new Date(millis());
}
diff --git a/pom.xml b/pom.xml
index 590dffa..ecae118 100644
--- a/pom.xml
+++ b/pom.xml
@@ -18,7 +18,7 @@
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
<packaging>pom</packaging>
- <version>7.0.0-SNAPSHOT</version>
+ <version>7.3.0-SNAPSHOT</version>
<name>JGit - Parent</name>
<url>${jgit-url}</url>
@@ -33,8 +33,8 @@
</description>
<scm>
- <url>https://git.eclipse.org/r/plugins/gitiles/jgit/jgit</url>
- <connection>scm:git:https://git.eclipse.org/r/jgit/jgit</connection>
+ <url>https://eclipse.gerrithub.io/plugins/gitiles/eclipse-jgit/jgit</url>
+ <connection>scm:git:https://eclipse.gerrithub.io/eclipse-jgit/jgit</connection>
</scm>
<ciManagement>
@@ -95,8 +95,8 @@
</mailingLists>
<issueManagement>
- <url>https://bugs.eclipse.org/bugs/buglist.cgi?query_format=advanced;component=JGit;product=JGit;classification=Technology</url>
- <system>Bugzilla</system>
+ <url>https://github.com/eclipse-jgit/jgit/issues</url>
+ <system>GitHub Issues</system>
</issueManagement>
<licenses>
@@ -118,37 +118,37 @@
<project.build.outputTimestamp>${commit.time.iso}</project.build.outputTimestamp>
- <jgit-last-release-version>6.9.0.202403050737-r</jgit-last-release-version>
- <ant-version>1.10.14</ant-version>
- <apache-sshd-version>2.12.0</apache-sshd-version>
+ <jgit-last-release-version>7.1.0.202411261347-r</jgit-last-release-version>
+ <ant-version>1.10.15</ant-version>
+ <apache-sshd-version>2.15.0</apache-sshd-version>
<jsch-version>0.1.55</jsch-version>
<jzlib-version>1.1.3</jzlib-version>
<javaewah-version>1.2.3</javaewah-version>
<junit-version>4.13.2</junit-version>
<test-fork-count>1C</test-fork-count>
- <args4j-version>2.33</args4j-version>
- <commons-compress-version>1.26.0</commons-compress-version>
+ <args4j-version>2.37</args4j-version>
+ <commons-compress-version>1.27.1</commons-compress-version>
<osgi-core-version>6.0.0</osgi-core-version>
- <servlet-api-version>6.0.0</servlet-api-version>
- <jetty-version>12.0.9</jetty-version>
- <japicmp-version>0.18.5</japicmp-version>
+ <servlet-api-version>6.1.0</servlet-api-version>
+ <jetty-version>12.0.16</jetty-version>
+ <japicmp-version>0.23.1</japicmp-version>
<httpclient-version>4.5.14</httpclient-version>
<httpcore-version>4.4.16</httpcore-version>
<slf4j-version>1.7.36</slf4j-version>
- <maven-javadoc-plugin-version>3.6.3</maven-javadoc-plugin-version>
- <gson-version>2.10.1</gson-version>
- <bouncycastle-version>1.77</bouncycastle-version>
- <spotbugs-maven-plugin-version>4.8.3.1</spotbugs-maven-plugin-version>
- <maven-project-info-reports-plugin-version>3.5.1</maven-project-info-reports-plugin-version>
- <maven-jxr-plugin-version>3.3.2</maven-jxr-plugin-version>
- <maven-surefire-plugin-version>3.2.5</maven-surefire-plugin-version>
+ <maven-javadoc-plugin-version>3.11.2</maven-javadoc-plugin-version>
+ <gson-version>2.12.1</gson-version>
+ <bouncycastle-version>1.80</bouncycastle-version>
+ <spotbugs-maven-plugin-version>4.9.1.0</spotbugs-maven-plugin-version>
+ <maven-project-info-reports-plugin-version>3.8.0</maven-project-info-reports-plugin-version>
+ <maven-jxr-plugin-version>3.6.0</maven-jxr-plugin-version>
+ <maven-surefire-plugin-version>3.5.2</maven-surefire-plugin-version>
<maven-surefire-report-plugin-version>${maven-surefire-plugin-version}</maven-surefire-report-plugin-version>
- <maven-compiler-plugin-version>3.12.1</maven-compiler-plugin-version>
+ <maven-compiler-plugin-version>3.14.0</maven-compiler-plugin-version>
<plexus-compiler-version>2.13.0</plexus-compiler-version>
<hamcrest-version>2.2</hamcrest-version>
- <assertj-version>3.25.3</assertj-version>
- <jna-version>5.14.0</jna-version>
- <byte-buddy-version>1.14.12</byte-buddy-version>
+ <assertj-version>3.27.3</assertj-version>
+ <jna-version>5.16.0</jna-version>
+ <byte-buddy-version>1.17.1</byte-buddy-version>
<!-- Properties to enable jacoco code coverage analysis -->
<sonar.core.codeCoveragePlugin>jacoco</sonar.core.codeCoveragePlugin>
@@ -184,7 +184,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
- <version>3.3.0</version>
+ <version>3.4.2</version>
<configuration>
<archive>
<manifestEntries>
@@ -208,13 +208,13 @@
<plugin>
<artifactId>maven-clean-plugin</artifactId>
- <version>3.3.2</version>
+ <version>3.4.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
- <version>3.5.1</version>
+ <version>3.6.0</version>
</plugin>
<plugin>
@@ -226,13 +226,13 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
- <version>3.6.1</version>
+ <version>3.8.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
- <version>3.3.0</version>
+ <version>3.3.1</version>
</plugin>
<plugin>
@@ -255,7 +255,7 @@
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
- <version>3.5.0</version>
+ <version>3.6.0</version>
</plugin>
<plugin>
@@ -277,7 +277,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
- <version>3.21.2</version>
+ <version>3.26.0</version>
<configuration>
<inputEncoding>${project.build.sourceEncoding}</inputEncoding>
<minimumTokens>100</minimumTokens>
@@ -300,17 +300,17 @@
<plugin>
<groupId>org.eclipse.cbi.maven.plugins</groupId>
<artifactId>eclipse-jarsigner-plugin</artifactId>
- <version>1.4.3</version>
+ <version>1.5.2</version>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
- <version>0.8.11</version>
+ <version>0.8.12</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
- <version>4.0.0-M13</version>
+ <version>4.0.0-M16</version>
<dependencies>
<dependency><!-- add support for ssh/scp -->
<groupId>org.apache.maven.wagon</groupId>
@@ -337,12 +337,12 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
- <version>3.1.1</version>
+ <version>3.1.3</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
- <version>3.1.1</version>
+ <version>3.1.3</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@@ -357,7 +357,7 @@
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
- <version>2.7.13</version>
+ <version>3.4.3</version>
</plugin>
<plugin>
<groupId>org.eclipse.dash</groupId>
@@ -367,12 +367,12 @@
<plugin>
<groupId>org.cyclonedx</groupId>
<artifactId>cyclonedx-maven-plugin</artifactId>
- <version>2.7.10</version>
+ <version>2.9.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-artifact-plugin</artifactId>
- <version>3.5.0</version>
+ <version>3.6.0</version>
<configuration>
<ignore>**/*cyclonedx.json</ignore>
<reproducible>true</reproducible>
@@ -381,7 +381,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
- <version>3.4.1</version>
+ <version>3.5.0</version>
</plugin>
</plugins>
</pluginManagement>
@@ -623,7 +623,7 @@
<plugin>
<groupId>io.github.git-commit-id</groupId>
<artifactId>git-commit-id-maven-plugin</artifactId>
- <version>7.0.0</version>
+ <version>9.0.1</version>
<executions>
<execution>
<id>get-the-git-infos</id>
@@ -642,18 +642,18 @@
<plugin>
<groupId>org.codehaus.gmavenplus</groupId>
<artifactId>gmavenplus-plugin</artifactId>
- <version>3.0.2</version>
+ <version>4.1.1</version>
<dependencies>
<dependency>
<groupId>org.apache.groovy</groupId>
<artifactId>groovy</artifactId>
- <version>4.0.15</version>
+ <version>4.0.21</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.groovy</groupId>
<artifactId>groovy-ant</artifactId>
- <version>4.0.15</version>
+ <version>4.0.21</version>
<scope>runtime</scope>
</dependency>
</dependencies>
@@ -884,15 +884,33 @@
</dependency>
<dependency>
+ <groupId>commons-codec</groupId>
+ <artifactId>commons-codec</artifactId>
+ <version>1.18.0</version>
+ </dependency>
+
+ <dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>${commons-compress-version}</version>
</dependency>
<dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>2.18.0</version>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ <version>1.3.5</version>
+ </dependency>
+
+ <dependency>
<groupId>org.tukaani</groupId>
<artifactId>xz</artifactId>
- <version>1.9</version>
+ <version>1.10</version>
<optional>true</optional>
</dependency>
@@ -989,7 +1007,7 @@
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
- <version>5.10.0</version>
+ <version>5.15.2</version>
</dependency>
<dependency>
@@ -1098,7 +1116,7 @@
<dependency>
<groupId>org.eclipse.jdt</groupId>
<artifactId>ecj</artifactId>
- <version>3.36.0</version>
+ <version>3.40.0</version>
</dependency>
</dependencies>
</plugin>
diff --git a/tools/BUILD b/tools/BUILD
index 8c424b3..844f004 100644
--- a/tools/BUILD
+++ b/tools/BUILD
@@ -10,6 +10,7 @@
java_runtime = "@rules_java//toolchains:remotejdk_17",
package_configuration = [
":error_prone",
+ ":error_prone_tests",
],
source_version = "17",
target_version = "17",
@@ -22,6 +23,7 @@
java_runtime = "@rules_java//toolchains:remotejdk_21",
package_configuration = [
":error_prone",
+ ":error_prone_tests",
],
source_version = "21",
target_version = "21",
@@ -32,9 +34,7 @@
# enabled. This warnings list is originally based on:
# https://github.com/bazelbuild/BUILD_file_generator/blob/master/tools/bazel_defs/java.bzl
# However, feel free to add any additional errors. Thus far they have all been pretty useful.
-java_package_configuration(
- name = "error_prone",
- javacopts = [
+errorprone_checks = [
"-XepDisableWarningsInGeneratedCode",
# The XepDisableWarningsInGeneratedCode disables only warnings, but
# not errors. We should manually exclude all files generated by
@@ -422,37 +422,57 @@
"-Xep:WrongOneof:ERROR",
"-Xep:XorPower:ERROR",
"-Xep:ZoneIdOfZ:ERROR",
- ],
+]
+
+
+exclude_in_tests = ["-Xep:EmptyBlockTag:WARN",
+ "-Xep:MissingSummary:WARN"]
+
+java_package_configuration(
+ name = "error_prone",
+ javacopts = errorprone_checks,
packages = ["error_prone_packages"],
)
+java_package_configuration(
+ name = "error_prone_tests",
+ javacopts = [ check for check in errorprone_checks if check not in exclude_in_tests],
+ packages = ["error_prone_packages_test"],
+)
+
package_group(
name = "error_prone_packages",
packages = [
- "//org.eclipse.jgit.ant.test/...",
"//org.eclipse.jgit.ant/...",
"//org.eclipse.jgit.archive/...",
- "//org.eclipse.jgit.gpg.bc.test/...",
"//org.eclipse.jgit.gpg.bc/...",
"//org.eclipse.jgit.http.apache/...",
"//org.eclipse.jgit.http.server/...",
- "//org.eclipse.jgit.http.test/...",
"//org.eclipse.jgit.junit.ssh/...",
"//org.eclipse.jgit.junit/...",
"//org.eclipse.jgit.junit/http/...",
- "//org.eclipse.jgit.lfs.server.test/...",
"//org.eclipse.jgit.lfs.server/...",
- "//org.eclipse.jgit.lfs.test/...",
"//org.eclipse.jgit.lfs/...",
- "//org.eclipse.jgit.pgm.test/...",
"//org.eclipse.jgit.pgm/...",
"//org.eclipse.jgit.ssh.apache.agent/...",
- "//org.eclipse.jgit.ssh.apache.test/...",
"//org.eclipse.jgit.ssh.apache/...",
- "//org.eclipse.jgit.ssh.jsch.test/...",
"//org.eclipse.jgit.ssh.jsch/...",
- "//org.eclipse.jgit.test/...",
"//org.eclipse.jgit.ui/...",
"//org.eclipse.jgit/...",
],
)
+
+package_group(
+ name = "error_prone_packages_test",
+ packages = [
+ "//org.eclipse.jgit.ant.test/...",
+ "//org.eclipse.jgit.gpg.bc.test/...",
+ "//org.eclipse.jgit.http.test/...",
+ "//org.eclipse.jgit.lfs.server.test/...",
+ "//org.eclipse.jgit.lfs.test/...",
+ "//org.eclipse.jgit.pgm.test/...",
+ "//org.eclipse.jgit.ssh.apache.test/...",
+ "//org.eclipse.jgit.ssh.jsch.test/...",
+ "//org.eclipse.jgit.test/...",
+ ],
+)
diff --git a/tools/workspace_status.py b/tools/workspace_status.py
index ca9e0a9..1186a4a 100644
--- a/tools/workspace_status.py
+++ b/tools/workspace_status.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# Copyright (C) 2020, David Ostrovsky <david@ostrovsky.org> and others
#
# This program and the accompanying materials are made available under the