diff src/org/tmatesoft/hg/internal/FileRevisionHistoryChunk.java @ 691:72fc7774b87e

Fix file.isCopy() for blame/annotate. Refactor status and blame to use newly introduced FileHistory helper that builds file rename history
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Fri, 02 Aug 2013 23:07:23 +0200
parents 6526d8adbc0f
children
line wrap: on
line diff
--- a/src/org/tmatesoft/hg/internal/FileRevisionHistoryChunk.java	Thu Aug 01 21:45:47 2013 +0200
+++ b/src/org/tmatesoft/hg/internal/FileRevisionHistoryChunk.java	Fri Aug 02 23:07:23 2013 +0200
@@ -25,7 +25,6 @@
 import java.util.LinkedList;
 
 import org.tmatesoft.hg.core.HgIterateDirection;
-import org.tmatesoft.hg.core.Nodeid;
 import org.tmatesoft.hg.repo.HgDataFile;
 import org.tmatesoft.hg.repo.HgRepository;
 import org.tmatesoft.hg.repo.HgRuntimeException;
@@ -41,16 +40,22 @@
 	private final HgDataFile df;
 	// change ancestry, sequence of file revisions
 	private IntVector fileRevsToVisit;
-	// parent pairs of complete file history
+	// parent pairs of complete file history, index offset by fileRevFrom
 	private IntVector fileParentRevs;
-	// map file revision to changelog revision (sparse array, only file revisions to visit are set)
+	// map file revision to changelog revision (sparse array, only file revisions to visit are set), index offset by fileRevFrom
 	private int[] file2changelog;
 	private int originChangelogRev = BAD_REVISION, originFileRev = BAD_REVISION;
-	private int csetRangeStart = NO_REVISION, csetRangeEnd = BAD_REVISION; 
+	private final int csetFrom, csetTo; 
+	private final int fileRevFrom, fileRevTo;
 	
 
-	public FileRevisionHistoryChunk(HgDataFile file) {
+	public FileRevisionHistoryChunk(HgDataFile file, int csetStart, int csetEnd, int fileStart, int fileEnd) {
+		assert fileEnd >= fileStart;
 		df = file;
+		csetFrom = csetStart;
+		csetTo = csetEnd;
+		fileRevFrom = fileStart;
+		fileRevTo = fileEnd;
 	}
 	
 	/**
@@ -64,52 +69,57 @@
 	 * @return changeset this file history chunk was chopped at, or {@link HgRepository#NO_REVISION} if none specified
 	 */
 	public int getStartChangeset() {
-		return csetRangeStart;
+		return csetFrom;
 	}
 	
 	/**
 	 * @return changeset this file history chunk ends at
 	 */
 	public int getEndChangeset() {
-		return csetRangeEnd;
+		return csetTo;
 	}
 	
-	public void init(int changelogRevisionIndex) throws HgRuntimeException {
-		csetRangeEnd = changelogRevisionIndex;
-		// XXX df.indexWalk(0, fileRevIndex, ) might be more effective
-		Nodeid fileRev = df.getRepo().getManifest().getFileRevision(changelogRevisionIndex, df.getPath());
-		int fileRevIndex = df.getRevisionIndex(fileRev);
+	public void init() throws HgRuntimeException {
 		int[] fileRevParents = new int[2];
-		fileParentRevs = new IntVector((fileRevIndex+1) * 2, 0);
-		fileParentRevs.add(NO_REVISION, NO_REVISION); // parents of fileRevIndex == 0
-		for (int i = 1; i <= fileRevIndex; i++) {
+		final int totalFileRevs = fileRevTo - fileRevFrom + 1;
+		fileParentRevs = new IntVector(totalFileRevs * 2, 0);
+		// pretend parents of fileRevStart are not set, regardless of actual state as we are not going to visit them anyway
+		fileParentRevs.add(NO_REVISION, NO_REVISION); 
+		// XXX df.indexWalk(fileRevStart, fileRevEnd, ) might be more effective
+		for (int i = fileRevFrom+1; i <= fileRevTo; i++) {
 			df.parents(i, fileRevParents, null, null);
 			fileParentRevs.add(fileRevParents[0], fileRevParents[1]);
 		}
 		// fileRevsToVisit keep file change ancestry from new to old
-		fileRevsToVisit = new IntVector(fileRevIndex + 1, 0);
+		fileRevsToVisit = new IntVector(totalFileRevs, 0);
 		// keep map of file revision to changelog revision
-		file2changelog = new int[fileRevIndex+1];
+		file2changelog = new int[totalFileRevs];
 		// only elements worth visit would get mapped, so there would be unfilled areas in the file2changelog,
 		// prevent from error (make it explicit) by bad value
 		Arrays.fill(file2changelog, BAD_REVISION);
 		LinkedList<Integer> queue = new LinkedList<Integer>();
-		BitSet seen = new BitSet(fileRevIndex + 1);
-		queue.add(fileRevIndex);
+		BitSet seen = new BitSet(totalFileRevs);
+		queue.add(fileRevTo);
 		do {
-			int x = queue.removeFirst();
-			if (seen.get(x)) {
+			int fileRev = queue.removeFirst();
+			int offFileRev = fileRev - fileRevFrom;
+			if (seen.get(offFileRev)) {
 				continue;
 			}
-			seen.set(x);
-			fileRevsToVisit.add(x);
-			file2changelog[x] = df.getChangesetRevisionIndex(x);
-			int p1 = fileParentRevs.get(2*x);
-			int p2 = fileParentRevs.get(2*x + 1);
-			if (p1 != NO_REVISION) {
+			seen.set(offFileRev);
+			int csetRev = df.getChangesetRevisionIndex(fileRev);
+			if (csetRev < csetFrom || csetRev > csetTo) {
+				continue;
+			}
+			fileRevsToVisit.add(fileRev);
+
+			file2changelog[offFileRev] = csetRev;
+			int p1 = fileParentRevs.get(2*offFileRev);
+			int p2 = fileParentRevs.get(2*offFileRev + 1);
+			if (p1 != NO_REVISION && p1 >= fileRevFrom) {
 				queue.addLast(p1);
 			}
-			if (p2 != NO_REVISION) {
+			if (p2 != NO_REVISION && p2 >= fileRevFrom) {
 				queue.addLast(p2);
 			}
 		} while (!queue.isEmpty());
@@ -117,37 +127,13 @@
 		fileRevsToVisit.sort(false);
 	}
 	
-	public void linkTo(FileRevisionHistoryChunk target) {
-		// assume that target.init() has been called already 
-		if (target == null) {
+	public void linkTo(FileRevisionHistoryChunk next) {
+		// assume that init() has been called already 
+		if (next == null) {
 			return;
 		}
-		target.originFileRev = fileRevsToVisit.get(0); // files to visit are new to old
-		target.originChangelogRev = changeset(target.originFileRev);
-	}
-
-	/**
-	 * Mark revision closest(ceil) to specified as the very first one (no parents) 
-	 */
-	public void chopAtChangeset(int firstChangelogRevOfInterest) {
-		csetRangeStart = firstChangelogRevOfInterest;
-		if (firstChangelogRevOfInterest == 0) {
-			return; // nothing to do
-		}
-		int i = 0, x = fileRevsToVisit.size(), fileRev = BAD_REVISION;
-		// fileRevsToVisit is new to old, greater numbers to smaller
-		while (i < x && changeset(fileRev = fileRevsToVisit.get(i)) >= firstChangelogRevOfInterest) {
-			i++;
-		}
-		assert fileRev != BAD_REVISION; // there's at least 1 revision in fileRevsToVisit
-		if (i == x && changeset(fileRev) != firstChangelogRevOfInterest) {
-			assert false : "Requested changeset shall belong to the chunk";
-			return;
-		}
-		fileRevsToVisit.trimTo(i); // no need to iterate more
-		// pretend fileRev got no parents
-		fileParentRevs.set(fileRev * 2, NO_REVISION);
-		fileParentRevs.set(fileRev, NO_REVISION);
+		next.originFileRev = fileRevsToVisit.get(0); // files to visit are new to old
+		next.originChangelogRev = changeset(next.originFileRev);
 	}
 
 	public int[] fileRevisions(HgIterateDirection iterateOrder) {
@@ -172,30 +158,32 @@
 	}
 	
 	public int changeset(int fileRevIndex) {
-		return file2changelog[fileRevIndex];
+		return file2changelog[fileRevIndex - fileRevFrom];
 	}
 	
 	public void fillFileParents(int fileRevIndex, int[] fileParents) {
-		if (fileRevIndex == 0 && originFileRev != BAD_REVISION) {
+		if (fileRevIndex == fileRevFrom && originFileRev != BAD_REVISION) {
 			// this chunk continues another file
 			assert originFileRev != NO_REVISION;
 			fileParents[0] = originFileRev;
 			fileParents[1] = NO_REVISION;
 			return;
 		}
-		fileParents[0] = fileParentRevs.get(fileRevIndex * 2);
-		fileParents[1] = fileParentRevs.get(fileRevIndex * 2 + 1);
+		int x = fileRevIndex - fileRevFrom;
+		fileParents[0] = fileParentRevs.get(x * 2);
+		fileParents[1] = fileParentRevs.get(x * 2 + 1);
 	}
 	
 	public void fillCsetParents(int fileRevIndex, int[] csetParents) {
-		if (fileRevIndex == 0 && originFileRev != BAD_REVISION) {
-			assert originFileRev != NO_REVISION;
+		if (fileRevIndex == fileRevFrom && originFileRev != BAD_REVISION) {
+			assert originChangelogRev != NO_REVISION;
 			csetParents[0] = originChangelogRev;
 			csetParents[1] = NO_REVISION; // I wonder if possible to start a copy with two parents?
 			return;
 		}
-		int fp1 = fileParentRevs.get(fileRevIndex * 2);
-		int fp2 = fileParentRevs.get(fileRevIndex * 2 + 1);
+		int x = fileRevIndex - fileRevFrom;
+		int fp1 = fileParentRevs.get(x * 2);
+		int fp2 = fileParentRevs.get(x * 2 + 1);
 		csetParents[0] = fp1 == NO_REVISION ? NO_REVISION : changeset(fp1);
 		csetParents[1] = fp2 == NO_REVISION ? NO_REVISION : changeset(fp2);
 	}