Merge branch 'stable-7.0' into stable-7.1

* stable-7.0:
  RevWalk: Add an isMergedIntoAnyCommit() method
  Pack: separate an open/close accounting lock
  http.server/BUILD: expose servlet resources as target

Change-Id: I25d3ffe86150d4d9e8a54634145cdbee34ea15fb
diff --git a/org.eclipse.jgit.http.server/BUILD b/org.eclipse.jgit.http.server/BUILD
index f8aa44d..a0dae48 100644
--- a/org.eclipse.jgit.http.server/BUILD
+++ b/org.eclipse.jgit.http.server/BUILD
@@ -2,11 +2,16 @@
 
 package(default_visibility = ["//visibility:public"])
 
+filegroup(
+    name = "jgit-servlet-resources",
+    srcs = glob(["resources/**"]),
+)
+
 java_library(
     name = "jgit-servlet",
     srcs = glob(["src/**/*.java"]),
     resource_strip_prefix = "org.eclipse.jgit.http.server/resources",
-    resources = glob(["resources/**"]),
+    resources = [":jgit-servlet-resources"],
     deps = [
         "//lib:servlet-api",
         # We want these deps to be provided_deps
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkUtilsReachableTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkUtilsReachableTest.java
index 0a045c9..ffc7c96 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkUtilsReachableTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkUtilsReachableTest.java
@@ -14,6 +14,7 @@
 import static org.junit.Assert.assertEquals;
 
 import java.util.Collection;
+import java.util.HashSet;
 import java.util.List;
 
 import org.eclipse.jgit.api.Git;
@@ -121,7 +122,7 @@ private void assertContains(RevCommit commit, Collection<Ref> refsThatShouldCont
 		Collection<Ref> sortedRefs = RefComparator.sort(allRefs);
 		List<Ref> actual = RevWalkUtils.findBranchesReachableFrom(commit,
 				rw, sortedRefs);
-		assertEquals(refsThatShouldContainCommit, actual);
+		assertEquals(new HashSet<>(refsThatShouldContainCommit), new HashSet<>(actual));
 	}
 
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/Pack.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/Pack.java
index 8d2a863..1a7b5de 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/Pack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/Pack.java
@@ -95,6 +95,9 @@ public class Pack implements Iterable<PackIndex.MutableEntry> {
 
 	private RandomAccessFile fd;
 
+	/** For managing open/close accounting of {@link #fd}. */
+	private final Object activeLock = new Object();
+
 	/** Serializes reads performed against {@link #fd}. */
 	private final Object readLock = new Object();
 
@@ -645,37 +648,48 @@ private void readFully(final long position, final byte[] dstbuf,
 			throw new EOFException();
 	}
 
-	private synchronized void beginCopyAsIs()
+	private void beginCopyAsIs()
 			throws StoredObjectRepresentationNotAvailableException {
-		if (++activeCopyRawData == 1 && activeWindows == 0) {
-			try {
-				doOpen();
-			} catch (IOException thisPackNotValid) {
-				throw new StoredObjectRepresentationNotAvailableException(
-						thisPackNotValid);
+		synchronized (activeLock) {
+			if (++activeCopyRawData == 1 && activeWindows == 0) {
+				try {
+					doOpen();
+				} catch (IOException thisPackNotValid) {
+					throw new StoredObjectRepresentationNotAvailableException(
+							thisPackNotValid);
+				}
 			}
 		}
 	}
 
-	private synchronized void endCopyAsIs() {
-		if (--activeCopyRawData == 0 && activeWindows == 0)
-			doClose();
-	}
-
-	synchronized boolean beginWindowCache() throws IOException {
-		if (++activeWindows == 1) {
-			if (activeCopyRawData == 0)
-				doOpen();
-			return true;
+	private void endCopyAsIs() {
+		synchronized (activeLock) {
+			if (--activeCopyRawData == 0 && activeWindows == 0) {
+				doClose();
+			}
 		}
-		return false;
 	}
 
-	synchronized boolean endWindowCache() {
-		final boolean r = --activeWindows == 0;
-		if (r && activeCopyRawData == 0)
-			doClose();
-		return r;
+	boolean beginWindowCache() throws IOException {
+		synchronized (activeLock) {
+			if (++activeWindows == 1) {
+				if (activeCopyRawData == 0) {
+					doOpen();
+				}
+				return true;
+			}
+			return false;
+		}
+	}
+
+	boolean endWindowCache() {
+		synchronized (activeLock) {
+			boolean r = --activeWindows == 0;
+			if (r && activeCopyRawData == 0) {
+				doClose();
+			}
+			return r;
+		}
 	}
 
 	private void doOpen() throws IOException {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
index 9f0e28d..41f98ba 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
@@ -19,9 +19,14 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Optional;
+import java.util.Map;
+import java.util.
+Optional;
+import java.util.Set;
 
 import org.eclipse.jgit.annotations.NonNull;
 import org.eclipse.jgit.annotations.Nullable;
@@ -523,6 +528,27 @@ public boolean isMergedIntoAny(RevCommit commit, Collection<Ref> refs)
 	}
 
 	/**
+	 * Determine if a <code>commit</code> is merged into any of the given
+	 * <code>revs</code>.
+	 *
+	 * @param commit
+	 *            commit the caller thinks is reachable from <code>revs</code>.
+	 * @param revs
+	 *            commits to start iteration from, and which is most likely a
+	 *            descendant (child) of <code>commit</code>.
+	 * @return true if commit is merged into any of the revs; false otherwise.
+	 * @throws java.io.IOException
+	 *             a pack file or loose object could not be read.
+	 * @since 6.10.1
+	 */
+	public boolean isMergedIntoAnyCommit(RevCommit commit, Collection<RevCommit> revs)
+			throws IOException {
+		return getCommitsMergedInto(commit, revs,
+				GetMergedIntoStrategy.RETURN_ON_FIRST_FOUND,
+				NullProgressMonitor.INSTANCE).size() > 0;
+	}
+
+	/**
 	 * Determine if a <code>commit</code> is merged into all of the given
 	 * <code>refs</code>.
 	 *
@@ -545,7 +571,26 @@ public boolean isMergedIntoAll(RevCommit commit, Collection<Ref> refs)
 
 	private List<Ref> getMergedInto(RevCommit needle, Collection<Ref> haystacks,
 			Enum returnStrategy, ProgressMonitor monitor) throws IOException {
+		Map<RevCommit, List<Ref>> refsByCommit = new HashMap<>();
+		for (Ref r : haystacks) {
+			RevObject o = peel(parseAny(r.getObjectId()));
+			if (!(o instanceof RevCommit)) {
+				continue;
+			}
+			refsByCommit.computeIfAbsent((RevCommit) o, c -> new ArrayList<>()).add(r);
+		}
+		monitor.update(1);
 		List<Ref> result = new ArrayList<>();
+		for (RevCommit c : getCommitsMergedInto(needle, refsByCommit.keySet(),
+				returnStrategy, monitor)) {
+			result.addAll(refsByCommit.get(c));
+		}
+		return result;
+	}
+
+	private Set<RevCommit> getCommitsMergedInto(RevCommit needle, Collection<RevCommit> haystacks,
+			Enum returnStrategy, ProgressMonitor monitor) throws IOException {
+		Set<RevCommit> result = new HashSet<>();
 		List<RevCommit> uninteresting = new ArrayList<>();
 		List<RevCommit> marked = new ArrayList<>();
 		RevFilter oldRF = filter;
@@ -561,16 +606,11 @@ private List<Ref> getMergedInto(RevCommit needle, Collection<Ref> haystacks,
 				needle.parseHeaders(this);
 			}
 			int cutoff = needle.getGeneration();
-			for (Ref r : haystacks) {
+			for (RevCommit c : haystacks) {
 				if (monitor.isCancelled()) {
 					return result;
 				}
 				monitor.update(1);
-				RevObject o = peel(parseAny(r.getObjectId()));
-				if (!(o instanceof RevCommit)) {
-					continue;
-				}
-				RevCommit c = (RevCommit) o;
 				reset(UNINTERESTING | TEMP_MARK);
 				markStart(c);
 				boolean commitFound = false;
@@ -582,7 +622,7 @@ private List<Ref> getMergedInto(RevCommit needle, Collection<Ref> haystacks,
 					}
 					if (References.isSameObject(next, needle)
 							|| (next.flags & TEMP_MARK) != 0) {
-						result.add(r);
+						result.add(c);
 						if (returnStrategy == GetMergedIntoStrategy.RETURN_ON_FIRST_FOUND) {
 							return result;
 						}