Mercurial > jhg
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)); + } +}