changeset 568:8ed4f4f4f0a6

Blame facility refactored, get ready for follow/no-follow support
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Wed, 10 Apr 2013 15:45:53 +0200
parents 88f04c7cfedb
children c4fd1037bc6f
files src/org/tmatesoft/hg/internal/FileAnnotation.java src/org/tmatesoft/hg/repo/HgBlameFacility.java test/org/tmatesoft/hg/test/TestBlame.java test/org/tmatesoft/hg/test/TestDiffHelper.java
diffstat 4 files changed, 102 insertions(+), 62 deletions(-) [+]
line wrap: on
line diff
--- a/src/org/tmatesoft/hg/internal/FileAnnotation.java	Tue Apr 09 19:27:32 2013 +0200
+++ b/src/org/tmatesoft/hg/internal/FileAnnotation.java	Wed Apr 10 15:45:53 2013 +0200
@@ -58,8 +58,8 @@
 			return;
 		}
 		FileAnnotation fa = new FileAnnotation(insp);
-		HgBlameFacility af = new HgBlameFacility();
-		af.annotate(df, changelogRevisionIndex, fa, HgIterateDirection.NewToOld);
+		HgBlameFacility af = new HgBlameFacility(df);
+		af.annotate(changelogRevisionIndex, fa, HgIterateDirection.NewToOld);
 	}
 
 	// keeps <startSeq1, startSeq2, len> of equal blocks, origin to target, from some previous step
--- a/src/org/tmatesoft/hg/repo/HgBlameFacility.java	Tue Apr 09 19:27:32 2013 +0200
+++ b/src/org/tmatesoft/hg/repo/HgBlameFacility.java	Wed Apr 10 15:45:53 2013 +0200
@@ -32,6 +32,7 @@
 import org.tmatesoft.hg.internal.Experimental;
 import org.tmatesoft.hg.internal.IntMap;
 import org.tmatesoft.hg.internal.IntVector;
+import org.tmatesoft.hg.internal.Internals;
 import org.tmatesoft.hg.internal.DiffHelper.LineSequence;
 import org.tmatesoft.hg.internal.DiffHelper.LineSequence.ByteChain;
 import org.tmatesoft.hg.internal.RangeSeq;
@@ -48,11 +49,19 @@
  */
 @Experimental(reason="Unstable API")
 public final class HgBlameFacility {
+	private final HgDataFile df;
+	
+	public HgBlameFacility(HgDataFile file) {
+		if (file == null) {
+			throw new IllegalArgumentException();
+		}
+		df = file;
+	}
 	
 	/**
 	 * mimic 'hg diff -r clogRevIndex1 -r clogRevIndex2'
 	 */
-	public void diff(HgDataFile df, int clogRevIndex1, int clogRevIndex2, Inspector insp) throws HgCallbackTargetException {
+	public void diff(int clogRevIndex1, int clogRevIndex2, Inspector insp) throws HgCallbackTargetException {
 		int fileRevIndex1 = fileRevIndex(df, clogRevIndex1);
 		int fileRevIndex2 = fileRevIndex(df, clogRevIndex2);
 		FileLinesCache fileInfoCache = new FileLinesCache(df, 5);
@@ -66,59 +75,23 @@
 	}
 	
 	/**
-	 * Walk file history up to revision at given changeset and report changes for each revision
+	 * Walk file history up/down to revision at given changeset and report changes for each revision
 	 */
-	public void annotate(HgDataFile df, int changelogRevisionIndex, Inspector insp, HgIterateDirection iterateOrder) throws HgCallbackTargetException {
+	public void annotate(int changelogRevisionIndex, Inspector insp, HgIterateDirection iterateOrder) throws HgCallbackTargetException {
 		if (!df.exists()) {
 			return;
 		}
 		// Note, changelogRevisionIndex may be TIP, while #implAnnotateChange doesn't tolerate constants
 		//
-		// XXX df.indexWalk(0, fileRevIndex, ) might be more effective
-		int fileRevIndex = fileRevIndex(df, changelogRevisionIndex);
+		FileRevisionHistoryChunk fileHistory = new FileRevisionHistoryChunk(df);
+		fileHistory.init(changelogRevisionIndex);
+//		fileHistory.linkTo(null); FIXME
+
 		int[] fileRevParents = new int[2];
-		IntVector fileParentRevs = new IntVector((fileRevIndex+1) * 2, 0);
-		fileParentRevs.add(NO_REVISION, NO_REVISION);
-		for (int i = 1; i <= fileRevIndex; i++) {
-			df.parents(i, fileRevParents, null, null);
-			fileParentRevs.add(fileRevParents[0], fileRevParents[1]);
-		}
-		// collect file revisions to visit, from newest to oldest:
-		// traverse parents, starting from the given file revision
-		// this ignores all file revision made in parallel to the one of interest
-		IntVector fileRevsToVisit = new IntVector(fileRevIndex + 1, 0);
-		LinkedList<Integer> queue = new LinkedList<Integer>();
-		BitSet seen = new BitSet(fileRevIndex + 1);
-		queue.add(fileRevIndex);
-		do {
-			int x = queue.removeFirst();
-			if (seen.get(x)) {
-				continue;
-			}
-			seen.set(x);
-			fileRevsToVisit.add(x);
-			int p1 = fileParentRevs.get(2*x);
-			int p2 = fileParentRevs.get(2*x + 1);
-			if (p1 != NO_REVISION) {
-				queue.addLast(p1);
-			}
-			if (p2 != NO_REVISION) {
-				queue.addLast(p2);
-			}
-		} while (!queue.isEmpty());
 		FileLinesCache fileInfoCache = new FileLinesCache(df, 10);
-		// make sure no child is processed before we handled all (grand-)parents of the element
-		fileRevsToVisit.sort(false);
-		// fileRevsToVisit now { r10, r7, r6, r5, r0 }
-		// and we'll iterate it from behind, e.g. old to new unless reversed 
-		if (iterateOrder == HgIterateDirection.NewToOld) {
-			fileRevsToVisit.reverse();
-		}
-		for (int i = fileRevsToVisit.size() - 1; i >= 0; i--) {
-			int fri = fileRevsToVisit.get(i);
+		for (int fri : fileHistory.fileRevisions(iterateOrder)) {
 			int clogRevIndex = df.getChangesetRevisionIndex(fri);
-			fileRevParents[0] = fileParentRevs.get(fri * 2);
-			fileRevParents[1] = fileParentRevs.get(fri * 2 + 1);
+			fileHistory.getParents(fri, fileRevParents);
 			implAnnotateChange(fileInfoCache, clogRevIndex, fri, fileRevParents, insp);
 		}
 	}
@@ -128,7 +101,7 @@
 	 * Unlike {@link #annotate(HgDataFile, int, Inspector, HgIterateDirection)}, doesn't
 	 * walk file history, looks at the specified revision only. Handles both parents (if merge revision).
 	 */
-	public void annotateSingleRevision(HgDataFile df, int changelogRevisionIndex, Inspector insp) throws HgCallbackTargetException {
+	public void annotateSingleRevision(int changelogRevisionIndex, Inspector insp) throws HgCallbackTargetException {
 		// TODO detect if file is text/binary (e.g. looking for chars < ' ' and not \t\r\n\f
 		int fileRevIndex = fileRevIndex(df, changelogRevisionIndex);
 		int[] fileRevParents = new int[2];
@@ -183,6 +156,74 @@
 		Nodeid fileRev = df.getRepo().getManifest().getFileRevision(csetRevIndex, df.getPath());
 		return df.getRevisionIndex(fileRev);
 	}
+	
+	private static class FileRevisionHistoryChunk {
+		private final HgDataFile df;
+		private IntVector fileRevsToVisit;
+		private IntVector fileParentRevs;
+
+		public FileRevisionHistoryChunk(HgDataFile file) {
+			df = file;
+		}
+		
+		public void getParents(int fileRevIndex, int[] fileRevParents) {
+			fileRevParents[0] = fileParentRevs.get(fileRevIndex * 2);
+			fileRevParents[1] = fileParentRevs.get(fileRevIndex * 2 + 1);
+		}
+
+		public void init (int changelogRevisionIndex) {
+			// XXX df.indexWalk(0, fileRevIndex, ) might be more effective
+			int fileRevIndex = fileRevIndex(df, changelogRevisionIndex);
+			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++) {
+				df.parents(i, fileRevParents, null, null);
+				fileParentRevs.add(fileRevParents[0], fileRevParents[1]);
+			}
+			fileRevsToVisit = new IntVector(fileRevIndex + 1, 0);
+			LinkedList<Integer> queue = new LinkedList<Integer>();
+			BitSet seen = new BitSet(fileRevIndex + 1);
+			queue.add(fileRevIndex);
+			do {
+				int x = queue.removeFirst();
+				if (seen.get(x)) {
+					continue;
+				}
+				seen.set(x);
+				fileRevsToVisit.add(x);
+				int p1 = fileParentRevs.get(2*x);
+				int p2 = fileParentRevs.get(2*x + 1);
+				if (p1 != NO_REVISION) {
+					queue.addLast(p1);
+				}
+				if (p2 != NO_REVISION) {
+					queue.addLast(p2);
+				}
+			} while (!queue.isEmpty());
+			// make sure no child is processed before we handled all (grand-)parents of the element
+			fileRevsToVisit.sort(false);
+			// now fileRevsToVisit keep file change ancestry from new to old
+		}
+		
+		public void linkTo(FileRevisionHistoryChunk origin) {
+			Internals.notImplemented();
+		}
+		
+		public int[] fileRevisions(HgIterateDirection iterateOrder) {
+			// fileRevsToVisit is { r10, r7, r6, r5, r0 }, new to old
+			int[] rv = fileRevsToVisit.toArray();
+			if (iterateOrder == HgIterateDirection.OldToNew) {
+				// reverse return value
+				for (int a = 0, b = rv.length-1; a < b; a++, b--) {
+					int t = rv[b];
+					rv[b] = rv[a];
+					rv[a] = t;
+				}
+			}
+			return rv;
+		}
+	}
 
 	private static class FileLinesCache {
 		private final HgDataFile df;
--- a/test/org/tmatesoft/hg/test/TestBlame.java	Tue Apr 09 19:27:32 2013 +0200
+++ b/test/org/tmatesoft/hg/test/TestBlame.java	Wed Apr 10 15:45:53 2013 +0200
@@ -70,7 +70,7 @@
 		final int checkChangeset = 539;
 		HgDataFile df = repo.getFileNode(fname);
 		ByteArrayOutputStream bos = new ByteArrayOutputStream();
-		new HgBlameFacility().annotateSingleRevision(df, checkChangeset, new DiffOutInspector(new PrintStream(bos)));
+		new HgBlameFacility(df).annotateSingleRevision(checkChangeset, new DiffOutInspector(new PrintStream(bos)));
 		LineGrepOutputParser gp = new LineGrepOutputParser("^@@.+");
 		ExecHelper eh = new ExecHelper(gp, null);
 		eh.run("hg", "diff", "-c", String.valueOf(checkChangeset), "-U", "0", fname);
@@ -141,10 +141,10 @@
 	public void testComplexHistoryAnnotate() throws Exception {
 		HgRepository repo = Configuration.get().find("test-annotate");
 		HgDataFile df = repo.getFileNode("file1");
-		HgBlameFacility af = new HgBlameFacility();
+		HgBlameFacility af = new HgBlameFacility(df);
 		ByteArrayOutputStream bos = new ByteArrayOutputStream();
 		DiffOutInspector dump = new DiffOutInspector(new PrintStream(bos));
-		af.annotate(df, TIP, dump, HgIterateDirection.OldToNew);
+		af.annotate(TIP, dump, HgIterateDirection.OldToNew);
 		LinkedList<String> apiResult = new LinkedList<String>(Arrays.asList(splitLines(bos.toString())));
 		
 		/*
@@ -227,18 +227,18 @@
 		final String fname = "src/org/tmatesoft/hg/internal/PatchGenerator.java";
 		final int checkChangeset = 539;
 		HgDataFile df = repo.getFileNode(fname);
-		HgBlameFacility af = new HgBlameFacility();
+		HgBlameFacility af = new HgBlameFacility(df);
 		DiffOutInspector dump = new DiffOutInspector(System.out);
 		System.out.println("541 -> 543");
-		af.annotateSingleRevision(df, 543, dump);
+		af.annotateSingleRevision(543, dump);
 		System.out.println("539 -> 541");
-		af.annotateSingleRevision(df, 541, dump);
+		af.annotateSingleRevision(541, dump);
 		System.out.println("536 -> 539");
-		af.annotateSingleRevision(df, checkChangeset, dump);
+		af.annotateSingleRevision(checkChangeset, dump);
 		System.out.println("531 -> 536");
-		af.annotateSingleRevision(df, 536, dump);
+		af.annotateSingleRevision(536, dump);
 		System.out.println(" -1 -> 531");
-		af.annotateSingleRevision(df, 531, dump);
+		af.annotateSingleRevision(531, dump);
 		
 		FileAnnotateInspector fai = new FileAnnotateInspector();
 		FileAnnotation.annotate(df, 541, fai);
@@ -252,7 +252,7 @@
 		final String fname = "src/org/tmatesoft/hg/repo/HgManifest.java";
 		final int checkChangeset = 415;
 		HgDataFile df = repo.getFileNode(fname);
-		HgBlameFacility af = new HgBlameFacility();
+		HgBlameFacility af = new HgBlameFacility(df);
 		DiffOutInspector dump = new DiffOutInspector(System.out);
 //		System.out.println("413 -> 415");
 //		af.diff(df, 413, 415, dump);
@@ -262,16 +262,16 @@
 //		dump.needRevisions(true);
 //		af.annotateChange(df, checkChangeset, dump);
 		dump.needRevisions(true);
-		af.annotate(df, checkChangeset, dump, HgIterateDirection.OldToNew);
+		af.annotate(checkChangeset, dump, HgIterateDirection.OldToNew);
 	}
 	
 	private void ccc() throws Throwable {
 		HgRepository repo = new HgLookup().detect("/home/artem/hg/hgtest-annotate-merge/");
 		HgDataFile df = repo.getFileNode("file.txt");
-		HgBlameFacility af = new HgBlameFacility();
+		HgBlameFacility af = new HgBlameFacility(df);
 		DiffOutInspector dump = new DiffOutInspector(System.out);
 		dump.needRevisions(true);
-		af.annotate(df, 8, dump, HgIterateDirection.NewToOld);
+		af.annotate(8, dump, HgIterateDirection.NewToOld);
 //		af.annotateSingleRevision(df, 113, dump);
 //		System.out.println();
 //		af.annotate(df, TIP, new LineDumpInspector(true), HgIterateDirection.NewToOld);
--- a/test/org/tmatesoft/hg/test/TestDiffHelper.java	Tue Apr 09 19:27:32 2013 +0200
+++ b/test/org/tmatesoft/hg/test/TestDiffHelper.java	Wed Apr 10 15:45:53 2013 +0200
@@ -172,7 +172,6 @@
 		}
 		@Override
 		protected void added(int s1InsertPoint, int s2From, int s2To) {
-			// TODO Auto-generated method stub
 			added.add(s1InsertPoint, s2From, s2To - s2From);
 		}
 		@Override