# HG changeset patch # User Artem Tikhomirov # Date 1355318232 -3600 # Node ID a6435c1a42d08e80576e1b8b3b4ec38695b86deb # Parent 27398bbfd543f3bdcadaffd1107d2d213634a292 Test for HgChangesetTreeHandler - make sure nothing is broken prior to adding --follow support diff -r 27398bbfd543 -r a6435c1a42d0 cmdline/org/tmatesoft/hg/console/Main.java --- a/cmdline/org/tmatesoft/hg/console/Main.java Fri Nov 30 22:52:39 2012 +0100 +++ b/cmdline/org/tmatesoft/hg/console/Main.java Wed Dec 12 14:17:12 2012 +0100 @@ -96,11 +96,11 @@ public static void main(String[] args) throws Exception { Main m = new Main(args); - m.tryExtensions(); +// m.tryExtensions(); // m.dumpBookmarks(); // m.readConfigFile(); // m.dumpCommitLastMessage(); -// m.buildFileLog(); + m.buildFileLog(); // m.testConsoleLog(); // m.testTreeTraversal(); // m.testRevisionMap(); diff -r 27398bbfd543 -r a6435c1a42d0 src/org/tmatesoft/hg/core/HgChangesetTreeHandler.java --- a/src/org/tmatesoft/hg/core/HgChangesetTreeHandler.java Fri Nov 30 22:52:39 2012 +0100 +++ b/src/org/tmatesoft/hg/core/HgChangesetTreeHandler.java Wed Dec 12 14:17:12 2012 +0100 @@ -47,7 +47,7 @@ public Nodeid fileRevision(); /** - * @return changeset associated with the current revision + * @return changeset associated with the current file revision */ public HgChangeset changeset(); @@ -58,7 +58,26 @@ public Nodeid changesetRevision(); /** - * Node, these are not necessarily in direct relation to parents of changeset from {@link #changeset()} + * Identifies parent changes, changesets where file/revlog in question was modified prior to change being visited. + * + * Note, these are not necessarily in direct relation to parents of changeset from {@link #changeset()} + * + * Imagine next history (grows from bottom to top): + *
+		 * o A    o
+		 * |   \  |
+		 * o B  \/
+		 * |    o C
+		 * |   /
+		 * o  /
+		 * | /
+		 * o D
+		 * 
+ * + * When we are at {@link TreeElement} for A, B and C are changeset parents, naturally. However + * if the file/revlog we've been walking has not been changed in B and C, but e.g. in D only, + * then this {@link #parents()} call would return pair with single element only, pointing to D + * * @return changesets that correspond to parents of the current file node, either pair element may be null. */ public Pair parents(); diff -r 27398bbfd543 -r a6435c1a42d0 src/org/tmatesoft/hg/core/HgLogCommand.java --- a/src/org/tmatesoft/hg/core/HgLogCommand.java Fri Nov 30 22:52:39 2012 +0100 +++ b/src/org/tmatesoft/hg/core/HgLogCommand.java Wed Dec 12 14:17:12 2012 +0100 @@ -330,36 +330,65 @@ } }; final ProgressSupport progressHelper = getProgressSupport(handler); - progressHelper.start(4); final CancelSupport cancelHelper = getCancelSupport(handler, true); - cancelHelper.checkCancelled(); - HgDataFile fileNode = repo.getFileNode(file); - // build tree of nodes according to parents in file's revlog - final TreeBuildInspector treeBuildInspector = new TreeBuildInspector(); - final HistoryNode[] completeHistory = treeBuildInspector.go(fileNode); - progressHelper.worked(1); - cancelHelper.checkCancelled(); - ElementImpl ei = new ElementImpl(treeBuildInspector.commitRevisions.length); - final ProgressSupport ph2; - if (treeBuildInspector.commitRevisions.length < 100 /*XXX is it really worth it? */) { - ei.initTransform(); - repo.getChangelog().range(ei, treeBuildInspector.commitRevisions); + + LinkedList fileRenamesQueue = buildFileRenamesQueue(); + progressHelper.start(4 * fileRenamesQueue.size()); + do { + HgDataFile fileNode = fileRenamesQueue.removeLast(); + cancelHelper.checkCancelled(); + // build tree of nodes according to parents in file's revlog + final TreeBuildInspector treeBuildInspector = new TreeBuildInspector(); + final HistoryNode[] completeHistory = treeBuildInspector.go(fileNode); progressHelper.worked(1); - ph2 = new ProgressSupport.Sub(progressHelper, 2); - } else { - ph2 = new ProgressSupport.Sub(progressHelper, 3); - } - ph2.start(completeHistory.length); - // XXX shall sort completeHistory according to changeset numbers? - for (int i = 0; i < completeHistory.length; i++ ) { - final HistoryNode n = completeHistory[i]; - handler.treeElement(ei.init(n)); - ph2.worked(1); cancelHelper.checkCancelled(); - } + ElementImpl ei = new ElementImpl(treeBuildInspector.commitRevisions.length); + final ProgressSupport ph2; + if (treeBuildInspector.commitRevisions.length < 100 /*XXX is it really worth it? */) { + ei.initTransform(); + repo.getChangelog().range(ei, treeBuildInspector.commitRevisions); + progressHelper.worked(1); + ph2 = new ProgressSupport.Sub(progressHelper, 2); + } else { + ph2 = new ProgressSupport.Sub(progressHelper, 3); + } + ph2.start(completeHistory.length); + // XXX shall sort completeHistory according to changeset numbers? + for (int i = 0; i < completeHistory.length; i++ ) { + final HistoryNode n = completeHistory[i]; + handler.treeElement(ei.init(n)); + ph2.worked(1); + cancelHelper.checkCancelled(); + } + } while (!fileRenamesQueue.isEmpty()); progressHelper.done(); } + /** + * Follows file renames and build a list of all corresponding file nodes. If {@link #followHistory} is false, + * the list contains one element only, file node with the name of the file as it was specified by the user. + * + * @return list of file renames, with most recent file first + */ + private LinkedList buildFileRenamesQueue() { + LinkedList rv = new LinkedList(); + if (!followHistory) { + rv.add(repo.getFileNode(file)); + return rv; + } + HgDataFile fileNode; + Path fp = file; + boolean isCopy; + do { + fileNode = repo.getFileNode(fp); + rv.addLast(fileNode); + if (isCopy = fileNode.isCopy()) { + fp = fileNode.getCopySourceName(); + } + } while (isCopy); + return rv; + } + // public void next(int revisionNumber, Nodeid nodeid, RawChangeset cset) { diff -r 27398bbfd543 -r a6435c1a42d0 test/org/tmatesoft/hg/test/TestHistory.java --- a/test/org/tmatesoft/hg/test/TestHistory.java Fri Nov 30 22:52:39 2012 +0100 +++ b/test/org/tmatesoft/hg/test/TestHistory.java Wed Dec 12 14:17:12 2012 +0100 @@ -29,14 +29,18 @@ import org.junit.Rule; import org.junit.Test; +import org.tmatesoft.hg.core.HgCallbackTargetException; import org.tmatesoft.hg.core.HgChangeset; import org.tmatesoft.hg.core.HgChangesetHandler; +import org.tmatesoft.hg.core.HgChangesetTreeHandler; import org.tmatesoft.hg.core.HgFileRevision; import org.tmatesoft.hg.core.HgLogCommand; import org.tmatesoft.hg.core.HgLogCommand.CollectHandler; +import org.tmatesoft.hg.core.Nodeid; import org.tmatesoft.hg.repo.HgLookup; import org.tmatesoft.hg.repo.HgRepository; import org.tmatesoft.hg.test.LogOutputParser.Record; +import org.tmatesoft.hg.util.Pair; import org.tmatesoft.hg.util.Path; @@ -118,6 +122,39 @@ }); report(what, sorted, false); } + + @Test + public void testChangesetTree() throws Exception { + repo = Configuration.get().find("branches-1"); + final String fname = "file1"; + assertTrue("[sanity]", repo.getFileNode(fname).exists()); + eh.run("hg", "log", "--debug", fname, "--cwd", repo.getLocation()); + + final LinkedList cmdResult = new LinkedList(); + new HgLogCommand(repo).file(fname, false).execute(new HgChangesetTreeHandler() { + + public void treeElement(TreeElement entry) throws HgCallbackTargetException { + // check consistency + Nodeid cset = entry.changeset().getNodeid(); + errorCollector.assertEquals(entry.changesetRevision(), cset); + Pair parents_a = entry.parents(); + Pair parents_b = entry.parentRevisions(); + if (parents_b.first().isNull()) { + errorCollector.assertTrue(parents_a.first() == null); + } else { + errorCollector.assertEquals(parents_b.first(), parents_a.first().getNodeid()); + } + if (parents_b.second().isNull()) { + errorCollector.assertTrue(parents_a.second() == null); + } else { + errorCollector.assertEquals(parents_b.second(), parents_a.second().getNodeid()); + } + // + cmdResult.add(entry.changeset()); + } + }); + report("execute with HgChangesetTreeHandler", cmdResult, true); + } private void report(String what, List r, boolean reverseConsoleResult) { final List consoleResult = changelogParser.getResult();