Merge branch 'stable-6.3' into stable-6.4

* stable-6.3:
  Remove blank in maven.config
  DirCache: support option index.skipHash

Change-Id: I18cf0da3a5dcc74865c44d82e7c328329814acae
diff --git a/.mvn/maven.config b/.mvn/maven.config
index ebbe288..3944d88 100644
--- a/.mvn/maven.config
+++ b/.mvn/maven.config
@@ -1 +1 @@
--T 1C
+-T1C
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBasicTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBasicTest.java
index 0fca652..618ccc0 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBasicTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBasicTest.java
@@ -17,19 +17,48 @@
 import static org.junit.Assert.fail;
 
 import java.io.File;
+import java.io.IOException;
 import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.Collection;
 
 import org.eclipse.jgit.errors.CorruptObjectException;
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.junit.MockSystemReader;
 import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.ConfigConstants;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.FileMode;
 import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
 import org.eclipse.jgit.util.SystemReader;
+import org.junit.Before;
 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;
 
+@RunWith(Parameterized.class)
 public class DirCacheBasicTest extends RepositoryTestCase {
+	@Parameter(0)
+	public boolean skipHash;
+
+	@Parameters(name = "skipHash: {0}")
+	public static Collection<Boolean[]> getSkipHashValues() {
+		return Arrays
+				.asList(new Boolean[][] { { Boolean.TRUE },
+						{ Boolean.FALSE } });
+	}
+
+	@Before
+	public void setup() throws IOException {
+		FileBasedConfig cfg = db.getConfig();
+		cfg.setBoolean(ConfigConstants.CONFIG_INDEX_SECTION, null,
+				ConfigConstants.CONFIG_KEY_SKIPHASH, skipHash);
+		cfg.save();
+	}
+
 	@Test
 	public void testReadMissing_RealIndex() throws Exception {
 		final File idx = new File(db.getDirectory(), "index");
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 03da615..e560612 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java
@@ -40,6 +40,7 @@
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.internal.storage.file.FileSnapshot;
 import org.eclipse.jgit.internal.storage.file.LockFile;
+import org.eclipse.jgit.internal.storage.io.NullMessageDigest;
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.Config.ConfigEnum;
@@ -327,6 +328,9 @@ public static DirCache lock(final File indexLocation, final FS fs,
 	/** If we read this index from disk, the original format. */
 	private DirCacheVersion version;
 
+	/** Whether to skip computing and checking the index checksum */
+	private boolean skipHash;
+
 	/**
 	 * Create a new in-core index representation.
 	 * <p>
@@ -446,7 +450,8 @@ public void clear() {
 	private void readFrom(InputStream inStream) throws IOException,
 			CorruptObjectException {
 		final BufferedInputStream in = new BufferedInputStream(inStream);
-		final MessageDigest md = Constants.newMessageDigest();
+		readConfig();
+		MessageDigest md = newMessageDigest();
 
 		// Read the index header and verify we understand it.
 		//
@@ -543,8 +548,12 @@ private void readFrom(InputStream inStream) throws IOException,
 		}
 
 		readIndexChecksum = md.digest();
-		if (!Arrays.equals(readIndexChecksum, hdr)) {
-			throw new CorruptObjectException(JGitText.get().DIRCChecksumMismatch);
+		if (!(skipHash
+				|| Arrays.equals(readIndexChecksum, hdr)
+				|| Arrays.equals(NullMessageDigest.getInstance().digest(),
+						hdr))) {
+			throw new CorruptObjectException(
+					JGitText.get().DIRCChecksumMismatch);
 		}
 	}
 
@@ -627,15 +636,9 @@ public void write() throws IOException {
 	}
 
 	void writeTo(File dir, OutputStream os) throws IOException {
-		final MessageDigest foot = Constants.newMessageDigest();
-		final DigestOutputStream dos = new DigestOutputStream(os, foot);
-
-		if (version == null && this.repository != null) {
-			// A new DirCache is being written.
-			DirCacheConfig config = repository.getConfig()
-					.get(DirCacheConfig::new);
-			version = config.getIndexVersion();
-		}
+		readConfig();
+		MessageDigest foot = newMessageDigest();
+		DigestOutputStream dos = new DigestOutputStream(os, foot);
 		if (version == null
 				|| version == DirCacheVersion.DIRC_VERSION_MINIMUM) {
 			version = DirCacheVersion.DIRC_VERSION_MINIMUM;
@@ -707,6 +710,22 @@ void writeTo(File dir, OutputStream os) throws IOException {
 		os.close();
 	}
 
+	private void readConfig() {
+		if (version == null && this.repository != null) {
+			DirCacheConfig config = repository.getConfig()
+					.get(DirCacheConfig::new);
+			version = config.getIndexVersion();
+			skipHash = config.isSkipHash();
+		}
+	}
+
+	private MessageDigest newMessageDigest() {
+		if (skipHash) {
+			return NullMessageDigest.getInstance();
+		}
+		return Constants.newMessageDigest();
+	}
+
 	/**
 	 * Commit this change and release the lock.
 	 * <p>
@@ -1071,6 +1090,8 @@ private static class DirCacheConfig {
 
 		private final DirCacheVersion indexVersion;
 
+		private final boolean skipHash;
+
 		public DirCacheConfig(Config cfg) {
 			boolean manyFiles = cfg.getBoolean(
 					ConfigConstants.CONFIG_FEATURE_SECTION,
@@ -1080,11 +1101,16 @@ public DirCacheConfig(Config cfg) {
 					ConfigConstants.CONFIG_KEY_VERSION,
 					manyFiles ? DirCacheVersion.DIRC_VERSION_PATHCOMPRESS
 							: DirCacheVersion.DIRC_VERSION_EXTENDED);
+			skipHash = cfg.getBoolean(ConfigConstants.CONFIG_INDEX_SECTION,
+					ConfigConstants.CONFIG_KEY_SKIPHASH, false);
 		}
 
 		public DirCacheVersion getIndexVersion() {
 			return indexVersion;
 		}
 
+		public boolean isSkipHash() {
+			return skipHash;
+		}
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/io/NullMessageDigest.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/io/NullMessageDigest.java
new file mode 100644
index 0000000..ea5877f
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/io/NullMessageDigest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023, SAP SE and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.eclipse.jgit.internal.storage.io;
+
+import java.security.MessageDigest;
+
+import org.eclipse.jgit.lib.Constants;
+
+/**
+ * Dummy message digest consisting of only null bytes with the length of an
+ * ObjectId. This class can be used to skip computing a real digest.
+ */
+public final class NullMessageDigest extends MessageDigest {
+	private static final byte[] digest = new byte[Constants.OBJECT_ID_LENGTH];
+
+	private static final NullMessageDigest INSTANCE = new NullMessageDigest();
+
+	/**
+	 * Get the only instance of NullMessageDigest
+	 *
+	 * @return the only instance of NullMessageDigest
+	 */
+	public static MessageDigest getInstance() {
+		return INSTANCE;
+	}
+
+	private NullMessageDigest() {
+		super("null"); //$NON-NLS-1$
+	}
+
+	@Override
+	protected void engineUpdate(byte input) {
+		// empty
+	}
+
+	@Override
+	protected void engineUpdate(byte[] input, int offset, int len) {
+		// empty
+	}
+
+	@Override
+	protected byte[] engineDigest() {
+		return digest;
+	}
+
+	@Override
+	protected void engineReset() {
+		// empty
+	}
+}
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 96f3198..5c48c7a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
@@ -449,6 +449,12 @@ public final class ConfigConstants {
 	public static final String CONFIG_KEY_INDEXVERSION = "indexversion";
 
 	/**
+	 * The "skiphash" key
+	 * @since 5.13.2
+	 */
+	public static final String CONFIG_KEY_SKIPHASH = "skiphash";
+
+	/**
 	 * The "hidedotfiles" key
 	 * @since 3.5
 	 */