diff src/org/tmatesoft/hg/repo/HgParentChildMap.java @ 652:cd77bf51b562

Push: tests. Commit respects phases.new-commit setting. Fix outgoing when changes are not children of common (Issue 47)
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Tue, 02 Jul 2013 23:21:16 +0200
parents 3b275cc2d2aa
children 629a7370554c
line wrap: on
line diff
--- a/src/org/tmatesoft/hg/repo/HgParentChildMap.java	Mon Jul 01 21:19:53 2013 +0200
+++ b/src/org/tmatesoft/hg/repo/HgParentChildMap.java	Tue Jul 02 23:21:16 2013 +0200
@@ -18,7 +18,9 @@
 
 import static org.tmatesoft.hg.repo.HgRepository.TIP;
 
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.BitSet;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
@@ -62,9 +64,10 @@
 	private Nodeid[] sequential; // natural repository order, childrenOf rely on ordering
 	private Nodeid[] sorted; // for binary search
 	private int[] sorted2natural; // indexes in sorted to indexes in sequential
-	private Nodeid[] firstParent;
+	private Nodeid[] firstParent; // parents by natural order (i.e. firstParent[A] is parent of revision with index A)
 	private Nodeid[] secondParent;
 	private final T revlog;
+	private BitSet heads; // 1 indicates revision got children
 
 
 	public HgParentChildMap(T owner) {
@@ -83,9 +86,11 @@
 		sequential[ix] = sorted[ix] = revision;
 		if (parent1Revision != -1) {
 			firstParent[ix] = sequential[parent1Revision];
+			heads.set(parent1Revision);
 		}
 		if (parent2Revision != -1) { // revlog of DataAccess.java has p2 set when p1 is -1
 			secondParent[ix] = sequential[parent2Revision];
+			heads.set(parent2Revision);
 		}
 	}
 	
@@ -97,13 +102,14 @@
 	public void init() throws HgRuntimeException {
 		final int revisionCount = revlog.getRevisionCount();
 		firstParent = new Nodeid[revisionCount];
-		// TODO [post 1.0] Branches/merges are less frequent, and most of secondParent would be -1/null, hence 
+		// TODO [post 1.1] Branches/merges are less frequent, and most of secondParent would be -1/null, hence 
 		// IntMap might be better alternative here, but need to carefully analyze (test) whether this brings
 		// real improvement (IntMap has 2n capacity, and element lookup is log(n) instead of array's constant)
 		secondParent = new Nodeid[revisionCount];
 		//
 		sequential = new Nodeid[revisionCount];
 		sorted = new Nodeid[revisionCount];
+		heads = new BitSet(revisionCount);
 		revlog.indexWalk(0, TIP, this);
 		Arrays.sort(sorted);
 		sorted2natural = new int[revisionCount];
@@ -233,20 +239,7 @@
 		int x = Arrays.binarySearch(sorted, nid);
 		assertSortedIndex(x);
 		int i = sorted2natural[x];
-		assert firstParent.length == secondParent.length; // just in case later I implement sparse array for secondParent
-		assert firstParent.length == sequential.length;
-		// to use == instead of equals, take the same Nodeid instance we used to fill all the arrays.
-		final Nodeid canonicalNode = sequential[i];
-		i++; // no need to check node itself. child nodes may appear in sequential only after revision in question
-		for (; i < sequential.length; i++) {
-			// TODO [post 1.0] likely, not very effective. 
-			// May want to optimize it with another (Tree|Hash)Set, created on demand on first use, 
-			// however, need to be careful with memory usage
-			if (firstParent[i] == canonicalNode || secondParent[i] == canonicalNode) {
-				return true;
-			}
-		}
-		return false;
+		return hasChildren(i);
 	}
 
 	/**
@@ -267,14 +260,19 @@
 		int x = Arrays.binarySearch(sorted, root);
 		assertSortedIndex(x);
 		root = sorted[x]; // canonical instance
+		final int start = sorted2natural[x];
+		if (!hasChildren(start)) {
+			return false; // root got no children at all
+		}
 		int y = Arrays.binarySearch(sorted, wannaBeChild);
-		if (y < 0 || y <= x) {
-			// not found or comes earlier than root
-			return false;
+		if (y < 0) {
+			return false; // not found
 		}
 		wannaBeChild = sorted[y]; // canonicalize
-		final int start = sorted2natural[x];
 		final int end = sorted2natural[y];
+		if (end <= start) {
+			return false; // potential child was in repository earlier than root
+		}
 		HashSet<Nodeid> parents = new HashSet<Nodeid>();
 		parents.add(root);
 		for (int i = start + 1; i < end; i++) {
@@ -284,4 +282,22 @@
 		}
 		return parents.contains(firstParent[end]) || parents.contains(secondParent[end]);
 	}
+	
+	/**
+	 * @return elements of this map that do not have a child recorded therein.
+	 */
+	public Collection<Nodeid> heads() {
+		ArrayList<Nodeid> result = new ArrayList<Nodeid>();
+		int index = 0;
+		do {
+			index = heads.nextClearBit(index);
+			result.add(sequential[index]);
+			index++;
+		} while (index < sequential.length);
+		return result;
+	}
+
+	private boolean hasChildren(int sequentialIndex) {
+		return heads.get(sequentialIndex);
+	}
 }
\ No newline at end of file