Mercurial > hg4j
diff src/org/tmatesoft/hg/core/HgLogCommand.java @ 520:1ee452f31187
Experimental support for inverse direction history walking. Refactored/streamlined cancellation in HgLogCommand and down the stack
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Fri, 21 Dec 2012 21:20:26 +0100 |
parents | 0d5e1ea7955e |
children | 2103388d4010 |
line wrap: on
line diff
--- a/src/org/tmatesoft/hg/core/HgLogCommand.java Thu Dec 20 20:21:59 2012 +0100 +++ b/src/org/tmatesoft/hg/core/HgLogCommand.java Fri Dec 21 21:20:26 2012 +0100 @@ -34,9 +34,13 @@ import java.util.Set; import java.util.TreeSet; +import org.tmatesoft.hg.internal.AdapterPlug; +import org.tmatesoft.hg.internal.BatchRangeHelper; +import org.tmatesoft.hg.internal.Experimental; import org.tmatesoft.hg.internal.IntMap; import org.tmatesoft.hg.internal.IntVector; import org.tmatesoft.hg.internal.Lifecycle; +import org.tmatesoft.hg.internal.LifecycleProxy; import org.tmatesoft.hg.repo.HgChangelog; import org.tmatesoft.hg.repo.HgChangelog.RawChangeset; import org.tmatesoft.hg.repo.HgDataFile; @@ -287,19 +291,41 @@ throw new HgBadArgumentException(String.format("Bad value %d for start revision for range [%1$d..%d]", startRev, lastCset), null); } final ProgressSupport progressHelper = getProgressSupport(handler); + final int BATCH_SIZE = 100; try { count = 0; HgParentChildMap<HgChangelog> pw = getParentHelper(file == null); // leave it uninitialized unless we iterate whole repo // ChangesetTransfrom creates a blank PathPool, and #file(String, boolean) above // may utilize it as well. CommandContext? How about StatusCollector there as well? csetTransform = new ChangesetTransformer(repo, handler, pw, progressHelper, getCancelSupport(handler, true)); + // FilteringInspector is responsible to check command arguments: users, branches, limit, etc. + // prior to passing cset to next Inspector, which is either (a) collector to reverse cset order, then invokes + // transformer from (b), below, with alternative cset order or (b) transformer to hi-level csets. FilteringInspector filterInsp = new FilteringInspector(); filterInsp.changesets(startRev, lastCset); if (file == null) { - progressHelper.start(endRev - startRev + 1); - repo.getChangelog().range(startRev, endRev, filterInsp); - csetTransform.checkFailure(); + progressHelper.start(lastCset - startRev + 1); + if (iterateDirection == IterateDirection.FromOldToNew) { + filterInsp.delegateTo(csetTransform); + repo.getChangelog().range(startRev, lastCset, filterInsp); + csetTransform.checkFailure(); + } else { + assert iterateDirection == IterateDirection.FromNewToOld; + BatchRangeHelper brh = new BatchRangeHelper(startRev, lastCset, BATCH_SIZE, true); + BatchChangesetInspector batchInspector = new BatchChangesetInspector(Math.min(lastCset-startRev+1, BATCH_SIZE)); + filterInsp.delegateTo(batchInspector); + while (brh.hasNext()) { + brh.next(); + repo.getChangelog().range(brh.start(), brh.end(), filterInsp); + for (BatchChangesetInspector.BatchRecord br : batchInspector.iterate(true)) { + csetTransform.next(br.csetIndex, br.csetRevision, br.cset); + csetTransform.checkFailure(); + } + batchInspector.reset(); + } + } } else { + filterInsp.delegateTo(csetTransform); final HgFileRenameHandlerMixin withCopyHandler = Adaptable.Factory.getAdapter(handler, HgFileRenameHandlerMixin.class, null); List<Pair<HgDataFile, Nodeid>> fileRenames = buildFileRenamesQueue(); progressHelper.start(-1/*XXX enum const, or a dedicated method startUnspecified(). How about startAtLeast(int)?*/); @@ -309,10 +335,12 @@ HgDataFile fileNode = curRename.first(); if (followAncestry) { TreeBuildInspector treeBuilder = new TreeBuildInspector(followAncestry); + @SuppressWarnings("unused") List<HistoryNode> fileAncestry = treeBuilder.go(fileNode, curRename.second()); int[] commitRevisions = narrowChangesetRange(treeBuilder.getCommitRevisions(), startRev, lastCset); if (iterateDirection == IterateDirection.FromOldToNew) { repo.getChangelog().range(filterInsp, commitRevisions); + csetTransform.checkFailure(); } else { assert iterateDirection == IterateDirection.FromNewToOld; // visit one by one in the opposite direction @@ -325,8 +353,24 @@ // report complete file history (XXX may narrow range with [startRev, endRev], but need to go from file rev to link rev) int fileStartRev = 0; //fileNode.getChangesetRevisionIndex(0) >= startRev int fileEndRev = fileNode.getLastRevision(); - fileNode.history(fileStartRev, fileEndRev, filterInsp); - csetTransform.checkFailure(); + if (iterateDirection == IterateDirection.FromOldToNew) { + fileNode.history(fileStartRev, fileEndRev, filterInsp); + csetTransform.checkFailure(); + } else { + assert iterateDirection == IterateDirection.FromNewToOld; + BatchRangeHelper brh = new BatchRangeHelper(fileStartRev, fileEndRev, BATCH_SIZE, true); + BatchChangesetInspector batchInspector = new BatchChangesetInspector(Math.min(fileEndRev-fileStartRev+1, BATCH_SIZE)); + filterInsp.delegateTo(batchInspector); + while (brh.hasNext()) { + brh.next(); + fileNode.history(brh.start(), brh.end(), filterInsp); + for (BatchChangesetInspector.BatchRecord br : batchInspector.iterate(true /*iterateDirection == IterateDirection.FromNewToOld*/)) { + csetTransform.next(br.csetIndex, br.csetRevision, br.cset); + csetTransform.checkFailure(); + } + batchInspector.reset(); + } + } } if (followRenames && withCopyHandler != null && nameIndex + 1 < fileRenamesSize) { Pair<HgDataFile, Nodeid> nextRename = fileRenames.get(nameIndex+1); @@ -354,6 +398,46 @@ } } + private static class BatchChangesetInspector extends AdapterPlug implements HgChangelog.Inspector { + private static class BatchRecord { + public final int csetIndex; + public final Nodeid csetRevision; + public final RawChangeset cset; + + public BatchRecord(int index, Nodeid nodeid, RawChangeset changeset) { + csetIndex = index; + csetRevision = nodeid; + cset = changeset; + } + } + private final ArrayList<BatchRecord> batch; + + public BatchChangesetInspector(int batchSizeHint) { + batch = new ArrayList<BatchRecord>(batchSizeHint); + } + + public BatchChangesetInspector reset() { + batch.clear(); + return this; + } + + public void next(int revisionIndex, Nodeid nodeid, RawChangeset cset) { + batch.add(new BatchRecord(revisionIndex, nodeid, cset.clone())); + } + + public Iterable<BatchRecord> iterate(final boolean reverse) { + return new Iterable<BatchRecord>() { + + public Iterator<BatchRecord> iterator() { + return reverse ? new ReverseIterator<BatchRecord>(batch) : batch.iterator(); + } + }; + } + + // alternative would be dispatch(HgChangelog.Inspector) and dispatchReverse() + // methods, but progress and cancellation might get messy then + } + // public static void main(String[] args) { // int[] r = new int[] {17, 19, 21, 23, 25, 29}; // System.out.println(Arrays.toString(narrowChangesetRange(r, 0, 45))); @@ -454,6 +538,21 @@ progressHelper.done(); } + /** + * DO NOT USE THIS METHOD, DEBUG PURPOSES ONLY!!! + */ + @Experimental(reason="Work in progress") + public HgLogCommand debugSwitch1() { + // FIXME can't expose iteration direction unless general iteration (changelog, not a file) supports it, too. + // however, need to test the code already there, hence this debug switch + if (iterateDirection == IterateDirection.FromOldToNew) { + iterateDirection = IterateDirection.FromNewToOld; + } else { + iterateDirection = IterateDirection.FromOldToNew; + } + return this; + } + private IterateDirection iterateDirection = IterateDirection.FromOldToNew; private static class ReverseIterator<E> implements Iterator<E> { @@ -820,16 +919,29 @@ // - private class FilteringInspector implements HgChangelog.Inspector, Lifecycle { + private class FilteringInspector extends AdapterPlug implements HgChangelog.Inspector, Adaptable { - private Callback lifecycle; private int firstCset = BAD_REVISION, lastCset = BAD_REVISION; + private HgChangelog.Inspector delegate; + // we use lifecycle to stop when limit is reached. + // delegate, however, may use lifecycle, too, so give it a chance + private LifecycleProxy lifecycleProxy; // limit to changesets in this range only public void changesets(int start, int end) { firstCset = start; lastCset = end; } + + public void delegateTo(HgChangelog.Inspector inspector) { + delegate = inspector; + // let delegate control life cycle, too + if (lifecycleProxy == null) { + super.attachAdapter(Lifecycle.class, lifecycleProxy = new LifecycleProxy(inspector)); + } else { + lifecycleProxy.init(inspector); + } + } public void next(int revisionNumber, Nodeid nodeid, RawChangeset cset) { if (limit > 0 && count >= limit) { @@ -862,22 +974,14 @@ if (date != null) { // TODO post-1.0 implement date support for log } - csetTransform.next(revisionNumber, nodeid, cset); - if (++count >= limit) { - if (lifecycle != null) { // FIXME support Lifecycle delegation - lifecycle.stop(); - } + delegate.next(revisionNumber, nodeid, cset); + count++; + if (limit > 0 && count >= limit) { + lifecycleProxy.stop(); } } + } - public void start(int count, Callback callback, Object token) { - lifecycle = callback; - } - - public void finish(Object token) { - } - } - private HgParentChildMap<HgChangelog> getParentHelper(boolean create) throws HgInvalidControlFileException { if (parentHelper == null && create) { parentHelper = new HgParentChildMap<HgChangelog>(repo.getChangelog()); @@ -885,8 +989,7 @@ } return parentHelper; } - - + public static class CollectHandler implements HgChangesetHandler { private final List<HgChangeset> result = new LinkedList<HgChangeset>();