throw new IllegalArgumentException();
}
if (csetTransform != null) {
throw new ConcurrentModificationException();
}
final ProgressSupport progressHelper = getProgressSupport(handler);
try {
if (repo.getChangelog().getRevisionCount() == 0) {
return;
}
final int firstCset = startRev;
final int lastCset = endRev == TIP ? repo.getChangelog().getLastRevision() : endRev;
// XXX pretty much like HgInternals.checkRevlogRange
if (lastCset < 0 || lastCset > repo.getChangelog().getLastRevision()) {
throw new HgBadArgumentException(String.format("Bad value %d for end revision", lastCset), null);
}
if (firstCset < 0 || firstCset > lastCset) {
throw new HgBadArgumentException(String.format("Bad value %d for start revision for range [%1$d..%d]", firstCset, lastCset), null);
}
final int BATCH_SIZE = 100;
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(firstCset, lastCset);
if (file == null) {
progressHelper.start(lastCset - firstCset + 1);
if (iterateDirection == HgIterateDirection.OldToNew) {
filterInsp.delegateTo(csetTransform);
repo.getChangelog().range(firstCset, lastCset, filterInsp);
csetTransform.checkFailure();
} else {
assert iterateDirection == HgIterateDirection.NewToOld;
BatchRangeHelper brh = new BatchRangeHelper(firstCset, lastCset, BATCH_SIZE, true);
BatchChangesetInspector batchInspector = new BatchChangesetInspector(Math.min(lastCset-firstCset+1, BATCH_SIZE));
filterInsp.delegateTo(batchInspector);
// XXX this batching code is bit verbose, refactor
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);
FileRenameQueueBuilder frqBuilder = new FileRenameQueueBuilder();
List<QueueElement> fileRenames = frqBuilder.buildFileRenamesQueue(firstCset, lastCset);
progressHelper.start(fileRenames.size());
for (int nameIndex = 0, fileRenamesSize = fileRenames.size(); nameIndex < fileRenamesSize; nameIndex++) {
QueueElement curRename = fileRenames.get(nameIndex);
HgDataFile fileNode = curRename.file();
if (followAncestry) {
TreeBuildInspector treeBuilder = new TreeBuildInspector(followAncestry);
@SuppressWarnings("unused")
List<HistoryNode> fileAncestry = treeBuilder.go(curRename);
int[] commitRevisions = narrowChangesetRange(treeBuilder.getCommitRevisions(), firstCset, lastCset);
if (iterateDirection == HgIterateDirection.OldToNew) {
repo.getChangelog().range(filterInsp, commitRevisions);
csetTransform.checkFailure();
} else {
assert iterateDirection == HgIterateDirection.NewToOld;
// visit one by one in the opposite direction
for (int i = commitRevisions.length-1; i >= 0; i--) {
int csetWithFileChange = commitRevisions[i];
repo.getChangelog().range(csetWithFileChange, csetWithFileChange, filterInsp);
}
}
} else {
// report complete file history (XXX may narrow range with [startRev, endRev], but need to go from file rev to link rev)
int fileStartRev = curRename.fileFrom();
int fileEndRev = curRename.file().getLastRevision(); //curRename.fileTo();
if (iterateDirection == HgIterateDirection.OldToNew) {
fileNode.history(fileStartRev, fileEndRev, filterInsp);
csetTransform.checkFailure();
} else {
assert iterateDirection == HgIterateDirection.NewToOld;
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 (withCopyHandler != null && nameIndex + 1 < fileRenamesSize) {
QueueElement nextRename = fileRenames.get(nameIndex+1);
HgFileRevision src, dst;
// A -> B
if (iterateDirection == HgIterateDirection.OldToNew) {
// curRename: A, nextRename: B
src = curRename.last();
dst = nextRename.first(src);
} else {
assert iterateDirection == HgIterateDirection.NewToOld;
// curRename: B, nextRename: A
src = nextRename.last();
dst = curRename.first(src);
}
withCopyHandler.copy(src, dst);
}
progressHelper.worked(1);
} // for renames
frqBuilder.reportRenameIfNotInQueue(fileRenames, withCopyHandler);
} // file != null
} catch (HgRuntimeException ex) {
throw new HgLibraryFailureException(ex);
} finally {
csetTransform = null;
progressHelper.done();
}
}