checkResult = null;
renamed = false;
if (cset == null || file == null || file.isDirectory()) {
throw new IllegalArgumentException();
}
HgDataFile dataFile = repo.getFileNode(file);
if (!dataFile.exists()) {
checkResult = new Outcome(Outcome.Kind.Success, String.format("File named %s is not known in the repository", file));
return checkResult;
}
Nodeid toExtract = null;
String phaseMsg = "Extract manifest revision failed";
try {
if (cachedManifest == null) {
int csetRev = repo.getChangelog().getRevisionIndex(cset);
cachedManifest = new ManifestRevision(null, null); // XXX how about context and cached manifest revisions
repo.getManifest().walk(csetRev, csetRev, cachedManifest);
// cachedManifest shall be meaningful - changelog.getRevisionIndex() above ensures we've got version that exists.
}
toExtract = cachedManifest.nodeid(file);
phaseMsg = "Follow copy/rename failed";
if (toExtract == null && followRenames) {
int csetIndex = repo.getChangelog().getRevisionIndex(cset);
int ccFileRevIndex = dataFile.getLastRevision(); // copy candidate
int csetFileEnds = dataFile.getChangesetRevisionIndex(ccFileRevIndex);
if (csetIndex > csetFileEnds) {
return new Outcome(Outcome.Kind.Success, String.format("%s: last known changeset for the file %s is %d. Follow renames is possible towards older changesets only", phaseMsg, file, csetFileEnds));
}
// @see FileRenameHistory, with similar code, which doesn't trace alternative paths
// traceback stack keeps record of all files with isCopy(fileRev) == true we've tried to follow, so that we can try earlier file
// revisions in case followed fileRev didn't succeed
ArrayDeque<Pair<HgDataFile, Integer>> traceback = new ArrayDeque<Pair<HgDataFile, Integer>>();
do {
int ccCsetIndex = dataFile.getChangesetRevisionIndex(ccFileRevIndex);
if (ccCsetIndex <= csetIndex) {
// present dataFile is our (distant) origin
toExtract = dataFile.getRevision(ccFileRevIndex);
renamed = true;
break;
}
if (!dataFile.isCopy(ccFileRevIndex)) {
// nothing left to return to when traceback.isEmpty()
while (ccFileRevIndex == 0 && !traceback.isEmpty()) {
Pair<HgDataFile, Integer> lastTurnPoint = traceback.pop();
dataFile = lastTurnPoint.first();
ccFileRevIndex = lastTurnPoint.second(); // generally ccFileRevIndex != 0 here, but doesn't hurt to check, hence while
// fall through to shift down from the file revision we've already looked at
}
ccFileRevIndex--;
continue;
}
if (ccFileRevIndex > 0) {
// there's no reason to memorize turn point if it's the very first revision
// of the file and we won't be able to try any other earlier revision
traceback.push(new Pair<HgDataFile, Integer>(dataFile, ccFileRevIndex));
}
HgFileRevision origin = dataFile.getCopySource(ccFileRevIndex);
dataFile = repo.getFileNode(origin.getPath());
ccFileRevIndex = dataFile.getRevisionIndex(origin.getRevision());
} while (ccFileRevIndex >= 0);
// didn't get to csetIndex, no ancestor in file rename history found.
}
} catch (HgRuntimeException ex) {
checkResult = new Outcome(Outcome.Kind.Failure, phaseMsg, ex);
return checkResult;
}
if (toExtract != null) {
Flags extractRevFlags = cachedManifest.flags(dataFile.getPath());
fileRevision = new HgFileRevision(repo, toExtract, extractRevFlags, dataFile.getPath());
checkResult = new Outcome(Outcome.Kind.Success, String.format("File %s, revision %s found at changeset %s", dataFile.getPath(), toExtract.shortNotation(), cset.shortNotation()));
return checkResult;
}
checkResult = new Outcome(Outcome.Kind.Success, String.format("File %s nor its origins were known at revision %s", file, cset.shortNotation()));
return checkResult;
}