# HG changeset patch # User Artem Tikhomirov # Date 1316656658 -7200 # Node ID ae8d116f4ee2cb898eb42aa6a48f1bea28a89d8d # Parent 85b8efde5586e18b72f22fe2af3a1af7be794142 Experimental code to build file history diff -r 85b8efde5586 -r ae8d116f4ee2 cmdline/org/tmatesoft/hg/console/Main.java --- 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 children = hw.childChangesets(); + for (Nodeid cc : children) { + sb.append(cc.shortNotation()); + sb.append(", "); + } + if (hw.isJoin()) { + final Pair 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); diff -r 85b8efde5586 -r ae8d116f4ee2 src/org/tmatesoft/hg/repo/HgDataFile.java --- 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 parentChangesets(); + Collection childChangesets(); + } + + public HistoryWalker history() { + final boolean[] needsSorting = { false }; + class HistoryNode { + int changeset; + Nodeid cset; + HistoryNode parent1, parent2; + List 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(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 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(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 childChangesets() { + HistoryNode n = completeHistory[index]; + if (n.children == null) { + return Collections.emptyList(); + } + ArrayList rv = new ArrayList(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); }