changeset 648:690e71d29bf6

Introduced RevisionSet to ease update of phase roots on push
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Tue, 25 Jun 2013 20:48:37 +0200
parents c75297c17867
children e79cf9a8130b
files build.xml src/org/tmatesoft/hg/internal/PhasesHelper.java src/org/tmatesoft/hg/internal/RevisionDescendants.java src/org/tmatesoft/hg/internal/RevisionSet.java test/org/tmatesoft/hg/test/TestPhases.java test/org/tmatesoft/hg/test/TestRevisionSet.java
diffstat 6 files changed, 299 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/build.xml	Tue Jun 25 18:53:18 2013 +0200
+++ b/build.xml	Tue Jun 25 20:48:37 2013 +0200
@@ -110,6 +110,7 @@
 			<test name="org.tmatesoft.hg.test.TestBlame" />
 			<test name="org.tmatesoft.hg.test.TestDiffHelper" />
 			<test name="org.tmatesoft.hg.test.TestRepositoryLock" />
+			<test name="org.tmatesoft.hg.test.TestRevisionSet" />
 			<test name="org.tmatesoft.hg.test.ComplexTest" />
 		</junit>
 	</target>
--- a/src/org/tmatesoft/hg/internal/PhasesHelper.java	Tue Jun 25 18:53:18 2013 +0200
+++ b/src/org/tmatesoft/hg/internal/PhasesHelper.java	Tue Jun 25 20:48:37 2013 +0200
@@ -131,7 +131,39 @@
 			}
 		}
 		return HgPhase.Public;
+	}
 
+
+	/**
+	 * @return all revisions with secret phase
+	 */
+	public RevisionSet allSecret() {
+		return allOf(HgPhase.Secret);
+	}
+	
+	public RevisionSet allDraft() {
+		return allOf(HgPhase.Draft).subtract(allOf(HgPhase.Secret));
+	}
+
+	/**
+	 * For a given phase, collect all revisions with phase that is the same or more private (i.e. for Draft, returns Draft+Secret)
+	 * The reason is not a nice API intention (which is awful, indeed), but an ease of implementation 
+	 */
+	private RevisionSet allOf(HgPhase phase) {
+		assert phase != HgPhase.Public;
+		if (!isCapableOfPhases()) {
+			return new RevisionSet(Collections.<Nodeid>emptyList());
+		}
+		final List<Nodeid> roots = getPhaseRoots(phase);
+		if (parentHelper != null) {
+			return new RevisionSet(roots).union(new RevisionSet(parentHelper.childrenOf(roots)));
+		} else {
+			RevisionSet rv = new RevisionSet(Collections.<Nodeid>emptyList());
+			for (RevisionDescendants rd : getPhaseDescendants(phase)) {
+				rv = rv.union(rd.asRevisionSet());
+			}
+			return rv;
+		}
 	}
 
 	private Boolean readRoots() throws HgRuntimeException {
--- a/src/org/tmatesoft/hg/internal/RevisionDescendants.java	Tue Jun 25 18:53:18 2013 +0200
+++ b/src/org/tmatesoft/hg/internal/RevisionDescendants.java	Tue Jun 25 20:48:37 2013 +0200
@@ -16,6 +16,7 @@
  */
 package org.tmatesoft.hg.internal;
 
+import java.util.ArrayList;
 import java.util.BitSet;
 
 import org.tmatesoft.hg.core.Nodeid;
@@ -37,6 +38,7 @@
 	private final int rootRevIndex;
 	private final int tipRevIndex; // this is the last revision we cache to
 	private final BitSet descendants;
+	private RevisionSet revset;
 
 	// in fact, may be refactored to deal not only with changelog, but any revlog (not sure what would be the usecase, though)
 	public RevisionDescendants(HgRepository hgRepo, int revisionIndex) throws HgRuntimeException {
@@ -108,4 +110,21 @@
 		assert ix < descendants.size();
 		return descendants.get(ix);
 	}
+
+	public RevisionSet asRevisionSet() {
+		if (revset == null) {
+			final ArrayList<Nodeid> revisions = new ArrayList<Nodeid>(descendants.cardinality());
+			repo.getChangelog().indexWalk(rootRevIndex, tipRevIndex, new HgChangelog.RevisionInspector() {
+
+				public void next(int revisionIndex, Nodeid revision, int linkedRevisionIndex) throws HgRuntimeException {
+					if (isDescendant(revisionIndex)) {
+						revisions.add(revision);
+					}
+				}
+			});
+			assert revisions.size() == descendants.cardinality();
+			revset = new RevisionSet(revisions);
+		}
+		return revset;
+	}
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/tmatesoft/hg/internal/RevisionSet.java	Tue Jun 25 20:48:37 2013 +0200
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2013 TMate Software Ltd
+ *  
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * For information on how to redistribute this software under
+ * the terms of a license other than GNU General Public License
+ * contact TMate Software at support@hg4j.com
+ */
+package org.tmatesoft.hg.internal;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.tmatesoft.hg.core.Nodeid;
+
+/**
+ * Unmodifiable collection of revisions with handy set operations
+ * 
+ * @author Artem Tikhomirov
+ * @author TMate Software Ltd.
+ */
+public final class RevisionSet {
+	
+	private final Set<Nodeid> elements;
+	
+	public RevisionSet(Collection<Nodeid> revisions) {
+		this(revisions == null ? new HashSet<Nodeid>() : new HashSet<Nodeid>(revisions));
+	}
+	
+	private RevisionSet(HashSet<Nodeid> revisions) {
+		if (revisions.isEmpty()) {
+			elements = Collections.<Nodeid>emptySet();
+		} else {
+			elements = revisions;
+		}
+	}
+
+	public RevisionSet roots() {
+		throw Internals.notImplemented();
+	}
+	
+	public RevisionSet heads() {
+		throw Internals.notImplemented();
+	}
+
+	public RevisionSet intersect(RevisionSet other) {
+		if (isEmpty()) {
+			return this;
+		}
+		if (other.isEmpty()) {
+			return other;
+		}
+		HashSet<Nodeid> copy = new HashSet<Nodeid>(elements);
+		copy.retainAll(other.elements);
+		return copy.size() == elements.size() ? this : new RevisionSet(copy);
+	}
+	
+	public RevisionSet subtract(RevisionSet other) {
+		if (isEmpty() || other.isEmpty()) {
+			return this;
+		}
+		HashSet<Nodeid> copy = new HashSet<Nodeid>(elements);
+		copy.removeAll(other.elements);
+		return copy.size() == elements.size() ? this : new RevisionSet(copy);
+	}
+
+	public RevisionSet union(RevisionSet other) {
+		if (isEmpty()) {
+			return other;
+		}
+		if (other.isEmpty()) {
+			return this;
+		}
+		HashSet<Nodeid> copy = new HashSet<Nodeid>(elements);
+		copy.addAll(other.elements);
+		return new RevisionSet(copy);
+	}
+
+	/**
+	 * A ^ B := (A\B).union(B\A)
+	 * A ^ B := A.union(B) \ A.intersect(B)
+	 */
+	public RevisionSet symmetricDifference(RevisionSet other) {
+		if (isEmpty()) {
+			return this;
+		}
+		if (other.isEmpty()) {
+			return other;
+		}
+		HashSet<Nodeid> copyA = new HashSet<Nodeid>(elements);
+		HashSet<Nodeid> copyB = new HashSet<Nodeid>(other.elements);
+		copyA.removeAll(other.elements);
+		copyB.removeAll(elements);
+		copyA.addAll(copyB);
+		return new RevisionSet(copyA);
+	}
+
+	public boolean isEmpty() {
+		return elements.isEmpty();
+	}
+
+	@Override
+	public String toString() {
+		StringBuilder sb = new StringBuilder();
+		sb.append('<');
+		if (!isEmpty()) {
+			sb.append(elements.size());
+			sb.append(':');
+		}
+		for (Nodeid n : elements) {
+			sb.append(n.shortNotation());
+			sb.append(',');
+		}
+		if (sb.length() > 1) {
+			sb.setCharAt(sb.length() - 1, '>');
+		} else {
+			sb.append('>');
+		}
+		return sb.toString();
+	}
+	
+	@Override
+	public boolean equals(Object obj) {
+		if (false == obj instanceof RevisionSet) {
+			return false;
+		}
+		return elements.equals(((RevisionSet) obj).elements);
+	}
+	
+	@Override
+	public int hashCode() {
+		return elements.hashCode();
+	}
+}
--- a/test/org/tmatesoft/hg/test/TestPhases.java	Tue Jun 25 18:53:18 2013 +0200
+++ b/test/org/tmatesoft/hg/test/TestPhases.java	Tue Jun 25 20:48:37 2013 +0200
@@ -18,12 +18,16 @@
 
 import static org.junit.Assert.*;
 
+import java.util.ArrayList;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import org.junit.Rule;
 import org.junit.Test;
+import org.tmatesoft.hg.core.Nodeid;
+import org.tmatesoft.hg.internal.Internals;
 import org.tmatesoft.hg.internal.PhasesHelper;
+import org.tmatesoft.hg.internal.RevisionSet;
 import org.tmatesoft.hg.repo.HgChangelog;
 import org.tmatesoft.hg.repo.HgInternals;
 import org.tmatesoft.hg.repo.HgLookup;
@@ -67,6 +71,41 @@
 		final long end = System.nanoTime();
 		System.out.printf("With ParentWalker(simulates log command for whole repo): %,d μs (pw init: %,d ns)\n", (end - start1)/1000, start2 - start1);
 	}
+	
+	@Test
+	public void testAllSecretAndDraft() throws Exception {
+		HgRepository repo = Configuration.get().find("test-phases");
+		Internals implRepo = HgInternals.getImplementationRepo(repo);
+		HgPhase[] expected = readPhases(repo);
+		ArrayList<Nodeid> secret = new ArrayList<Nodeid>();
+		ArrayList<Nodeid> draft = new ArrayList<Nodeid>();
+		ArrayList<Nodeid> pub = new ArrayList<Nodeid>();
+		for (int i = 0; i < expected.length; i++) {
+			Nodeid n = repo.getChangelog().getRevision(i);
+			switch (expected[i]) {
+			case Secret : secret.add(n); break; 
+			case Draft : draft.add(n); break;
+			case Public : pub.add(n); break;
+			default : throw new IllegalStateException();
+			}
+		}
+		final RevisionSet rsSecret = new RevisionSet(secret);
+		final RevisionSet rsDraft = new RevisionSet(draft);
+		assertFalse("[sanity]", rsSecret.isEmpty());
+		assertFalse("[sanity]", rsDraft.isEmpty());
+		HgParentChildMap<HgChangelog> pw = new HgParentChildMap<HgChangelog>(repo.getChangelog());
+		pw.init();
+		PhasesHelper ph1 = new PhasesHelper(implRepo, null);
+		PhasesHelper ph2 = new PhasesHelper(implRepo, pw);
+		RevisionSet s1 = ph1.allSecret().symmetricDifference(rsSecret);
+		RevisionSet s2 = ph2.allSecret().symmetricDifference(rsSecret);
+		errorCollector.assertTrue("Secret,no ParentChildMap:" + s1.toString(), s1.isEmpty());
+		errorCollector.assertTrue("Secret, with ParentChildMap:" + s2.toString(), s2.isEmpty());
+		RevisionSet s3 = ph1.allDraft().symmetricDifference(rsDraft);
+		RevisionSet s4 = ph2.allDraft().symmetricDifference(rsDraft);
+		errorCollector.assertTrue("Draft,no ParentChildMap:" + s3.toString(), s3.isEmpty());
+		errorCollector.assertTrue("Draft, with ParentChildMap:" + s4.toString(), s4.isEmpty());
+	}
 
 	private HgPhase[] initAndCheck(PhasesHelper ph, HgPhase[] expected) throws HgRuntimeException {
 		HgChangelog clog = ph.getRepo().getChangelog();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/org/tmatesoft/hg/test/TestRevisionSet.java	Tue Jun 25 20:48:37 2013 +0200
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2013 TMate Software Ltd
+ *  
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * For information on how to redistribute this software under
+ * the terms of a license other than GNU General Public License
+ * contact TMate Software at support@hg4j.com
+ */
+package org.tmatesoft.hg.test;
+
+import java.util.Arrays;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.tmatesoft.hg.core.Nodeid;
+import org.tmatesoft.hg.internal.RevisionSet;
+
+/**
+ * 
+ * @author Artem Tikhomirov
+ * @author TMate Software Ltd.
+ */
+public class TestRevisionSet {
+	
+	@Rule
+	public ErrorCollectorExt errorCollector = new ErrorCollectorExt();
+	
+	@Test
+	public void testRegularSetOperations() {
+		Nodeid n1 = Nodeid.fromAscii("c75297c1786734589175c673db40e8ecaa032b09");
+		Nodeid n2 = Nodeid.fromAscii("3b7d51ed4c65082f9235e3459e282d7ff723aa97");
+		Nodeid n3 = Nodeid.fromAscii("14dac192aa262feb8ff6645a102648498483a188");
+		Nodeid n4 = Nodeid.fromAscii("1deea2f332183c947937f6df988c2c6417efc217");
+		RevisionSet a = f(n1, n2, n3);
+		RevisionSet b = f(n3, n4);
+		RevisionSet union_ab = f(n1, n2, n3, n4);
+		RevisionSet intersect_ab = f(n3);
+		RevisionSet subtract_ab = f(n1, n2);
+		RevisionSet subtract_ba = f(n4);
+		RevisionSet symDiff_ab = f(n1, n2, n4);
+		
+		errorCollector.assertEquals(union_ab, a.union(b));
+		errorCollector.assertEquals(union_ab, b.union(a));
+		errorCollector.assertEquals(intersect_ab, a.intersect(b));
+		errorCollector.assertEquals(intersect_ab, b.intersect(a));
+		errorCollector.assertEquals(subtract_ab, a.subtract(b));
+		errorCollector.assertEquals(subtract_ba, b.subtract(a));
+		errorCollector.assertEquals(symDiff_ab, a.symmetricDifference(b));
+		errorCollector.assertEquals(symDiff_ab, b.symmetricDifference(a));
+	}
+
+	
+	private static RevisionSet f(Nodeid... nodes) {
+		return new RevisionSet(Arrays.asList(nodes));
+	}
+}