Merge branch 'master' into stable-6.5

* master:
  Shortcut during git fetch for avoiding looping through all local refs
  FetchCommand: fix fetchSubmodules to work on a Ref to a blob
  Silence API warnings introduced by I466dcde6
  Allow the exclusions of refs prefixes from bitmap
  PackWriterBitmapPreparer: do not include annotated tags in bitmap
  BatchingProgressMonitor: avoid int overflow when computing percentage
  [pgm] Fetch-CLI: add support for shallow
  Speedup GC listing objects referenced from reflogs
  Re-add servlet-api 4.0 to the target platform
  Upgrade maven plugins
  Cache trustFolderStat/trustPackedRefsStat value per-instance
  Refresh 'objects' dir and retry if a loose object is not found
  FileSnapshotTest: Add more MISSING_FILE coverage

Change-Id: I370bc228481864912c3cd88d43e5a70517b1c186
diff --git a/Documentation/config-options.md b/Documentation/config-options.md
index 9cb3c62..612b845 100644
--- a/Documentation/config-options.md
+++ b/Documentation/config-options.md
@@ -45,7 +45,7 @@
 | `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 and packed-refs file'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. Setting this option to `false` can help to workaround caching issues on NFS, but reduces performance.|
+| `core.trustFolderStat` | `true` | ⃞ | Whether to trust the pack folder's, packed-refs file's and loose-objects folder's file attributes (Java equivalent of stat command on *nix). When looking for pack files, if `false` JGit will always scan the `.git/objects/pack` folder and if set to `true` it assumes that pack files are unchanged if the file attributes of the pack folder are unchanged. When getting the list of packed refs, if `false` JGit will always read the packed-refs file and if set to `true` it uses the file attributes of the packed-refs file and will only read it if a file attribute has changed. When looking for loose objects, if `false` and if a loose object is not found, JGit will open and close a stream to `.git/objects` folder (which can refresh its directory listing, at least on some NFS clients) and retry looking for that loose object. Setting this option to `false` can help to workaround caching issues on NFS, but reduces performance. |
 | `core.trustPackedRefsStat` | `unset` | ⃞ | Whether to trust the file attributes (Java equivalent of stat command on *nix) of the packed-refs file. If `never` JGit will ignore the file attributes of the packed-refs file and always read it. If `always` JGit will trust the file attributes of the packed-refs file and will only read it if a file attribute has changed. `after_open` behaves the same as `always`, except that the packed-refs file is opened and closed before its file attributes are considered. An open/close of the packed-refs file is known to refresh its file attributes, at least on some NFS clients. If `unset`, JGit will use the behavior described in `trustFolderStat`. |
 | `core.worktree` | Root directory of the working tree if it is not the parent directory of the `.git` directory | ✅ | The path to the root of the working tree. |
 
@@ -87,6 +87,7 @@
 | `pack.bitmapContiguousCommitCount` | `100` | ⃞ | Count of most recent commits for which to build bitmaps. |
 | `pack.bitmapDistantCommitSpan` | `5000` | ⃞ | Span of commits when building bitmaps for distant history. |
 | `pack.bitmapExcessiveBranchCount` | `100` | ⃞ | The count of branches deemed "excessive". If the count of branches in a repository exceeds this number and bitmaps are enabled, "inactive" branches will have fewer bitmaps than "active" branches. |
+| `pack.bitmapExcludedRefsPrefixes` | | ⃞ | The refs prefixes to be excluded when building bitmaps. May be specified more than once to exclude multiple prefixes. |
 | `pack.bitmapInactiveBranchAgeInDays` | `90` | ⃞ | Age in days that marks a branch as "inactive" for bitmap creation. |
 | `pack.bitmapRecentCommitCount` | `20000`  | ⃞ | Count at which to switch from `bitmapRecentCommitSpan` to `bitmapDistantCommitSpan`. |
 | `pack.bitmapRecentCommitSpan` | `100` | ⃞ | Span of commits when building bitmaps for recent history. |
diff --git a/org.eclipse.jgit.benchmarks/pom.xml b/org.eclipse.jgit.benchmarks/pom.xml
index 3e37e82..f7ef238 100644
--- a/org.eclipse.jgit.benchmarks/pom.xml
+++ b/org.eclipse.jgit.benchmarks/pom.xml
@@ -56,7 +56,7 @@
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-enforcer-plugin</artifactId>
-        <version>3.0.0-M3</version>
+        <version>3.1.0</version>
         <executions>
           <execution>
             <id>enforce-maven</id>
@@ -76,7 +76,7 @@
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-compiler-plugin</artifactId>
-        <version>3.8.1</version>
+        <version>3.10.1</version>
         <configuration>
           <encoding>UTF-8</encoding>
           <release>${java.version}</release>
@@ -113,7 +113,7 @@
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-shade-plugin</artifactId>
-        <version>3.2.4</version>
+        <version>3.4.1</version>
         <executions>
           <execution>
             <phase>package</phase>
@@ -175,7 +175,7 @@
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-site-plugin</artifactId>
-          <version>3.9.1</version>
+          <version>4.0.0-M4</version>
           <dependencies>
             <dependency><!-- add support for ssh/scp -->
               <groupId>org.apache.maven.wagon</groupId>
@@ -187,17 +187,17 @@
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-surefire-report-plugin</artifactId>
-          <version>3.0.0-M5</version>
+          <version>3.0.0-M8</version>
         </plugin>
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-jxr-plugin</artifactId>
-          <version>3.1.1</version>
+          <version>3.3.0</version>
         </plugin>
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-project-info-reports-plugin</artifactId>
-          <version>3.1.1</version>
+          <version>3.4.2</version>
         </plugin>
       </plugins>
     </pluginManagement>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target
index 8f041a2..72f96f3 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.17" sequenceNumber="1673448236">
+<target name="jgit-4.17" sequenceNumber="1673875570">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.jetty.http" version="10.0.13"/>
@@ -14,6 +14,11 @@
       <repository id="jetty-10.0.x" location="https://download.eclipse.org/oomph/jetty/release/10.0.13/"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+      <unit id="jakarta.servlet-api" version="4.0.0"/>
+      <unit id="jakarta.servlet-api.source" version="4.0.0"/>
+      <repository id="jetty-10.0.6" location="https://download.eclipse.org/eclipse/jetty/10.0.6/"/>
+    </location>
+    <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="com.google.gson" version="2.10.0.v20221207-1049"/>
       <unit id="com.google.gson.source" version="2.10.0.v20221207-1049"/>
       <unit id="com.jcraft.jsch" version="0.1.55.v20221112-0806"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.target
index 8f93f0f..a871c58 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.target
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.18" sequenceNumber="1673448236">
+<target name="jgit-4.18" sequenceNumber="1673875570">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.jetty.http" version="10.0.13"/>
@@ -14,6 +14,11 @@
       <repository id="jetty-10.0.x" location="https://download.eclipse.org/oomph/jetty/release/10.0.13/"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+      <unit id="jakarta.servlet-api" version="4.0.0"/>
+      <unit id="jakarta.servlet-api.source" version="4.0.0"/>
+      <repository id="jetty-10.0.6" location="https://download.eclipse.org/eclipse/jetty/10.0.6/"/>
+    </location>
+    <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="com.google.gson" version="2.10.0.v20221207-1049"/>
       <unit id="com.google.gson.source" version="2.10.0.v20221207-1049"/>
       <unit id="com.jcraft.jsch" version="0.1.55.v20221112-0806"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.target
index 88599aa..819a065 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19.target
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.19-staging" sequenceNumber="1673448236">
+<target name="jgit-4.19-staging" sequenceNumber="1673875570">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.jetty.http" version="10.0.13"/>
@@ -14,6 +14,11 @@
       <repository id="jetty-10.0.x" location="https://download.eclipse.org/oomph/jetty/release/10.0.13/"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+      <unit id="jakarta.servlet-api" version="4.0.0"/>
+      <unit id="jakarta.servlet-api.source" version="4.0.0"/>
+      <repository id="jetty-10.0.6" location="https://download.eclipse.org/eclipse/jetty/10.0.6/"/>
+    </location>
+    <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="com.google.gson" version="2.10.0.v20221207-1049"/>
       <unit id="com.google.gson.source" version="2.10.0.v20221207-1049"/>
       <unit id="com.jcraft.jsch" version="0.1.55.v20221112-0806"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.target
index 5a8dfae..cd615ff 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.20.target
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.20" sequenceNumber="1673448236">
+<target name="jgit-4.20" sequenceNumber="1673875570">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.jetty.http" version="10.0.13"/>
@@ -14,6 +14,11 @@
       <repository id="jetty-10.0.x" location="https://download.eclipse.org/oomph/jetty/release/10.0.13/"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+      <unit id="jakarta.servlet-api" version="4.0.0"/>
+      <unit id="jakarta.servlet-api.source" version="4.0.0"/>
+      <repository id="jetty-10.0.6" location="https://download.eclipse.org/eclipse/jetty/10.0.6/"/>
+    </location>
+    <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="com.google.gson" version="2.10.0.v20221207-1049"/>
       <unit id="com.google.gson.source" version="2.10.0.v20221207-1049"/>
       <unit id="com.jcraft.jsch" version="0.1.55.v20221112-0806"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.target
index d5ccbc1..993c93f 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.21.target
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.21" sequenceNumber="1673448236">
+<target name="jgit-4.21" sequenceNumber="1673875570">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.jetty.http" version="10.0.13"/>
@@ -14,6 +14,11 @@
       <repository id="jetty-10.0.x" location="https://download.eclipse.org/oomph/jetty/release/10.0.13/"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+      <unit id="jakarta.servlet-api" version="4.0.0"/>
+      <unit id="jakarta.servlet-api.source" version="4.0.0"/>
+      <repository id="jetty-10.0.6" location="https://download.eclipse.org/eclipse/jetty/10.0.6/"/>
+    </location>
+    <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="com.google.gson" version="2.10.0.v20221207-1049"/>
       <unit id="com.google.gson.source" version="2.10.0.v20221207-1049"/>
       <unit id="com.jcraft.jsch" version="0.1.55.v20221112-0806"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.22.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.22.target
index 04b6af5..86444a5 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.22.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.22.target
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.22" sequenceNumber="1673448234">
+<target name="jgit-4.22" sequenceNumber="1673875570">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.jetty.http" version="10.0.13"/>
@@ -14,6 +14,11 @@
       <repository id="jetty-10.0.x" location="https://download.eclipse.org/oomph/jetty/release/10.0.13/"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+      <unit id="jakarta.servlet-api" version="4.0.0"/>
+      <unit id="jakarta.servlet-api.source" version="4.0.0"/>
+      <repository id="jetty-10.0.6" location="https://download.eclipse.org/eclipse/jetty/10.0.6/"/>
+    </location>
+    <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="com.google.gson" version="2.10.0.v20221207-1049"/>
       <unit id="com.google.gson.source" version="2.10.0.v20221207-1049"/>
       <unit id="com.jcraft.jsch" version="0.1.55.v20221112-0806"/>
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
index a4a3c64..2c1692f 100644
--- 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
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.23" sequenceNumber="1673448234">
+<target name="jgit-4.23" sequenceNumber="1673875570">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.jetty.http" version="10.0.13"/>
@@ -14,6 +14,11 @@
       <repository id="jetty-10.0.x" location="https://download.eclipse.org/oomph/jetty/release/10.0.13/"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+      <unit id="jakarta.servlet-api" version="4.0.0"/>
+      <unit id="jakarta.servlet-api.source" version="4.0.0"/>
+      <repository id="jetty-10.0.6" location="https://download.eclipse.org/eclipse/jetty/10.0.6/"/>
+    </location>
+    <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="com.google.gson" version="2.10.0.v20221207-1049"/>
       <unit id="com.google.gson.source" version="2.10.0.v20221207-1049"/>
       <unit id="com.jcraft.jsch" version="0.1.55.v20221112-0806"/>
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
index 4fed83f..3c296b5 100644
--- 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
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.24" sequenceNumber="1673448234">
+<target name="jgit-4.24" sequenceNumber="1673875570">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.jetty.http" version="10.0.13"/>
@@ -14,6 +14,11 @@
       <repository id="jetty-10.0.x" location="https://download.eclipse.org/oomph/jetty/release/10.0.13/"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+      <unit id="jakarta.servlet-api" version="4.0.0"/>
+      <unit id="jakarta.servlet-api.source" version="4.0.0"/>
+      <repository id="jetty-10.0.6" location="https://download.eclipse.org/eclipse/jetty/10.0.6/"/>
+    </location>
+    <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="com.google.gson" version="2.10.0.v20221207-1049"/>
       <unit id="com.google.gson.source" version="2.10.0.v20221207-1049"/>
       <unit id="com.jcraft.jsch" version="0.1.55.v20221112-0806"/>
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
index 1a7d8bb..ad5d995 100644
--- 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
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.25" sequenceNumber="1673448234">
+<target name="jgit-4.25" sequenceNumber="1673875570">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.jetty.http" version="10.0.13"/>
@@ -14,6 +14,11 @@
       <repository id="jetty-10.0.x" location="https://download.eclipse.org/oomph/jetty/release/10.0.13/"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+      <unit id="jakarta.servlet-api" version="4.0.0"/>
+      <unit id="jakarta.servlet-api.source" version="4.0.0"/>
+      <repository id="jetty-10.0.6" location="https://download.eclipse.org/eclipse/jetty/10.0.6/"/>
+    </location>
+    <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="com.google.gson" version="2.10.0.v20221207-1049"/>
       <unit id="com.google.gson.source" version="2.10.0.v20221207-1049"/>
       <unit id="com.jcraft.jsch" version="0.1.55.v20221112-0806"/>
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
index fd1f0b0..dce8c55 100644
--- 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
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.26" sequenceNumber="1673448442">
+<target name="jgit-4.26" sequenceNumber="1673875557">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.jetty.http" version="10.0.13"/>
@@ -14,6 +14,11 @@
       <repository id="jetty-10.0.x" location="https://download.eclipse.org/oomph/jetty/release/10.0.13/"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
+      <unit id="jakarta.servlet-api" version="4.0.0"/>
+      <unit id="jakarta.servlet-api.source" version="4.0.0"/>
+      <repository id="jetty-10.0.6" location="https://download.eclipse.org/eclipse/jetty/10.0.6/"/>
+    </location>
+    <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="com.google.gson" version="2.10.0.v20221207-1049"/>
       <unit id="com.google.gson.source" version="2.10.0.v20221207-1049"/>
       <unit id="com.jcraft.jsch" version="0.1.55.v20221112-0806"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-10.0.x.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-10.0.x.tpd
index 9fe89e2..e1afcff 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-10.0.x.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-10.0.x.tpd
@@ -9,3 +9,8 @@
 	org.eclipse.jetty.util [10.0.13,10.0.14]
 	org.eclipse.jetty.util.ajax [10.0.13,10.0.14]
 }
+
+location jetty-10.0.6 "https://download.eclipse.org/eclipse/jetty/10.0.6/" {
+	jakarta.servlet-api [4.0.0, 5.0.0)
+	jakarta.servlet-api.source [4.0.0, 5.0.0)
+}
\ No newline at end of file
diff --git a/org.eclipse.jgit.packaging/pom.xml b/org.eclipse.jgit.packaging/pom.xml
index 06e9680..53ca7e3 100644
--- a/org.eclipse.jgit.packaging/pom.xml
+++ b/org.eclipse.jgit.packaging/pom.xml
@@ -24,7 +24,6 @@
   <properties>
     <java.version>11</java.version>
     <tycho-version>2.7.5</tycho-version>
-    <tycho-extras-version>${tycho-version}</tycho-extras-version>
     <target-platform>jgit-4.17</target-platform>
   </properties>
 
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 4cbd61c..cbb5bbb 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
@@ -17,14 +17,20 @@
 import static org.junit.Assert.assertTrue;
 
 import java.io.File;
+import java.time.Instant;
 import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
 
 import org.eclipse.jgit.api.Git;
 import org.eclipse.jgit.junit.JGitTestUtil;
 import org.eclipse.jgit.junit.MockSystemReader;
+import org.eclipse.jgit.junit.TestRepository;
 import org.eclipse.jgit.lib.CLIRepositoryTestCase;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.PersonIdent;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.RefUpdate;
 import org.eclipse.jgit.lib.Repository;
@@ -41,10 +47,14 @@
 
 	private Git git;
 
+	private TestRepository<Repository> tr;
+
 	@Override
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
+
+		tr = new TestRepository<>(db);
 		git = new Git(db);
 	}
 
@@ -112,6 +122,22 @@
 		return git.commit().setMessage("Initial commit").call();
 	}
 
+	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()))
+				.setMessage("Second commit").call();
+	}
+
+	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()))
+				.setMessage("Third commit").call();
+	}
+
 	@Test
 	public void testCloneEmpty() throws Exception {
 		File gitDir = db.getDirectory();
@@ -203,4 +229,117 @@
 		assertEquals("refs/*", fetchRefSpec.getDestination());
 		assertNotNull(git2.getRepository().exactRef("refs/meta/foo/bar"));
 	}
+
+	@Test
+	public void testDepth() throws Exception {
+		createInitialCommit();
+		createSecondCommit();
+		createThirdCommit();
+
+		File gitDir = db.getDirectory();
+		String sourceURI = gitDir.toURI().toString();
+		File target = createTempDirectory("target");
+		String cmd = "git clone --depth 1 " + sourceURI + " "
+				+ shellQuote(target.getPath());
+		String[] result = execute(cmd);
+		assertArrayEquals(new String[] {
+				"Cloning into '" + target.getPath() + "'...", "", "" }, result);
+
+		Git git2 = Git.open(target);
+		addRepoToClose(git2.getRepository());
+
+		List<RevCommit> log = StreamSupport
+				.stream(git2.log().all().call().spliterator(), false)
+				.collect(Collectors.toList());
+		assertEquals(1, log.size());
+		RevCommit commit = log.get(0);
+		assertEquals(Set.of(commit.getId()),
+				git2.getRepository().getObjectDatabase().getShallowCommits());
+		assertEquals("Third commit", commit.getFullMessage());
+		assertEquals(0, commit.getParentCount());
+	}
+
+	@Test
+	public void testDepth2() throws Exception {
+		createInitialCommit();
+		createSecondCommit();
+		createThirdCommit();
+
+		File gitDir = db.getDirectory();
+		String sourceURI = gitDir.toURI().toString();
+		File target = createTempDirectory("target");
+		String cmd = "git clone --depth 2 " + sourceURI + " "
+				+ shellQuote(target.getPath());
+		String[] result = execute(cmd);
+		assertArrayEquals(new String[] {
+				"Cloning into '" + target.getPath() + "'...", "", "" }, result);
+
+		Git git2 = Git.open(target);
+		addRepoToClose(git2.getRepository());
+
+		List<RevCommit> log = StreamSupport
+				.stream(git2.log().all().call().spliterator(), false)
+				.collect(Collectors.toList());
+		assertEquals(2, log.size());
+		assertEquals(List.of("Third commit", "Second commit"), log.stream()
+				.map(RevCommit::getFullMessage).collect(Collectors.toList()));
+	}
+
+	@Test
+	public void testCloneRepositoryWithShallowSince() throws Exception {
+		createInitialCommit();
+		tr.tick(30);
+		RevCommit secondCommit = createSecondCommit();
+		tr.tick(45);
+		createThirdCommit();
+
+		File gitDir = db.getDirectory();
+		String sourceURI = gitDir.toURI().toString();
+		File target = createTempDirectory("target");
+		String cmd = "git clone --shallow-since="
+				+ Instant.ofEpochSecond(secondCommit.getCommitTime()).toString()
+				+ " " + sourceURI + " " + shellQuote(target.getPath());
+		String[] result = execute(cmd);
+		assertArrayEquals(new String[] {
+				"Cloning into '" + target.getPath() + "'...", "", "" }, result);
+
+		Git git2 = Git.open(target);
+		addRepoToClose(git2.getRepository());
+
+		List<RevCommit> log = StreamSupport
+				.stream(git2.log().all().call().spliterator(), false)
+				.collect(Collectors.toList());
+		assertEquals(2, log.size());
+		assertEquals(List.of("Third commit", "Second commit"), log.stream()
+				.map(RevCommit::getFullMessage).collect(Collectors.toList()));
+	}
+
+	@Test
+	public void testCloneRepositoryWithShallowExclude() throws Exception {
+		final RevCommit firstCommit = createInitialCommit();
+		final RevCommit secondCommit = createSecondCommit();
+		createThirdCommit();
+
+		File gitDir = db.getDirectory();
+		String sourceURI = gitDir.toURI().toString();
+		File target = createTempDirectory("target");
+		String cmd = "git clone --shallow-exclude="
+				+ firstCommit.getId().getName() + " --shallow-exclude="
+				+ secondCommit.getId().getName() + " " + sourceURI + " "
+				+ shellQuote(target.getPath());
+		String[] result = execute(cmd);
+		assertArrayEquals(new String[] {
+				"Cloning into '" + target.getPath() + "'...", "", "" }, result);
+
+		Git git2 = Git.open(target);
+		addRepoToClose(git2.getRepository());
+
+		List<RevCommit> log = StreamSupport
+				.stream(git2.log().all().call().spliterator(), false)
+				.collect(Collectors.toList());
+		assertEquals(1, log.size());
+		assertEquals(List.of("Third commit"), log.stream()
+				.map(RevCommit::getFullMessage).collect(Collectors.toList()));
+	}
+
 }
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 48f4e85..98d711d 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
@@ -137,6 +137,7 @@
 metaVar_commitPaths=paths
 metaVar_configFile=FILE
 metaVar_connProp=conn.prop
+metaVar_depth=<depth>
 metaVar_diffAlg=ALGORITHM
 metaVar_directory=DIRECTORY
 metaVar_extraArgument=ours|theirs
@@ -144,6 +145,7 @@
 metaVar_filepattern=filepattern
 metaVar_gitDir=GIT_DIR
 metaVar_hostName=HOSTNAME
+metaVar_instant=<instant>
 metaVar_lfsStorage=STORAGE
 metaVar_linesOfContext=lines
 metaVar_message=message
@@ -168,6 +170,8 @@
 metaVar_s3StorageClass=STORAGE-CLASS
 metaVar_seconds=SECONDS
 metaVar_service=SERVICE
+metaVar_shallowExclude=<revision>
+metaVar_shallowSince=<date>
 metaVar_tagLocalUser=<GPG key ID>
 metaVar_tool=TOOL
 metaVar_treeish=tree-ish
@@ -374,6 +378,7 @@
 usage_diffAlgorithm=the diff algorithm to use. Currently supported are: 'myers', 'histogram'
 usage_DiffTool=git difftool is a Git command that allows you to compare and edit files between revisions using common diff tools.\ngit difftool is a frontend to git diff and accepts the same options and arguments.
 usage_MergeTool=git-mergetool - Run merge conflict resolution tools to resolve merge conflicts.\nUse git mergetool to run one of several merge utilities to resolve merge conflicts. It is typically run after git merge.
+usage_depth=Limit fetching to the specified number of commits from the tip of each remote branch history.
 usage_directoriesToExport=directories to export
 usage_disableTheServiceInAllRepositories=disable the service in all repositories
 usage_displayAListOfAllRegisteredJgitCommands=Display a list of all registered jgit commands
@@ -447,6 +452,8 @@
 usage_runLfsStore=Run LFS Store in a given directory
 usage_S3NoSslVerify=Skip verification of Amazon server certificate and hostname
 usage_setTheGitRepositoryToOperateOn=set the git repository to operate on
+usage_shallowExclude=Deepen or shorten the history of a shallow repository to exclude commits reachable from a specified remote branch or tag. 
+usage_shallowSince=Deepen or shorten the history of a shallow repository to include all reachable commits after <date>.
 usage_show=Display one commit
 usage_showRefNamesMatchingCommits=Show ref names matching commits
 usage_showPatch=display patch
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java
index f28915d..9f9fa8f 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java
@@ -13,7 +13,10 @@
 import java.io.File;
 import java.io.IOException;
 import java.text.MessageFormat;
+import java.time.Instant;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
 
 import org.eclipse.jgit.api.CloneCommand;
 import org.eclipse.jgit.api.Git;
@@ -48,6 +51,15 @@
 	@Option(name = "--quiet", usage = "usage_quiet")
 	private Boolean quiet;
 
+	@Option(name = "--depth", metaVar = "metaVar_depth", usage = "usage_depth")
+	private Integer depth = null;
+
+	@Option(name = "--shallow-since", metaVar = "metaVar_shallowSince", usage = "usage_shallowSince")
+	private Instant shallowSince = null;
+
+	@Option(name = "--shallow-exclude", metaVar = "metaVar_shallowExclude", usage = "usage_shallowExclude")
+	private List<String> shallowExcludes = new ArrayList<>();
+
 	@Option(name = "--recurse-submodules", usage = "usage_recurseSubmodules")
 	private boolean cloneSubmodules;
 
@@ -97,6 +109,16 @@
 				.setMirror(isMirror).setNoCheckout(noCheckout).setBranch(branch)
 				.setCloneSubmodules(cloneSubmodules).setTimeout(timeout);
 
+		if (depth != null) {
+			command.setDepth(depth.intValue());
+		}
+		if (shallowSince != null) {
+			command.setShallowSince(shallowSince);
+		}
+		for (String shallowExclude : shallowExcludes) {
+			command.addShallowExclude(shallowExclude);
+		}
+
 		command.setGitDir(gitdir == null ? null : new File(gitdir));
 		command.setDirectory(localNameF);
 		boolean msgs = quiet == null || !quiet.booleanValue();
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Fetch.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Fetch.java
index fbce4a5..2e0c36b 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Fetch.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Fetch.java
@@ -14,6 +14,8 @@
 
 import java.io.IOException;
 import java.text.MessageFormat;
+import java.time.Instant;
+import java.util.ArrayList;
 import java.util.List;
 
 import org.eclipse.jgit.api.FetchCommand;
@@ -62,6 +64,15 @@
 	@Option(name = "--tags", usage="usage_tags", aliases = { "-t" })
 	private Boolean tags;
 
+	@Option(name = "--depth", metaVar = "metaVar_depth", usage = "usage_depth")
+	private Integer depth = null;
+
+	@Option(name = "--shallow-since", metaVar = "metaVar_shallowSince", usage = "usage_shallowSince")
+	private Instant shallowSince = null;
+
+	@Option(name = "--shallow-exclude", metaVar = "metaVar_shallowExclude", usage = "usage_shallowExclude")
+	private List<String> shallowExcludes = new ArrayList<>();
+
 	@Option(name = "--no-tags", usage = "usage_notags", aliases = { "-n" })
 	void notags(@SuppressWarnings("unused")
 	final boolean ignored) {
@@ -120,6 +131,15 @@
 				fetch.setTagOpt(tags.booleanValue() ? TagOpt.FETCH_TAGS
 						: TagOpt.NO_TAGS);
 			}
+			if (depth != null) {
+				fetch.setDepth(depth.intValue());
+			}
+			if (shallowSince != null) {
+				fetch.setShallowSince(shallowSince);
+			}
+			for (String shallowExclude : shallowExcludes) {
+				fetch.addShallowExclude(shallowExclude);
+			}
 			if (0 <= timeout) {
 				fetch.setTimeout(timeout);
 			}
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 490f800..d07268b 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
@@ -214,6 +214,7 @@
 	/***/ public String metaVar_filepattern;
 	/***/ public String metaVar_gitDir;
 	/***/ public String metaVar_hostName;
+	/***/ public String metaVar_instant;
 	/***/ public String metaVar_lfsStorage;
 	/***/ public String metaVar_linesOfContext;
 	/***/ public String metaVar_message;
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/CmdLineParser.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/CmdLineParser.java
index 5d32e65..df0b39b 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/CmdLineParser.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/CmdLineParser.java
@@ -13,6 +13,7 @@
 import java.io.IOException;
 import java.io.Writer;
 import java.lang.reflect.Field;
+import java.time.Instant;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
@@ -55,6 +56,7 @@
 		registry.registerHandler(RevCommit.class, RevCommitHandler.class);
 		registry.registerHandler(RevTree.class, RevTreeHandler.class);
 		registry.registerHandler(List.class, OptionWithValuesListHandler.class);
+		registry.registerHandler(Instant.class, InstantHandler.class);
 	}
 
 	private final Repository db;
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/InstantHandler.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/InstantHandler.java
new file mode 100644
index 0000000..feee78e
--- /dev/null
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/InstantHandler.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2022, Harald Weiner and others
+ *
+ * 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.opt;
+
+import java.time.Instant;
+
+import org.eclipse.jgit.pgm.internal.CLIText;
+import org.kohsuke.args4j.CmdLineException;
+import org.kohsuke.args4j.CmdLineParser;
+import org.kohsuke.args4j.OptionDef;
+import org.kohsuke.args4j.spi.OptionHandler;
+import org.kohsuke.args4j.spi.Parameters;
+import org.kohsuke.args4j.spi.Setter;
+
+/**
+ * Custom argument handler {@link java.time.Instant} from string values.
+ * <p>
+ * Assumes the parser has been initialized with a Repository.
+ *
+ * @since 6.5
+ */
+public class InstantHandler extends OptionHandler<Instant> {
+	/**
+	 * Create a new handler for the command name.
+	 * <p>
+	 * This constructor is used only by args4j.
+	 *
+	 * @param parser
+	 *            a {@link org.kohsuke.args4j.CmdLineParser} object.
+	 * @param option
+	 *            a {@link org.kohsuke.args4j.OptionDef} object.
+	 * @param setter
+	 *            a {@link org.kohsuke.args4j.spi.Setter} object.
+	 */
+	public InstantHandler(CmdLineParser parser, OptionDef option,
+			Setter<? super Instant> setter) {
+		super(parser, option, setter);
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public int parseArguments(Parameters params) throws CmdLineException {
+		Instant instant = Instant.parse(params.getParameter(0));
+		setter.addValue(instant);
+		return 1;
+	}
+
+	/** {@inheritDoc} */
+	@Override
+	public String getDefaultMetaVariable() {
+		return CLIText.get().metaVar_instant;
+	}
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileSnapshotTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileSnapshotTest.java
index 5e87b8f..12773c2 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileSnapshotTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileSnapshotTest.java
@@ -209,6 +209,20 @@
 		assertTrue(fs2.equals(fs1));
 	}
 
+	@Test
+	public void snapshotAndFileMissingIsNotModified() throws Exception {
+		File doesNotExist = trash.resolve("DOES_NOT_EXIST").toFile();
+		FileSnapshot missing = FileSnapshot.save(doesNotExist);
+		assertFalse(missing.isModified(doesNotExist));
+	}
+
+	@Test
+	public void missingFileEquals() throws Exception {
+		FileSnapshot missing = FileSnapshot.save(
+				trash.resolve("DOES_NOT_EXIST").toFile());
+		assertTrue(missing.equals(FileSnapshot.MISSING_FILE));
+	}
+
 	@SuppressWarnings("boxing")
 	@Test
 	public void detectFileModified() throws IOException {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/pack/GcCommitSelectionTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/pack/GcCommitSelectionTest.java
index bb56c84..0c09ad1 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/pack/GcCommitSelectionTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/pack/GcCommitSelectionTest.java
@@ -83,6 +83,40 @@
 	}
 
 	@Test
+	public void testBitmapDoesNotIncludeAnnotatedTags() throws Exception {
+		/*
+		 * Make sure that the bitmap generated for the following commit
+		 * graph does not include commit2 because it is not reachable by any
+		 * heads, despite being reachable from tag1 through the annotated-tag1.
+		 *
+		 * refs/heads/main
+		 *   ^
+		 *   |
+		 *  commit1 <-- commit2 <- annotated-tag1 <- tag1
+		 *   ^
+		 *   |
+		 *  commit0
+		 */
+		String mainBranch = "refs/heads/main";
+		BranchBuilder bb = tr.branch(mainBranch);
+
+		String commitMsg = "commit msg";
+		String fileBody = "file body";
+		String tagName = "tag1";
+		bb.commit().message(commitMsg + " 1").add("file1", fileBody).create();
+		RevCommit commit1 = bb.commit().message(commitMsg + " 2").add("file2", fileBody).create();
+		RevCommit commit2 = bb.commit().message(commitMsg + " 3").add("file3", fileBody).create();
+		tr.lightweightTag(tagName, tr.tag(tagName, commit2));
+		tr.branch(mainBranch).update(commit1);
+
+		gc.setExpireAgeMillis(0);
+		gc.gc();
+
+		// Create only 2 bitmaps, for commit0 and commit1, excluding commit2
+		assertEquals(2, gc.getStatistics().numberOfBitmaps);
+	}
+
+	@Test
 	public void testBitmapSpansWithMerges() throws Exception {
 		/*
 		 * Commits that are merged. Since 55 is in the oldest history it is
@@ -187,6 +221,24 @@
 	}
 
 	@Test
+	public void testBitmapsForExcludedBranches() throws Exception {
+		createNewCommitOnNewBranch("main");
+		createNewCommitOnNewBranch("other");
+		PackConfig packConfig = new PackConfig();
+		packConfig.setBitmapExcludedRefsPrefixes(new String[] { "refs/heads/other" });
+		gc.setPackConfig(packConfig);
+		gc.gc();
+		assertEquals(1,
+			gc.getStatistics().numberOfBitmaps);
+	}
+
+	private void createNewCommitOnNewBranch(String branchName) throws Exception {
+		BranchBuilder bb = tr.branch("refs/heads/" + branchName);
+		String msg = "New branch " + branchName;
+		bb.commit().message(msg).add("some-filename.txt", msg).create();
+	}
+
+	@Test
 	public void testSelectionOrderingWithChains() throws Exception {
 		/*-
 		 * Create a history like this, where 'N' is the number of seconds from
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/TextProgressMonitorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/TextProgressMonitorTest.java
new file mode 100644
index 0000000..55ca2cd
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/TextProgressMonitorTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2023, SAP SE or an SAP affiliate company and others
+ *
+ * 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.ByteArrayOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.StandardCharsets;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TextProgressMonitorTest {
+
+	private TextProgressMonitor m;
+
+	private ByteArrayOutputStream buf;
+
+	@Before
+	public void setup() {
+		buf = new ByteArrayOutputStream();
+		m = new TextProgressMonitor(
+				new OutputStreamWriter(buf, StandardCharsets.UTF_8));
+	}
+
+	@Test
+	public void testSimple() throws Exception {
+		m.beginTask("task", 10);
+		for (int i = 0; i < 10; i++) {
+			m.update(1);
+		}
+		m.endTask();
+		Assert.assertArrayEquals(
+				new String[] { "", "task:                    10% ( 1/10)",
+						"task:                    20% ( 2/10)",
+						"task:                    30% ( 3/10)",
+						"task:                    40% ( 4/10)",
+						"task:                    50% ( 5/10)",
+						"task:                    60% ( 6/10)",
+						"task:                    70% ( 7/10)",
+						"task:                    80% ( 8/10)",
+						"task:                    90% ( 9/10)",
+						"task:                   100% (10/10)",
+						"task:                   100% (10/10)\n" },
+				bufLines());
+	}
+
+	@Test
+	public void testLargeNumbers() throws Exception {
+		m.beginTask("task", 1_000_000_000);
+		for (int i = 0; i < 10; i++) {
+			m.update(100_000_000);
+		}
+		m.endTask();
+		Assert.assertArrayEquals(
+				new String[] { "",
+						"task:                    10% ( 100000000/1000000000)",
+						"task:                    20% ( 200000000/1000000000)",
+						"task:                    30% ( 300000000/1000000000)",
+						"task:                    40% ( 400000000/1000000000)",
+						"task:                    50% ( 500000000/1000000000)",
+						"task:                    60% ( 600000000/1000000000)",
+						"task:                    70% ( 700000000/1000000000)",
+						"task:                    80% ( 800000000/1000000000)",
+						"task:                    90% ( 900000000/1000000000)",
+						"task:                   100% (1000000000/1000000000)",
+						"task:                   100% (1000000000/1000000000)\n" },
+				bufLines());
+	}
+
+	String[] bufLines() throws UnsupportedEncodingException {
+		String s = new String(buf.toString(StandardCharsets.UTF_8.name()));
+		return s.split("\r");
+	}
+}
diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters
index cc152f9..107b7d5 100644
--- a/org.eclipse.jgit/.settings/.api_filters
+++ b/org.eclipse.jgit/.settings/.api_filters
@@ -3,6 +3,12 @@
     <resource path="src/org/eclipse/jgit/lib/ConfigConstants.java" type="org.eclipse.jgit.lib.ConfigConstants">
         <filter id="1142947843">
             <message_arguments>
+                <message_argument value="5.13.2"/>
+                <message_argument value="CONFIG_KEY_BITMAP_EXCLUDED_REFS_PREFIXES"/>
+            </message_arguments>
+        </filter>
+        <filter id="1142947843">
+            <message_arguments>
                 <message_argument value="6.1.1"/>
                 <message_argument value="CONFIG_KEY_TRUST_PACKED_REFS_STAT"/>
             </message_arguments>
@@ -32,4 +38,38 @@
             </message_arguments>
         </filter>
     </resource>
+    <resource path="src/org/eclipse/jgit/lib/Repository.java" type="org.eclipse.jgit.lib.Repository">
+        <filter id="1142947843">
+            <message_arguments>
+                <message_argument value="5.13.2"/>
+                <message_argument value="getReflogReader(Ref)"/>
+            </message_arguments>
+        </filter>
+    </resource>
+    <resource path="src/org/eclipse/jgit/storage/pack/PackConfig.java" type="org.eclipse.jgit.storage.pack.PackConfig">
+        <filter id="336658481">
+            <message_arguments>
+                <message_argument value="org.eclipse.jgit.storage.pack.PackConfig"/>
+                <message_argument value="DEFAULT_BITMAP_EXCLUDED_REFS_PREFIXES"/>
+            </message_arguments>
+        </filter>
+        <filter id="1142947843">
+            <message_arguments>
+                <message_argument value="5.13.2"/>
+                <message_argument value="DEFAULT_BITMAP_EXCLUDED_REFS_PREFIXES"/>
+            </message_arguments>
+        </filter>
+        <filter id="1142947843">
+            <message_arguments>
+                <message_argument value="5.13.2"/>
+                <message_argument value="getBitmapExcludedRefsPrefixes()"/>
+            </message_arguments>
+        </filter>
+        <filter id="1142947843">
+            <message_arguments>
+                <message_argument value="5.13.2"/>
+                <message_argument value="setBitmapExcludedRefsPrefixes(String[])"/>
+            </message_arguments>
+        </filter>
+    </resource>
 </component>
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 e7a8be0..3c772c2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java
@@ -151,6 +151,9 @@
 			if (fetchHead == null) {
 				return;
 			}
+			if (revWalk.parseAny(fetchHead).getType() == Constants.OBJ_BLOB) {
+				return;
+			}
 			walk.setTree(revWalk.parseTree(fetchHead));
 			while (walk.next()) {
 				try (Repository submoduleRepo = walk.getRepository()) {
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 3ebce6c..3e92cdd 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
@@ -31,6 +31,7 @@
 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.JGitInternalException;
 import org.eclipse.jgit.attributes.AttributesNode;
@@ -530,6 +531,12 @@
 		return new ReflogReaderImpl(this, ref.getName());
 	}
 
+	@Override
+	public @NonNull ReflogReader getReflogReader(@NonNull Ref ref)
+			throws IOException {
+		return new ReflogReaderImpl(this, ref.getName());
+	}
+
 	/** {@inheritDoc} */
 	@Override
 	public AttributesNodeProvider createAttributesNodeProvider() {
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 273d658..d41aea4 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
@@ -800,6 +800,10 @@
 		Set<ObjectId> tagTargets = new HashSet<>();
 		Set<ObjectId> indexObjects = listNonHEADIndexObjects();
 
+		Set<ObjectId> refsToExcludeFromBitmap = repo.getRefDatabase()
+				.getRefsByPrefix(pconfig.getBitmapExcludedRefsPrefixes())
+				.stream().map(Ref::getObjectId).collect(Collectors.toSet());
+
 		for (Ref ref : refsBefore) {
 			checkCancelled();
 			nonHeads.addAll(listRefLogObjects(ref, 0));
@@ -844,7 +848,7 @@
 		Pack heads = null;
 		if (!allHeadsAndTags.isEmpty()) {
 			heads = writePack(allHeadsAndTags, PackWriter.NONE, allTags,
-					tagTargets, excluded);
+					refsToExcludeFromBitmap, tagTargets, excluded);
 			if (heads != null) {
 				ret.add(heads);
 				excluded.add(0, heads.getIndex());
@@ -852,13 +856,13 @@
 		}
 		if (!nonHeads.isEmpty()) {
 			Pack rest = writePack(nonHeads, allHeadsAndTags, PackWriter.NONE,
-					tagTargets, excluded);
+					PackWriter.NONE, tagTargets, excluded);
 			if (rest != null)
 				ret.add(rest);
 		}
 		if (!txnHeads.isEmpty()) {
 			Pack txn = writePack(txnHeads, PackWriter.NONE, PackWriter.NONE,
-					null, excluded);
+					PackWriter.NONE, null, excluded);
 			if (txn != null)
 				ret.add(txn);
 		}
@@ -1127,10 +1131,7 @@
 	 * @throws IOException
 	 */
 	private Set<ObjectId> listRefLogObjects(Ref ref, long minTime) throws IOException {
-		ReflogReader reflogReader = repo.getReflogReader(ref.getName());
-		if (reflogReader == null) {
-			return Collections.emptySet();
-		}
+		ReflogReader reflogReader = repo.getReflogReader(ref);
 		List<ReflogEntry> rlEntries = reflogReader
 				.getReverseEntries();
 		if (rlEntries == null || rlEntries.isEmpty())
@@ -1234,6 +1235,7 @@
 
 	private Pack writePack(@NonNull Set<? extends ObjectId> want,
 			@NonNull Set<? extends ObjectId> have, @NonNull Set<ObjectId> tags,
+			@NonNull Set<ObjectId> excludedRefsTips,
 			Set<ObjectId> tagTargets, List<ObjectIdSet> excludeObjects)
 			throws IOException {
 		checkCancelled();
@@ -1265,7 +1267,8 @@
 			if (excludeObjects != null)
 				for (ObjectIdSet idx : excludeObjects)
 					pw.excludeObjects(idx);
-			pw.preparePack(pm, want, have, PackWriter.NONE, tags);
+			pw.preparePack(pm, want, have, PackWriter.NONE,
+					union(tags, excludedRefsTips));
 			if (pw.getObjectCount() == 0)
 				return null;
 			checkCancelled();
@@ -1378,6 +1381,15 @@
 		}
 	}
 
+	private Set<? extends ObjectId> union(Set<ObjectId> tags,
+			Set<ObjectId> excludedRefsHeadsTips) {
+		HashSet<ObjectId> unionSet = new HashSet<>(
+				tags.size() + excludedRefsHeadsTips.size());
+		unionSet.addAll(tags);
+		unionSet.addAll(excludedRefsHeadsTips);
+		return unionSet;
+	}
+
 	private void checkCancelled() throws CancelledException {
 		if (pm.isCancelled() || Thread.currentThread().isInterrupted()) {
 			throw new CancelledException(JGitText.get().operationCanceled);
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 33621a1..b9af83d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LooseObjects.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LooseObjects.java
@@ -14,6 +14,7 @@
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.io.InputStream;
 import java.nio.file.Files;
 import java.nio.file.NoSuchFileException;
 import java.nio.file.StandardCopyOption;
@@ -24,6 +25,8 @@
 import org.eclipse.jgit.internal.storage.file.FileObjectDatabase.InsertLooseObjectResult;
 import org.eclipse.jgit.lib.AbbreviatedObjectId;
 import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.ConfigConstants;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectLoader;
@@ -52,15 +55,22 @@
 
 	private final UnpackedObjectCache unpackedObjectCache;
 
+	private final boolean trustFolderStat;
+
 	/**
 	 * Initialize a reference to an on-disk object directory.
 	 *
+	 * @param config
+	 *            configuration for the loose objects handler.
 	 * @param dir
 	 *            the location of the <code>objects</code> directory.
 	 */
-	LooseObjects(File dir) {
+	LooseObjects(Config config, File dir) {
 		directory = dir;
 		unpackedObjectCache = new UnpackedObjectCache();
+		trustFolderStat = config.getBoolean(
+				ConfigConstants.CONFIG_CORE_SECTION,
+				ConfigConstants.CONFIG_KEY_TRUSTFOLDERSTAT, true);
 	}
 
 	/**
@@ -98,6 +108,19 @@
 	 * @return {@code true} if the specified object is stored as a loose object.
 	 */
 	boolean has(AnyObjectId objectId) {
+		boolean exists = hasWithoutRefresh(objectId);
+		if (trustFolderStat || exists) {
+			return exists;
+		}
+		try (InputStream stream = Files.newInputStream(directory.toPath())) {
+			// refresh directory to work around NFS caching issue
+		} catch (IOException e) {
+			return false;
+		}
+		return hasWithoutRefresh(objectId);
+	}
+
+	private boolean hasWithoutRefresh(AnyObjectId objectId) {
 		return fileFor(objectId).exists();
 	}
 
@@ -183,6 +206,22 @@
 	 */
 	ObjectLoader getObjectLoader(WindowCursor curs, File path, AnyObjectId id)
 			throws IOException {
+		try {
+			return getObjectLoaderWithoutRefresh(curs, path, id);
+		} catch (FileNotFoundException e) {
+			if (trustFolderStat) {
+				throw e;
+			}
+			try (InputStream stream = Files
+					.newInputStream(directory.toPath())) {
+				// refresh directory to work around NFS caching issues
+			}
+			return getObjectLoaderWithoutRefresh(curs, path, id);
+		}
+	}
+
+	private ObjectLoader getObjectLoaderWithoutRefresh(WindowCursor curs,
+			File path, AnyObjectId id) throws IOException {
 		try (FileInputStream in = new FileInputStream(path)) {
 			unpackedObjectCache().add(id);
 			return UnpackedObject.open(in, path, id, curs);
@@ -203,16 +242,34 @@
 	}
 
 	long getSize(WindowCursor curs, AnyObjectId id) throws IOException {
+		try {
+			return getSizeWithoutRefresh(curs, id);
+		} catch (FileNotFoundException noFile) {
+			try {
+				if (trustFolderStat) {
+					throw noFile;
+				}
+				try (InputStream stream = Files
+						.newInputStream(directory.toPath())) {
+					// refresh directory to work around NFS caching issue
+				}
+				return getSizeWithoutRefresh(curs, id);
+			} catch (FileNotFoundException e) {
+				if (fileFor(id).exists()) {
+					throw noFile;
+				}
+				unpackedObjectCache().remove(id);
+				return -1;
+			}
+		}
+	}
+
+	private long getSizeWithoutRefresh(WindowCursor curs, AnyObjectId id)
+			throws IOException {
 		File f = fileFor(id);
 		try (FileInputStream in = new FileInputStream(f)) {
 			unpackedObjectCache().add(id);
 			return UnpackedObject.getSize(in, id, curs);
-		} catch (FileNotFoundException noFile) {
-			if (f.exists()) {
-				throw noFile;
-			}
-			unpackedObjectCache().remove(id);
-			return -1;
 		}
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
index 6be3620..e341674 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
@@ -126,7 +126,7 @@
 		File packDirectory = new File(objects, "pack"); //$NON-NLS-1$
 		File preservedDirectory = new File(packDirectory, "preserved"); //$NON-NLS-1$
 		alternatesFile = new File(objects, Constants.INFO_ALTERNATES);
-		loose = new LooseObjects(objects);
+		loose = new LooseObjects(config, objects);
 		packed = new PackDirectory(config, packDirectory);
 		preserved = new PackDirectory(config, preservedDirectory);
 		fileCommitGraph = new FileCommitGraph(objects);
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 f32909f..a7f28c6 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
@@ -63,12 +63,12 @@
 	private static final PackList NO_PACKS = new PackList(FileSnapshot.DIRTY,
 			new Pack[0]);
 
-	private final Config config;
-
 	private final File directory;
 
 	private final AtomicReference<PackList> packList;
 
+	private final boolean trustFolderStat;
+
 	/**
 	 * Initialize a reference to an on-disk 'pack' directory.
 	 *
@@ -78,9 +78,16 @@
 	 *            the location of the {@code pack} directory.
 	 */
 	PackDirectory(Config config, File directory) {
-		this.config = config;
 		this.directory = directory;
 		packList = new AtomicReference<>(NO_PACKS);
+
+		// Whether to trust the pack folder's modification time. If set to false
+		// we will always scan the .git/objects/pack folder to check for new
+		// pack files. If set to true (default) we use the folder's size,
+		// modification time, and key (inode) and assume that no new pack files
+		// can be in this folder if these attributes have not changed.
+		trustFolderStat = config.getBoolean(ConfigConstants.CONFIG_CORE_SECTION,
+				ConfigConstants.CONFIG_KEY_TRUSTFOLDERSTAT, true);
 	}
 
 	/**
@@ -331,16 +338,6 @@
 	}
 
 	boolean searchPacksAgain(PackList old) {
-		// Whether to trust the pack folder's modification time. If set
-		// to false we will always scan the .git/objects/pack folder to
-		// check for new pack files. If set to true (default) we use the
-		// lastmodified attribute of the folder and assume that no new
-		// pack files can be in this folder if his modification time has
-		// not changed.
-		boolean trustFolderStat = config.getBoolean(
-				ConfigConstants.CONFIG_CORE_SECTION,
-				ConfigConstants.CONFIG_KEY_TRUSTFOLDERSTAT, true);
-
 		return ((!trustFolderStat) || old.snapshot.isModified(directory))
 				&& old != scanPacks(old);
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
index 38930c3..7f8f56b 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
@@ -174,6 +174,10 @@
 
 	private List<Integer> retrySleepMs = RETRY_SLEEP_MS;
 
+	private final boolean trustFolderStat;
+
+	private final TrustPackedRefsStat trustPackedRefsStat;
+
 	RefDirectory(FileRepository db) {
 		final FS fs = db.getFS();
 		parent = db;
@@ -185,6 +189,13 @@
 
 		looseRefs.set(RefList.<LooseRef> emptyList());
 		packedRefs.set(NO_PACKED_REFS);
+		trustFolderStat = db.getConfig()
+				.getBoolean(ConfigConstants.CONFIG_CORE_SECTION,
+						ConfigConstants.CONFIG_KEY_TRUSTFOLDERSTAT, true);
+		trustPackedRefsStat = db.getConfig()
+				.getEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
+						ConfigConstants.CONFIG_KEY_TRUST_PACKED_REFS_STAT,
+						TrustPackedRefsStat.UNSET);
 	}
 
 	Repository getRepository() {
@@ -886,16 +897,6 @@
 	}
 
 	PackedRefList getPackedRefs() throws IOException {
-		boolean trustFolderStat = getRepository().getConfig().getBoolean(
-				ConfigConstants.CONFIG_CORE_SECTION,
-				ConfigConstants.CONFIG_KEY_TRUSTFOLDERSTAT, true);
-		TrustPackedRefsStat trustPackedRefsStat =
-				getRepository().getConfig().getEnum(
-						ConfigConstants.CONFIG_CORE_SECTION,
-						null,
-						ConfigConstants.CONFIG_KEY_TRUST_PACKED_REFS_STAT,
-						TrustPackedRefsStat.UNSET);
-
 		final PackedRefList curList = packedRefs.get();
 
 		switch (trustPackedRefsStat) {
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 771d574..5a18f1e 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
@@ -408,6 +408,9 @@
 		List<RevCommit> newWantsByNewest = new ArrayList<>(want.size());
 		Set<RevCommit> newWants = new HashSet<>(want.size());
 		for (AnyObjectId objectId : want) {
+			if(excludeFromBitmapSelection.contains(objectId)) {
+				continue;
+			}
 			RevObject ro = rw.peel(rw.parseAny(objectId));
 			if (!(ro instanceof RevCommit) || reuse.contains(ro)
 					|| excludeFromBitmapSelection.contains(ro)) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchingProgressMonitor.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchingProgressMonitor.java
index 2caefa4..49e295a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchingProgressMonitor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchingProgressMonitor.java
@@ -176,7 +176,7 @@
 				}
 			} else {
 				// Display once per second or when 1% is done.
-				int currPercent = lastWork * 100 / totalWork;
+				int currPercent = Math.round(lastWork * 100F / totalWork);
 				if (display) {
 					pm.onUpdate(taskName, lastWork, totalWork, currPercent);
 					output = true;
@@ -201,8 +201,8 @@
 				if (totalWork == UNKNOWN) {
 					pm.onEndTask(taskName, lastWork);
 				} else {
-					int pDone = lastWork * 100 / totalWork;
-					pm.onEndTask(taskName, lastWork, totalWork, pDone);
+					int currPercent = Math.round(lastWork * 100F / totalWork);
+					pm.onEndTask(taskName, lastWork, totalWork, currPercent);
 				}
 			}
 			if (timerFuture != null)
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 f63f310..ed4bf31 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
@@ -721,6 +721,12 @@
 	public static final String CONFIG_KEY_BITMAP_EXCESSIVE_BRANCH_COUNT = "bitmapexcessivebranchcount";
 
 	/**
+	 * The "pack.bitmapExcludedRefsPrefixes" key
+	 * @since 5.13.2
+	 */
+	public static final String CONFIG_KEY_BITMAP_EXCLUDED_REFS_PREFIXES = "bitmapexcludedrefsprefixes";
+
+	/**
 	 * The "pack.bitmapInactiveBranchAgeInDays" key
 	 * @since 5.8
 	 */
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 e594e52..db2571c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
@@ -1695,6 +1695,22 @@
 			throws IOException;
 
 	/**
+	 * Get the reflog reader. Subclasses should override this method and provide
+	 * a more efficient implementation.
+	 *
+	 * @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.
+	 * @throws IOException
+	 * @since 5.13.2
+	 */
+	public @Nullable ReflogReader getReflogReader(@NonNull	Ref ref)
+			throws IOException {
+		return getReflogReader(ref.getName());
+	}
+
+	/**
 	 * Return the information stored in the file $GIT_DIR/MERGE_MSG. In this
 	 * file operations triggering a merge will store a template for the commit
 	 * message of the merge commit.
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 6aa8be6..a10f6cf 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
@@ -16,6 +16,7 @@
 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_BITMAP_CONTIGUOUS_COMMIT_COUNT;
 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_BITMAP_DISTANT_COMMIT_SPAN;
 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_BITMAP_EXCESSIVE_BRANCH_COUNT;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_BITMAP_EXCLUDED_REFS_PREFIXES;
 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_BITMAP_INACTIVE_BRANCH_AGE_INDAYS;
 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_BITMAP_RECENT_COMMIT_COUNT;
 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_BUILD_BITMAPS;
@@ -226,6 +227,14 @@
 	public static final int DEFAULT_BITMAP_INACTIVE_BRANCH_AGE_IN_DAYS = 90;
 
 	/**
+	 * Default refs prefixes excluded from the calculation of pack bitmaps.
+	 *
+	 * @see #setBitmapExcludedRefsPrefixes(String[])
+	 * @since 5.13.2
+	 */
+	public static final String[] DEFAULT_BITMAP_EXCLUDED_REFS_PREFIXES = new String[0];
+
+	/**
 	 * Default max time to spend during the search for reuse phase. This
 	 * optimization is disabled by default: {@value}
 	 *
@@ -285,6 +294,8 @@
 
 	private int bitmapInactiveBranchAgeInDays = DEFAULT_BITMAP_INACTIVE_BRANCH_AGE_IN_DAYS;
 
+	private String[] bitmapExcludedRefsPrefixes = DEFAULT_BITMAP_EXCLUDED_REFS_PREFIXES;
+
 	private Duration searchForReuseTimeout = DEFAULT_SEARCH_FOR_REUSE_TIMEOUT;
 
 	private boolean cutDeltaChains;
@@ -1145,6 +1156,27 @@
 	}
 
 	/**
+	 * Get the refs prefixes excluded from the Bitmap.
+	 *
+	 * @return the refs prefixes excluded from the Bitmap.
+	 * @since 5.13.2
+	 */
+	public String[] getBitmapExcludedRefsPrefixes() {
+		return bitmapExcludedRefsPrefixes;
+	}
+
+	/**
+	 * Set the refs prefixes excluded from the Bitmap.
+	 *
+	 * @param excludedRefsPrefixes
+	 *            the refs prefixes excluded from the Bitmap.
+	 * @since 5.13.2
+	 */
+	public void setBitmapExcludedRefsPrefixes(String[] excludedRefsPrefixes) {
+		bitmapExcludedRefsPrefixes = excludedRefsPrefixes;
+	}
+
+	/**
 	 * Set the max time to spend during the search for reuse phase.
 	 *
 	 * @param timeout
@@ -1220,6 +1252,12 @@
 		setBitmapInactiveBranchAgeInDays(rc.getInt(CONFIG_PACK_SECTION,
 				CONFIG_KEY_BITMAP_INACTIVE_BRANCH_AGE_INDAYS,
 				getBitmapInactiveBranchAgeInDays()));
+		String[] excludedRefsPrefixesArray = rc.getStringList(CONFIG_PACK_SECTION,
+			null,
+			CONFIG_KEY_BITMAP_EXCLUDED_REFS_PREFIXES);
+		if(excludedRefsPrefixesArray.length > 0) {
+			setBitmapExcludedRefsPrefixes(excludedRefsPrefixesArray);
+		}
 		setSearchForReuseTimeout(Duration.ofSeconds(rc.getTimeUnit(
 				CONFIG_PACK_SECTION, null,
 				CONFIG_KEY_SEARCH_FOR_REUSE_TIMEOUT,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
index 28c3b6a..e0eb126 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
@@ -47,6 +47,7 @@
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.RefDatabase;
 import org.eclipse.jgit.revwalk.ObjectWalk;
+import org.eclipse.jgit.revwalk.RevObject;
 import org.eclipse.jgit.revwalk.RevWalk;
 import org.eclipse.jgit.util.StringUtils;
 
@@ -387,11 +388,19 @@
 	private boolean askForIsComplete() throws TransportException {
 		try {
 			try (ObjectWalk ow = new ObjectWalk(transport.local)) {
-				for (ObjectId want : askFor.keySet())
-					ow.markStart(ow.parseAny(want));
-				for (Ref ref : localRefs().values())
-					ow.markUninteresting(ow.parseAny(ref.getObjectId()));
-				ow.checkConnectivity();
+				boolean hasCommitObject = false;
+				for (ObjectId want : askFor.keySet()) {
+					RevObject obj = ow.parseAny(want);
+					ow.markStart(obj);
+					hasCommitObject |= obj.getType() == Constants.OBJ_COMMIT;
+				}
+				// Checking connectivity makes sense on commits only
+				if (hasCommitObject) {
+					for (Ref ref : localRefs().values()) {
+						ow.markUninteresting(ow.parseAny(ref.getObjectId()));
+					}
+					ow.checkConnectivity();
+				}
 			}
 			return transport.getDepth() == null; // if depth is set we need to request objects that are already available
 		} catch (MissingObjectException e) {
diff --git a/pom.xml b/pom.xml
index 729cd13..f3afee2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -163,20 +163,19 @@
     <osgi-core-version>6.0.0</osgi-core-version>
     <servlet-api-version>4.0.0</servlet-api-version>
     <jetty-version>10.0.13</jetty-version>
-    <japicmp-version>0.15.3</japicmp-version>
+    <japicmp-version>0.17.1</japicmp-version>
     <httpclient-version>4.5.14</httpclient-version>
     <httpcore-version>4.4.16</httpcore-version>
     <slf4j-version>1.7.30</slf4j-version>
-    <maven-javadoc-plugin-version>3.3.1</maven-javadoc-plugin-version>
-    <tycho-extras-version>2.6.0</tycho-extras-version>
+    <maven-javadoc-plugin-version>3.4.1</maven-javadoc-plugin-version>
     <gson-version>2.10</gson-version>
     <bouncycastle-version>1.72</bouncycastle-version>
-    <spotbugs-maven-plugin-version>4.3.0</spotbugs-maven-plugin-version>
-    <maven-project-info-reports-plugin-version>3.1.2</maven-project-info-reports-plugin-version>
-    <maven-jxr-plugin-version>3.1.1</maven-jxr-plugin-version>
-    <maven-surefire-plugin-version>3.0.0-M5</maven-surefire-plugin-version>
+    <spotbugs-maven-plugin-version>4.7.3.0</spotbugs-maven-plugin-version>
+    <maven-project-info-reports-plugin-version>3.4.2</maven-project-info-reports-plugin-version>
+    <maven-jxr-plugin-version>3.3.0</maven-jxr-plugin-version>
+    <maven-surefire-plugin-version>3.0.0-M8</maven-surefire-plugin-version>
     <maven-surefire-report-plugin-version>${maven-surefire-plugin-version}</maven-surefire-report-plugin-version>
-    <maven-compiler-plugin-version>3.8.1</maven-compiler-plugin-version>
+    <maven-compiler-plugin-version>3.10.1</maven-compiler-plugin-version>
     <plexus-compiler-version>2.12.1</plexus-compiler-version>
     <hamcrest-version>2.2</hamcrest-version>
     <assertj-version>3.20.2</assertj-version>
@@ -223,7 +222,7 @@
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-jar-plugin</artifactId>
-          <version>3.2.0</version>
+          <version>3.3.0</version>
           <configuration>
             <archive>
               <manifestEntries>
@@ -242,25 +241,25 @@
 
         <plugin>
           <artifactId>maven-clean-plugin</artifactId>
-          <version>3.1.0</version>
+          <version>3.2.0</version>
         </plugin>
 
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-shade-plugin</artifactId>
-          <version>3.2.4</version>
+          <version>3.4.1</version>
         </plugin>
 
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-antrun-plugin</artifactId>
-          <version>3.0.0</version>
+          <version>3.1.0</version>
         </plugin>
 
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-dependency-plugin</artifactId>
-          <version>3.2.0</version>
+          <version>3.5.0</version>
         </plugin>
 
         <plugin>
@@ -289,7 +288,7 @@
         <plugin>
           <groupId>org.codehaus.mojo</groupId>
           <artifactId>build-helper-maven-plugin</artifactId>
-          <version>3.2.0</version>
+          <version>3.3.0</version>
         </plugin>
 
         <plugin>
@@ -312,7 +311,7 @@
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-pmd-plugin</artifactId>
-          <version>3.15.0</version>
+          <version>3.20.0</version>
           <configuration>
             <sourceEncoding>utf-8</sourceEncoding>
             <minimumTokens>100</minimumTokens>
@@ -335,17 +334,17 @@
         <plugin>
           <groupId>org.eclipse.cbi.maven.plugins</groupId>
           <artifactId>eclipse-jarsigner-plugin</artifactId>
-          <version>1.3.2</version>
+          <version>1.3.5</version>
         </plugin>
         <plugin>
           <groupId>org.jacoco</groupId>
           <artifactId>jacoco-maven-plugin</artifactId>
-          <version>0.8.7</version>
+          <version>0.8.8</version>
         </plugin>
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-site-plugin</artifactId>
-          <version>3.9.1</version>
+          <version>4.0.0-M4</version>
           <dependencies>
             <dependency><!-- add support for ssh/scp -->
               <groupId>org.apache.maven.wagon</groupId>
@@ -372,12 +371,12 @@
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-deploy-plugin</artifactId>
-          <version>3.0.0-M1</version>
+          <version>3.0.0</version>
         </plugin>
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-install-plugin</artifactId>
-          <version>3.0.0-M1</version>
+          <version>3.1.0</version>
         </plugin>
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
@@ -387,12 +386,12 @@
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-resources-plugin</artifactId>
-          <version>3.2.0</version>
+          <version>3.3.0</version>
         </plugin>
         <plugin>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-maven-plugin</artifactId>
-          <version>2.5.4</version>
+          <version>2.7.7</version>
         </plugin>
         <plugin>
           <groupId>org.eclipse.dash</groupId>
@@ -406,7 +405,7 @@
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-enforcer-plugin</artifactId>
-        <version>3.0.0</version>
+        <version>3.1.0</version>
         <executions>
           <execution>
             <id>enforce-maven</id>