diff src/org/tmatesoft/hg/internal/RevisionDescendants.java @ 471:7bcfbc255f48

Merge changes from smartgit3 branch into 1.1 stream
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Wed, 11 Jul 2012 20:40:47 +0200
parents 03fd8d079e9c
children 2a0b09eec376
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/tmatesoft/hg/internal/RevisionDescendants.java	Wed Jul 11 20:40:47 2012 +0200
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2012 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.BitSet;
+
+import org.tmatesoft.hg.core.Nodeid;
+import org.tmatesoft.hg.repo.HgChangelog;
+import org.tmatesoft.hg.repo.HgInvalidControlFileException;
+import org.tmatesoft.hg.repo.HgInvalidStateException;
+import org.tmatesoft.hg.repo.HgRepository;
+
+/**
+ * Represent indicators which revisions are descendants of the supplied root revision
+ * This is sort of lightweight alternative to ParentWalker#childrenOf 
+ * 
+ * @author Artem Tikhomirov
+ * @author TMate Software Ltd.
+ */
+public class RevisionDescendants {
+
+	private final HgRepository repo;
+	private final int rootRevIndex;
+	private final int tipRevIndex; // this is the last revision we cache to
+	private final BitSet descendants;
+
+	// 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) {
+		repo = hgRepo;
+		rootRevIndex = revisionIndex;
+		// even if tip moves, we still answer correctly for those isCandidate()
+		tipRevIndex = repo.getChangelog().getLastRevision(); 
+		if (revisionIndex < 0 || revisionIndex > tipRevIndex) {
+			String m = "Revision to build descendants for shall be in range [%d,%d], not %d";
+			throw new IllegalArgumentException(String.format(m, 0, tipRevIndex, revisionIndex));
+		}
+		descendants = new BitSet(tipRevIndex - rootRevIndex + 1);
+	}
+	
+	public void build() throws HgInvalidControlFileException {
+		final BitSet result = descendants;
+		result.set(0);
+		if (rootRevIndex == tipRevIndex) {
+			return;
+		}
+		repo.getChangelog().indexWalk(rootRevIndex+1, tipRevIndex, new HgChangelog.ParentInspector() {
+			// TODO ParentRevisionInspector, with no parent nodeids, just indexes?
+
+			private int i = 1; // above we start with revision next to rootRevIndex, which is at offset 0
+			public void next(int revisionIndex, Nodeid revision, int parent1, int parent2, Nodeid nidParent1, Nodeid nidParent2) {
+				int p1x = parent1 - rootRevIndex;
+				int p2x = parent2 - rootRevIndex;
+				boolean p1IsDescendant = false, p2IsDescendant = false;
+				if (p1x >= 0) { // parent1 is among descendants candidates
+					assert p1x < result.size();
+					p1IsDescendant = result.get(p1x);
+				}
+				if (p2x >= 0) {
+					assert p2x < result.size();
+					p2IsDescendant = result.get(p2x);
+				}
+				//
+				int rx = revisionIndex - rootRevIndex;
+				if (rx != i) {
+					throw new HgInvalidStateException(String.format("Sanity check failed. Revision %d. Expected:%d, was:%d", revisionIndex, rx, i));
+				}
+				// current revision is descendant if any of its parents is descendant
+				result.set(rx, p1IsDescendant || p2IsDescendant);
+				i++;
+			}
+		});
+	}
+
+	// deliberately doesn't allow TIP
+	public boolean isCandidate(int revIndex) {
+		return (revIndex >= rootRevIndex && revIndex <= tipRevIndex) ;
+	}
+
+	public boolean hasDescendants() { // isEmpty is better name?
+		// bit at rootRevIndex is always set
+		return descendants.nextSetBit(rootRevIndex+1) != -1;
+	}
+
+	public boolean isDescendant(int revisionIndex) {
+		assert isCandidate(revisionIndex);
+		int ix = revisionIndex - rootRevIndex;
+		assert ix < descendants.size();
+		return descendants.get(ix);
+	}
+}