diff src/org/tmatesoft/hg/repo/HgBlameFacility.java @ 573:e49f9d9513fa

Partial blame when start/end revisions are in the middle of a single filename history
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Fri, 12 Apr 2013 19:50:21 +0200
parents 36853bb80a35
children 43cfa08ff3fd
line wrap: on
line diff
--- a/src/org/tmatesoft/hg/repo/HgBlameFacility.java	Fri Apr 12 18:30:55 2013 +0200
+++ b/src/org/tmatesoft/hg/repo/HgBlameFacility.java	Fri Apr 12 19:50:21 2013 +0200
@@ -79,7 +79,7 @@
 		if (wrongRevisionIndex(changelogRevIndexStart) || wrongRevisionIndex(changelogRevIndexEnd)) {
 			throw new IllegalArgumentException();
 		}
-		// Note, changelogRevisionIndex may be TIP, while the code below doesn't tolerate constants
+		// Note, changelogRevIndexEnd may be TIP, while the code below doesn't tolerate constants
 		//
 		int lastRevision = df.getRepo().getChangelog().getLastRevision();
 		if (changelogRevIndexEnd == TIP) {
@@ -101,8 +101,18 @@
 			fileCompleteHistory.addFirst(fileHistory); // to get the list in old-to-new order
 			nextChunk = fileHistory;
 			bh.useFileUpTo(currentFile, fileLastClogRevIndex);
-			if (currentFile.isCopy()) {
-				// TODO SessionContext.getPathFactory() and replace all Path.create
+			if (fileHistory.changeset(0) > changelogRevIndexStart && currentFile.isCopy()) {
+				// fileHistory.changeset(0) is the earliest revision we know about so far,
+				// once we get to revisions earlier than the requested start, stop digging.
+				// The reason there's NO == (i.e. not >=) because:
+				// (easy): once it's equal, we've reached our intended start
+				// (hard): if changelogRevIndexStart happens to be exact start of one of renames in the 
+				// chain of renames (test-annotate2 repository, file1->file1a->file1b, i.e. points 
+				// to the very start of file1a or file1 history), presence of == would get us to the next 
+				// chunk and hence changed parents of present chunk's first element. Our annotate alg 
+				// relies on parents only (i.e. knows nothing about 'last iteration element') to find out 
+				// what to compare, and hence won't report all lines of 'last iteration element' (which is the
+				// first revision of the renamed file) as "added in this revision", leaving gaps in annotate
 				HgRepository repo = currentFile.getRepo();
 				Nodeid originLastRev = currentFile.getCopySourceRevision();
 				currentFile = repo.getFileNode(currentFile.getCopySourceName());
@@ -110,9 +120,10 @@
 				// XXX perhaps, shall fail with meaningful exception if new file doesn't exist (.i/.d not found for whatever reason)
 				// or source revision is missing?
 			} else {
+				fileHistory.chopAtChangeset(changelogRevIndexStart);
 				currentFile = null; // stop iterating
 			}
-		} while (currentFile != null && fileLastClogRevIndex >= changelogRevIndexStart);
+		} while (currentFile != null && fileLastClogRevIndex > changelogRevIndexStart);
 		// fileCompleteHistory is in (origin, intermediate target, ultimate target) order
 
 		int[] fileClogParentRevs = new int[2];
@@ -406,7 +417,30 @@
 			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) {
+			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);
+		}
+
 		public int[] fileRevisions(HgIterateDirection iterateOrder) {
 			// fileRevsToVisit is { r10, r7, r6, r5, r0 }, new to old
 			int[] rv = fileRevsToVisit.toArray();