PackedBatchRefUpdate: Release locks in reverse order of acquisition

PackedBatchRefUpdate#execute takes three locks on the way in. First the
in-process packed-refs lock, then the loose ref locks, and last is the
packed-refs lock file. On the way out it unlocked them in a different
order, the loose refs, then the packed-refs lock file, then the
in-process lock.

This change gives each lock its own finally block, scoped to the region
where it is actually held. Every lock is now unlocked in the reverse of
the order it was locked. And no path can slip past an unlock, since a
failure to release one lock still leaves the enclosing finally blocks to
release the others. Before this, a failure while unlocking the loose
refs would have left the packed-refs lock file behind on disk.

Change-Id: Iac7425f9e4fab666b0c04fc6f153789d24950009
Signed-off-by: Kaushik Lingarkar <klingarkar@nvidia.com>
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 cd03600..12a7031 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,49 +146,52 @@ public void execute(RevWalk walk, ProgressMonitor monitor,
 			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)
-			if (!refdb.isInClone() && shouldLockLooseRefs) {
-				locks = lockLooseRefs(pending);
-				if (locks == null) {
-					return;
-				}
-				refdb.pack(locks);
-			}
-
-			packedRefsLock = refdb.lockPackedRefsOrThrow();
-			PackedRefList oldPackedList = refdb.getLockedPackedRefs(packedRefsLock);
-			RefList<Ref> newRefs = applyUpdates(walk, oldPackedList, pending);
-			if (newRefs == null) {
-				return;
-			}
-			refdb.commitPackedRefs(packedRefsLock, newRefs, oldPackedList,
-					true, oldPackedList.traits());
-		} catch (LockFailedException e) {
-			lockFailure(pending.get(0), pending);
-			return;
-		} finally {
+			Map<String, LockFile> locks = null;
 			try {
-				unlockAll(locks);
-				if (packedRefsLock != null) {
-					// This will be no-op if commitPackedRefs is successful as
-					// it will remove the lock file (by renaming over real
-					// file).
+				// 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)
+				if (!refdb.isInClone() && shouldLockLooseRefs) {
+					locks = lockLooseRefs(pending);
+					if (locks == null) {
+						return;
+					}
+					refdb.pack(locks);
+				}
+
+				LockFile packedRefsLock = refdb.lockPackedRefsOrThrow();
+				try {
+					PackedRefList oldPackedList = refdb
+							.getLockedPackedRefs(packedRefsLock);
+					RefList<Ref> newRefs = applyUpdates(walk, oldPackedList,
+							pending);
+					if (newRefs == null) {
+						return;
+					}
+					refdb.commitPackedRefs(packedRefsLock, newRefs,
+							oldPackedList, true, oldPackedList.traits());
+				} finally {
+					// This will be no-op if commitPackedRefs is successful
+					// as it will remove the lock file (by renaming over
+					// real file).
 					packedRefsLock.unlock();
 				}
+			} catch (LockFailedException e) {
+				lockFailure(pending.get(0), pending);
+				return;
 			} finally {
-				refdb.inProcessPackedRefsLock.unlock();
+				unlockAll(locks);
 			}
+		} finally {
+			refdb.inProcessPackedRefsLock.unlock();
 		}
 
 		refdb.fireRefsChanged();