Merge "Add cooperative cancellation to RevWalk traversal"
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 4dc40df..79f2013 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
@@ -1533,6 +1533,25 @@ public void testCommonRefPrefix() {
 				StringUtils.commonPrefix("refs/heads/", "refs/heads/main"));
 	}
 
+	@Test
+	public void testPackDoesNotUnlockHeldLocks() throws IOException {
+		String ref = "refs/heads/master";
+		writeLooseRef(ref, A);
+
+		File refFile = refdir.fileFor(ref);
+		LockFile held = new LockFile(refFile);
+		assertTrue("must acquire lock", held.lock());
+		try {
+			refdir.pack(Map.of(ref, held));
+			assertFalse("loose ref must be deleted after packing",
+					refFile.exists());
+			assertTrue("held lock must still be locked after pack()",
+					held.isLocked());
+		} finally {
+			held.unlock();
+		}
+	}
+
 	void writePackedRef(String name, AnyObjectId id) throws IOException {
 		writePackedRefs(id.name() + " " + name + "\n");
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackedBatchRefUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackedBatchRefUpdate.java
index f33fe0b..cd03600 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackedBatchRefUpdate.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackedBatchRefUpdate.java
@@ -146,20 +146,15 @@ public void execute(RevWalk walk, ProgressMonitor monitor,
 			return;
 		}
 
-		// Pack refs normally, so we can create lock files even in the case where
-		// refs/x is deleted and refs/x/y is created in this batch.
-		try {
-			refdb.pack(
-					pending.stream().map(ReceiveCommand::getRefName).collect(toList()));
-		} catch (LockFailedException e) {
-			lockFailure(pending.get(0), pending);
-			return;
-		}
-
 		Map<String, LockFile> locks = null;
 		LockFile packedRefsLock = null;
 		refdb.inProcessPackedRefsLock.lock();
 		try {
+			// Pack refs normally, so we can create lock files even in the case
+			// where refs/x is deleted and refs/x/y is created in this batch.
+			refdb.pack(pending.stream().map(ReceiveCommand::getRefName)
+					.collect(toList()));
+
 			// During clone locking isn't needed since no refs exist yet.
 			// This also helps to avoid problems with refs only differing in
 			// case on a case insensitive filesystem (bug 528497)
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 a9211fe..d5ac5aa 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
@@ -876,8 +876,13 @@ private void pack(Collection<String> refs,
 								}
 								newLoose = curLoose.remove(idx);
 							} while (!looseRefs.compareAndSet(curLoose, newLoose));
-							int levels = levelsIn(refName) - 2;
-							deleteAndUnlock(refFile, levels, rLck);
+							if (shouldUnlock) {
+								int levels = levelsIn(refName) - 2;
+								deleteAndUnlock(refFile, levels, rLck);
+								shouldUnlock = false;
+							} else {
+								delete(refFile);
+							}
 							LOG.debug(JGitText.get().deleteLooseRef, refFile, clr_oid);
 						}
 					} finally {