changeset 305:ae8d116f4ee2

Experimental code to build file history
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Thu, 22 Sep 2011 03:57:38 +0200
parents 85b8efde5586
children 971baa95fb07
files cmdline/org/tmatesoft/hg/console/Main.java src/org/tmatesoft/hg/repo/HgDataFile.java
diffstat 2 files changed, 193 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/cmdline/org/tmatesoft/hg/console/Main.java	Wed Sep 21 18:26:16 2011 +0200
+++ b/cmdline/org/tmatesoft/hg/console/Main.java	Thu Sep 22 03:57:38 2011 +0200
@@ -20,6 +20,7 @@
 
 import java.io.File;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -85,7 +86,8 @@
 
 	public static void main(String[] args) throws Exception {
 		Main m = new Main(args);
-		m.testConsoleLog();
+		m.buildFileLog();
+//		m.testConsoleLog();
 //		m.testTreeTraversal();
 //		m.testRevisionMap();
 //		m.testSubrepos();
@@ -105,6 +107,59 @@
 //		m.dumpCompleteManifestHigh();
 //		m.bunchOfTests();
 	}
+
+	private void buildFileLog() {
+		final HgDataFile fn = hgRepo.getFileNode("file1");
+		HgDataFile.HistoryWalker hw = fn.history();
+		while (hw.hasNext()) {
+			hw.next();
+			StringBuilder sb = new StringBuilder();
+			Collection<Nodeid> children = hw.childChangesets();
+			for (Nodeid cc : children) {
+				sb.append(cc.shortNotation());
+				sb.append(", ");
+			}
+			if (hw.isJoin()) {
+				final Pair<Nodeid, Nodeid> parents = hw.parentChangesets();
+				System.out.printf("join[(%s, %s) => %s]\n", parents.first().shortNotation(), parents.second().shortNotation(), hw.changesetRevision().shortNotation());
+			}
+			if (hw.isFork()) {
+				System.out.printf("fork[%s => %s]\n", hw.changesetRevision().shortNotation(), sb);
+			}
+			if (!hw.isFork() && !hw.isJoin() && !children.isEmpty()) {
+				System.out.printf("%s => %s\n", hw.changesetRevision().shortNotation(), sb);
+			}
+		}
+	}
+
+	private void buildFileLogOld() {
+		final HgDataFile fn = hgRepo.getFileNode("file1");
+		final int[] fileChangesetRevisions = new int[fn.getRevisionCount()];
+		fn.history(new HgChangelog.Inspector() {
+			private int fileLocalRevisions = 0;
+			private int[] parentRevisions = new int[2];
+			
+			public void next(int revisionNumber, Nodeid nodeid, RawChangeset cset) {
+				fileChangesetRevisions[fileLocalRevisions] = revisionNumber;
+				fn.parents(fileLocalRevisions, parentRevisions, null, null);
+				boolean join = parentRevisions[0] != -1 && parentRevisions[1] != -1;
+				if (join) {
+					System.out.print("join[");
+				}
+				if (parentRevisions[0] != -1) {
+					System.out.printf("%2d->%2d, ", fileChangesetRevisions[parentRevisions[0]], revisionNumber);
+				}
+				if (parentRevisions[1] != -1) {
+					System.out.printf("%2d->%2d, ", fileChangesetRevisions[parentRevisions[1]], revisionNumber);
+				}
+				if (join) {
+					System.out.print("]");
+				}
+				fileLocalRevisions++;
+			}
+		});
+		System.out.println();
+	}
 	
 	private void testConsoleLog() {
 		LogFacility fc = new StreamLogFacility(true, true, true, System.out);
--- a/src/org/tmatesoft/hg/repo/HgDataFile.java	Wed Sep 21 18:26:16 2011 +0200
+++ b/src/org/tmatesoft/hg/repo/HgDataFile.java	Thu Sep 22 03:57:38 2011 +0200
@@ -28,18 +28,24 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.NoSuchElementException;
 
 import org.tmatesoft.hg.core.HgDataStreamException;
 import org.tmatesoft.hg.core.HgException;
 import org.tmatesoft.hg.core.Nodeid;
 import org.tmatesoft.hg.internal.DataAccess;
+import org.tmatesoft.hg.internal.Experimental;
 import org.tmatesoft.hg.internal.FilterByteChannel;
 import org.tmatesoft.hg.internal.FilterDataAccess;
 import org.tmatesoft.hg.internal.IntMap;
 import org.tmatesoft.hg.internal.RevlogStream;
+import org.tmatesoft.hg.repo.HgChangelog.RawChangeset;
 import org.tmatesoft.hg.util.ByteChannel;
 import org.tmatesoft.hg.util.CancelSupport;
 import org.tmatesoft.hg.util.CancelledException;
+import org.tmatesoft.hg.util.Pair;
 import org.tmatesoft.hg.util.Path;
 import org.tmatesoft.hg.util.ProgressSupport;
 
@@ -225,6 +231,137 @@
 		}
 	}
 	
+	@Experimental(reason="Investigate approaches to build complete file history log")
+	public interface HistoryWalker {
+		void next();
+		boolean hasNext();
+		Nodeid changesetRevision();
+		RawChangeset changeset();
+		boolean isFork();
+		boolean isJoin();
+		Pair<Nodeid, Nodeid> parentChangesets();
+		Collection<Nodeid> childChangesets();
+	}
+	
+	public HistoryWalker history() {
+		final boolean[] needsSorting = { false };
+		class HistoryNode {
+			int changeset;
+			Nodeid cset;
+			HistoryNode parent1, parent2;
+			List<HistoryNode> children;
+
+			public HistoryNode(int cs, HistoryNode p1, HistoryNode p2) {
+				changeset = cs;
+				parent1 = p1;
+				parent2 = p2;
+				if (p1 != null) {
+					p1.addChild(this);
+				}
+				if (p2 != null) {
+					p2.addChild(this);
+				}
+			}
+			
+			public Nodeid changesetRevision() {
+				if (cset == null) {
+					cset = getRepo().getChangelog().getRevision(changeset);
+				}
+				return cset;
+			}
+
+			public void addChild(HistoryNode child) {
+				if (children == null) {
+					children = new ArrayList<HistoryNode>(2);
+				}
+				children.add(child);
+			}
+		}
+		final HistoryNode[] completeHistory = new HistoryNode[getRevisionCount()];
+		final int[] commitRevisions = new int[completeHistory.length];
+		RevlogStream.Inspector insp = new RevlogStream.Inspector() {
+			public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess data) {
+				if (revisionNumber > 0) {
+					if (commitRevisions[revisionNumber-1] > linkRevision) {
+						needsSorting[0] = true;
+					}
+				}
+				HistoryNode p1 = null, p2 = null;
+				if (parent1Revision != -1) {
+					p1 = completeHistory[parent1Revision];
+				}
+				if (parent2Revision != -1) {
+					p2 = completeHistory[parent2Revision];
+				}
+				completeHistory[revisionNumber] = new HistoryNode(linkRevision, p1, p2);
+			}
+		};
+		content.iterate(0, getLastRevision(), false, insp);
+		// XXX may read changeset revisions at once (to avoid numerous changelog.getRevision reads)
+		// but perhaps just nodeids, not RawChangeset (changelog.iterate(data=false)
+		// XXX sort completeHistory according to changeset numbers?
+		return new HistoryWalker() {
+			private int index = -1;
+			
+			public void next() {
+				if (!hasNext()) {
+					throw new NoSuchElementException();
+				}
+				index++;
+			}
+
+			public boolean hasNext() {
+				return index+1 < completeHistory.length;
+			}
+			
+			public Pair<Nodeid, Nodeid> parentChangesets() {
+				HistoryNode p;
+				Nodeid p1, p2;
+				if ((p = completeHistory[index].parent1) != null) {
+					p1 = p.changesetRevision();
+				} else {
+					p1 = Nodeid.NULL;
+				}
+				if ((p = completeHistory[index].parent2) != null) {
+					p2 = p.changesetRevision();
+				} else {
+					p2 = Nodeid.NULL;
+				}
+				return new Pair<Nodeid, Nodeid>(p1, p2);
+			}
+
+			public boolean isJoin() {
+				return completeHistory[index].parent1 != null && completeHistory[index].parent2 != null;
+			}
+			
+			public boolean isFork() {
+				HistoryNode n = completeHistory[index];
+				return n.children != null && n.children.size() > 1;
+			}
+			
+			public Collection<Nodeid> childChangesets() {
+				HistoryNode n = completeHistory[index];
+				if (n.children == null) {
+					return Collections.emptyList();
+				}
+				ArrayList<Nodeid> rv = new ArrayList<Nodeid>(n.children.size());
+				for (HistoryNode hn : n.children) {
+					rv.add(hn.changesetRevision());
+				}
+				return rv;
+			}
+			
+			public Nodeid changesetRevision() {
+				return completeHistory[index].changesetRevision();
+			}
+			
+			public RawChangeset changeset() {
+				
+				return null;
+			}
+		};
+	}
+	
 	public void history(HgChangelog.Inspector inspector) {
 		history(0, getLastRevision(), inspector);
 	}