Add retainOnReset(RevFlag) to RevWalk to simplify reset usage

Applications sometimes use a RevFlag instead of a Set<RevObject>
to track boolean state bits about objects being processed. However
this requires careful use of the resetRetain() methods to avoid an
accidental clearing of the RevFlag bits, effectively clearing the
Set<RevObject> the application wanted to track.

Simplify that use case by offering retainOnReset, a collection of
flags that are never cleared by the RevWalk.

Change-Id: I4c05b89b1398e4a4f371eac3a5d1d5edddec838f
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 79cc42d..d19e467 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
@@ -174,6 +174,7 @@ public class RevWalk implements Iterable<RevCommit> {
 
 	private int delayFreeFlags;
 
+	private int retainOnReset;
 	int carryFlags = UNINTERESTING;
 
 	final ArrayList<RevCommit> roots;
@@ -1093,6 +1094,47 @@ public void carry(final Collection<RevFlag> set) {
 	}
 
 	/**
+	 * Preserve a RevFlag during all {@code reset} methods.
+	 * <p>
+	 * Calling {@code retainOnReset(flag)} avoids needing to pass the flag
+	 * during each {@code resetRetain()} invocation on this instance.
+	 * <p>
+	 * Clearing flags marked retainOnReset requires disposing of the flag with
+	 * {@code #disposeFlag(RevFlag)} or disposing of the entire RevWalk by
+	 * {@code #dispose()}.
+	 *
+	 * @param flag
+	 *            the flag to retain during all resets.
+	 * @since 3.6
+	 */
+	public final void retainOnReset(RevFlag flag) {
+		if ((freeFlags & flag.mask) != 0)
+			throw new IllegalArgumentException(MessageFormat.format(JGitText.get().flagIsDisposed, flag.name));
+		if (flag.walker != this)
+			throw new IllegalArgumentException(MessageFormat.format(JGitText.get().flagNotFromThis, flag.name));
+		retainOnReset |= flag.mask;
+	}
+
+	/**
+	 * Preserve a set of RevFlags during all {@code reset} methods.
+	 * <p>
+	 * Calling {@code retainOnReset(set)} avoids needing to pass the flags
+	 * during each {@code resetRetain()} invocation on this instance.
+	 * <p>
+	 * Clearing flags marked retainOnReset requires disposing of the flag with
+	 * {@code #disposeFlag(RevFlag)} or disposing of the entire RevWalk by
+	 * {@code #dispose()}.
+	 *
+	 * @param flags
+	 *            the flags to retain during all resets.
+	 * @since 3.6
+	 */
+	public final void retainOnReset(Collection<RevFlag> flags) {
+		for (RevFlag f : flags)
+			retainOnReset(f);
+	}
+
+	/**
 	 * Allow a flag to be recycled for a different use.
 	 * <p>
 	 * Recycled flags always come back as a different Java object instance when
@@ -1110,6 +1152,7 @@ public void disposeFlag(final RevFlag flag) {
 	}
 
 	void freeFlag(final int mask) {
+		retainOnReset &= ~mask;
 		if (isNotStarted()) {
 			freeFlags |= mask;
 			carryFlags &= ~mask;
@@ -1158,6 +1201,9 @@ public final void resetRetain(final RevFlagSet retainFlags) {
 	 * Unlike {@link #dispose()} previously acquired RevObject (and RevCommit)
 	 * instances are not invalidated. RevFlag instances are not invalidated, but
 	 * are removed from all RevObjects.
+	 * <p>
+	 * See {@link #retainOnReset(RevFlag)} for an alternative that does not
+	 * require passing the flags during each reset.
 	 *
 	 * @param retainFlags
 	 *            application flags that should <b>not</b> be cleared from
@@ -1183,7 +1229,7 @@ public final void resetRetain(final RevFlag... retainFlags) {
 	 */
 	protected void reset(int retainFlags) {
 		finishDelayedFreeFlags();
-		retainFlags |= PARSED;
+		retainFlags |= PARSED | retainOnReset;
 		final int clearFlags = ~retainFlags;
 
 		final FIFORevQueue q = new FIFORevQueue();
@@ -1227,6 +1273,7 @@ public void dispose() {
 		reader.release();
 		freeFlags = APP_FLAGS;
 		delayFreeFlags = 0;
+		retainOnReset = 0;
 		carryFlags = UNINTERESTING;
 		objects.clear();
 		reader.release();