changeset 630:72c979555cb8

HgDiffCommand. Do not use deprecated code. Javadoc
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Thu, 23 May 2013 19:44:28 +0200
parents 5f52074707b2
children 8a5cdcb27b8f
files src/org/tmatesoft/hg/core/HgDiffCommand.java src/org/tmatesoft/hg/core/HgRepoFacade.java src/org/tmatesoft/hg/internal/FileAnnotation.java test/org/tmatesoft/hg/test/TestBlame.java
diffstat 4 files changed, 118 insertions(+), 62 deletions(-) [+]
line wrap: on
line diff
--- a/src/org/tmatesoft/hg/core/HgDiffCommand.java	Wed May 22 16:46:15 2013 +0200
+++ b/src/org/tmatesoft/hg/core/HgDiffCommand.java	Thu May 23 19:44:28 2013 +0200
@@ -16,16 +16,15 @@
  */
 package org.tmatesoft.hg.core;
 
-import static org.tmatesoft.hg.repo.HgInternals.wrongRevisionIndex;
 import static org.tmatesoft.hg.repo.HgRepository.NO_REVISION;
 import static org.tmatesoft.hg.repo.HgRepository.TIP;
 
 import org.tmatesoft.hg.internal.BlameHelper;
+import org.tmatesoft.hg.internal.CsetParamKeeper;
 import org.tmatesoft.hg.internal.Experimental;
 import org.tmatesoft.hg.internal.FileHistory;
 import org.tmatesoft.hg.internal.FileRevisionHistoryChunk;
 import org.tmatesoft.hg.repo.HgDataFile;
-import org.tmatesoft.hg.repo.HgInternals;
 import org.tmatesoft.hg.repo.HgRepository;
 import org.tmatesoft.hg.repo.HgRuntimeException;
 import org.tmatesoft.hg.util.CancelledException;
@@ -43,12 +42,13 @@
 
 	private final HgRepository repo;
 	private HgDataFile df;
-	private int clogRevIndexStart, clogRevIndexEnd;
-	private int clogRevIndexToParents;
+	private final CsetParamKeeper clogRevIndexStart, clogRevIndexEnd;
 	private HgIterateDirection iterateDirection = HgIterateDirection.NewToOld;
 
 	public HgDiffCommand(HgRepository hgRepo) {
 		repo = hgRepo;
+		clogRevIndexStart = new CsetParamKeeper(hgRepo);
+		clogRevIndexEnd = new CsetParamKeeper(hgRepo);
 	}
 	
 	public HgDiffCommand file(Path file) {
@@ -56,24 +56,63 @@
 		return this;
 	}
 
+	/**
+	 * Selects the file which history to blame, mandatory.
+	 * 
+	 * @param file repository file
+	 * @return <code>this</code> for convenience
+	 */
 	public HgDiffCommand file(HgDataFile file) {
 		df = file;
 		return this;
 	}
 
-	public HgDiffCommand range(int changelogRevIndexStart, int changelogRevIndexEnd) {
-		clogRevIndexStart = changelogRevIndexStart;
-		clogRevIndexEnd = changelogRevIndexEnd;
+	/**
+	 * Select range of file's history for {@link #executeDiff(HgBlameInspector)}
+	 * and {@link #executeAnnotate(HgBlameInspector)}.
+	 * <p>
+	 * {@link #executeDiff(HgBlameInspector) diff} uses these as revisions to diff against each other, while 
+	 * {@link #executeAnnotate(HgBlameInspector) annotate} walks the range. 
+	 * 
+	 * @param changelogRevIndexStart index of changelog revision, left range boundary
+	 * @param changelogRevIndexEnd index of changelog revision, right range boundary
+	 * @return <code>this</code> for convenience
+	 * @throws HgBadArgumentException if failed to find any of supplied changeset 
+	 */
+	public HgDiffCommand range(int changelogRevIndexStart, int changelogRevIndexEnd) throws HgBadArgumentException {
+		clogRevIndexStart.set(changelogRevIndexStart);
+		clogRevIndexEnd.set(changelogRevIndexEnd);
 		return this;
 	}
 	
-	// FIXME javadoc when needed and difference with range
-	public HgDiffCommand changeset(int changelogRevIndex) {
-		clogRevIndexToParents = changelogRevIndex;
+	/**
+	 * Selects revision for {@link #executeAnnotateSingleRevision(HgBlameInspector)}, the one 
+	 * to diff against its parents. 
+	 * 
+	 * Besides, it is handy when range of interest spans up to the very beginning of the file history 
+	 * (and thus is equivalent to <code>range(0, changelogRevIndex)</code>)
+	 * 
+	 * @param changelogRevIndex index of changelog revision
+	 * @return <code>this</code> for convenience
+	 * @throws HgBadArgumentException if failed to find supplied changeset 
+	 */
+	public HgDiffCommand changeset(int changelogRevIndex) throws HgBadArgumentException {
+		clogRevIndexStart.set(0);
+		clogRevIndexEnd.set(changelogRevIndex);
 		return this;
 	}
 
-	// FIXME javadoc when needed
+	/**
+	 * Revision differences are reported in selected order when 
+	 * annotating {@link #range(int, int) range} of changesets with
+	 * {@link #executeAnnotate(HgBlameInspector)}.
+	 * <p>
+	 * This method doesn't affect {@link #executeAnnotateSingleRevision(HgBlameInspector)} and
+	 * {@link #executeDiff(HgBlameInspector)}
+	 * 
+	 * @param order desired iteration order 
+	 * @return <code>this</code> for convenience
+	 */
 	public HgDiffCommand order(HgIterateDirection order) {
 		iterateDirection = order;
 		return this;
@@ -82,41 +121,41 @@
 	// FIXME progress and cancellation
 	
 	/**
-	 * mimic 'hg diff -r clogRevIndex1 -r clogRevIndex2'
+	 * Diff two revisions selected with {@link #range(int, int)} against each other.
+	 * <p>mimics 'hg diff -r clogRevIndex1 -r clogRevIndex2'
+	 * 
+ 	 * @throws HgCallbackTargetException propagated exception from the handler
+	 * @throws CancelledException if execution of the command was cancelled
+	 * @throws HgException subclass thereof to indicate specific issue with the command arguments or repository state
 	 */
 	public void executeDiff(HgBlameInspector insp) throws HgCallbackTargetException, CancelledException, HgException {
+		checkFile();
 		try {
-			int fileRevIndex1 = fileRevIndex(df, clogRevIndexStart);
-			int fileRevIndex2 = fileRevIndex(df, clogRevIndexEnd);
+			int fileRevIndex1 = fileRevIndex(df, clogRevIndexStart.get());
+			int fileRevIndex2 = fileRevIndex(df, clogRevIndexEnd.get());
 			BlameHelper bh = new BlameHelper(insp);
-			bh.prepare(df, clogRevIndexStart, clogRevIndexEnd);
-			bh.diff(fileRevIndex1, clogRevIndexStart, fileRevIndex2, clogRevIndexEnd);
+			bh.prepare(df, clogRevIndexStart.get(), clogRevIndexEnd.get());
+			bh.diff(fileRevIndex1, clogRevIndexStart.get(), fileRevIndex2, clogRevIndexEnd.get());
 		} catch (HgRuntimeException ex) {
 			throw new HgLibraryFailureException(ex);
 		}
 	}
 
 	/**
-	 * Walk file history range and report changes (diff) for each revision
+	 * Walk file history {@link #range(int, int) range} and report changes (diff) for each revision
+	 * 
+ 	 * @throws HgCallbackTargetException propagated exception from the handler
+	 * @throws CancelledException if execution of the command was cancelled
+	 * @throws HgException subclass thereof to indicate specific issue with the command arguments or repository state
 	 */
 	public void executeAnnotate(HgBlameInspector insp) throws HgCallbackTargetException, CancelledException, HgException {
-		if (wrongRevisionIndex(clogRevIndexStart) || wrongRevisionIndex(clogRevIndexEnd)) {
-			throw new IllegalArgumentException();
-		}
-		// FIXME check file and range are set
+		checkFile();
 		try {
-			// Note, changelogRevIndexEnd may be TIP, while the code below doesn't tolerate constants
-			//
-			int lastRevision = repo.getChangelog().getLastRevision();
-			if (clogRevIndexEnd == TIP) {
-				clogRevIndexEnd = lastRevision;
-			}
-			HgInternals.checkRevlogRange(clogRevIndexStart, clogRevIndexEnd, lastRevision);
 			if (!df.exists()) {
 				return;
 			}
 			BlameHelper bh = new BlameHelper(insp);
-			FileHistory fileHistory = bh.prepare(df, clogRevIndexStart, clogRevIndexEnd);
+			FileHistory fileHistory = bh.prepare(df, clogRevIndexStart.get(), clogRevIndexEnd.get());
 	
 			int[] fileClogParentRevs = new int[2];
 			int[] fileParentRevs = new int[2];
@@ -124,8 +163,8 @@
 				for (int fri : fhc.fileRevisions(iterateDirection)) {
 					int clogRevIndex = fhc.changeset(fri);
 					// the way we built fileHistory ensures we won't walk past [changelogRevIndexStart..changelogRevIndexEnd]
-					assert clogRevIndex >= clogRevIndexStart;
-					assert clogRevIndex <= clogRevIndexEnd;
+					assert clogRevIndex >= clogRevIndexStart.get();
+					assert clogRevIndex <= clogRevIndexEnd.get();
 					fhc.fillFileParents(fri, fileParentRevs);
 					fhc.fillCsetParents(fri, fileClogParentRevs);
 					bh.annotateChange(fri, clogRevIndex, fileParentRevs, fileClogParentRevs);
@@ -140,10 +179,15 @@
 	 * Annotates changes of the file against its parent(s). 
 	 * Unlike {@link #annotate(HgDataFile, int, Inspector, HgIterateDirection)}, doesn't
 	 * walk file history, looks at the specified revision only. Handles both parents (if merge revision).
+	 * 
+ 	 * @throws HgCallbackTargetException propagated exception from the handler
+	 * @throws CancelledException if execution of the command was cancelled
+	 * @throws HgException subclass thereof to indicate specific issue with the command arguments or repository state
 	 */
 	public void executeAnnotateSingleRevision(HgBlameInspector insp) throws HgCallbackTargetException, CancelledException, HgException {
+		checkFile();
 		try {
-			int changelogRevisionIndex = clogRevIndexToParents;
+			int changelogRevisionIndex = clogRevIndexEnd.get();
 			// 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];
@@ -163,6 +207,11 @@
 		}
 	}
 
+	private void checkFile() {
+		if (df == null) {
+			throw new IllegalArgumentException("File is not set");
+		}
+	}
 
 	private static int fileRevIndex(HgDataFile df, int csetRevIndex) throws HgRuntimeException {
 		Nodeid fileRev = df.getRepo().getManifest().getFileRevision(csetRevIndex, df.getPath());
--- a/src/org/tmatesoft/hg/core/HgRepoFacade.java	Wed May 22 16:46:15 2013 +0200
+++ b/src/org/tmatesoft/hg/core/HgRepoFacade.java	Thu May 23 19:44:28 2013 +0200
@@ -153,4 +153,8 @@
 	public HgCommitCommand createCommitCommand() {
 		return new HgCommitCommand(repo);
 	}
+	
+	public HgDiffCommand createDiffCommand() {
+		return new HgDiffCommand(repo);
+	}
 }
--- a/src/org/tmatesoft/hg/internal/FileAnnotation.java	Wed May 22 16:46:15 2013 +0200
+++ b/src/org/tmatesoft/hg/internal/FileAnnotation.java	Thu May 23 19:44:28 2013 +0200
@@ -17,13 +17,9 @@
 package org.tmatesoft.hg.internal;
 
 
-import org.tmatesoft.hg.core.HgCallbackTargetException;
-import org.tmatesoft.hg.core.HgIterateDirection;
 import org.tmatesoft.hg.core.HgBlameInspector;
 import org.tmatesoft.hg.core.HgBlameInspector.RevisionDescriptor;
-import org.tmatesoft.hg.repo.HgDataFile;
 import org.tmatesoft.hg.repo.HgInvalidStateException;
-import org.tmatesoft.hg.repo.HgRuntimeException;
 
 /**
  * Produce output like 'hg annotate' does
@@ -46,17 +42,6 @@
 		int totalLines();
 	}
 
-	/**
-	 * Annotate file revision, line by line.
-	 */
-	public static void annotate(HgDataFile df, int changelogRevisionIndex, LineInspector insp) throws HgCallbackTargetException, HgRuntimeException {
-		if (!df.exists()) {
-			return;
-		}
-		FileAnnotation fa = new FileAnnotation(insp);
-		df.annotate(0, changelogRevisionIndex, fa, HgIterateDirection.NewToOld);
-	}
-
 	// keeps <startSeq1, startSeq2, len> of equal blocks, origin to target, from some previous step
 	private RangeSeq activeEquals;
 	// equal blocks of the current iteration, to be recalculated before next step
--- a/test/org/tmatesoft/hg/test/TestBlame.java	Wed May 22 16:46:15 2013 +0200
+++ b/test/org/tmatesoft/hg/test/TestBlame.java	Thu May 23 19:44:28 2013 +0200
@@ -42,15 +42,15 @@
 import org.junit.Test;
 import org.tmatesoft.hg.core.HgAnnotateCommand;
 import org.tmatesoft.hg.core.HgAnnotateCommand.LineInfo;
+import org.tmatesoft.hg.core.HgBlameInspector;
 import org.tmatesoft.hg.core.HgCallbackTargetException;
-import org.tmatesoft.hg.core.HgIterateDirection;
+import org.tmatesoft.hg.core.HgDiffCommand;
 import org.tmatesoft.hg.core.HgRepoFacade;
 import org.tmatesoft.hg.core.Nodeid;
 import org.tmatesoft.hg.internal.FileAnnotation;
 import org.tmatesoft.hg.internal.FileAnnotation.LineDescriptor;
 import org.tmatesoft.hg.internal.FileAnnotation.LineInspector;
 import org.tmatesoft.hg.internal.IntVector;
-import org.tmatesoft.hg.repo.HgBlameInspector;
 import org.tmatesoft.hg.repo.HgChangelog;
 import org.tmatesoft.hg.repo.HgDataFile;
 import org.tmatesoft.hg.repo.HgLookup;
@@ -75,7 +75,9 @@
 		final int checkChangeset = repo.getChangelog().getRevisionIndex(Nodeid.fromAscii("946b131962521f9199e1fedbdc2487d3aaef5e46")); // 539
 		HgDataFile df = repo.getFileNode(fname);
 		ByteArrayOutputStream bos = new ByteArrayOutputStream();
-		df.annotateSingleRevision(checkChangeset, new DiffOutInspector(new PrintStream(bos)));
+		HgDiffCommand diffCmd = new HgDiffCommand(repo);
+		diffCmd.file(df).changeset(checkChangeset);
+		diffCmd.executeAnnotateSingleRevision(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);
@@ -92,6 +94,8 @@
 		HgDataFile df = repo.getFileNode(fname);
 		AnnotateRunner ar = new AnnotateRunner(df.getPath(), null);
 
+		final HgDiffCommand diffCmd = new HgDiffCommand(repo);
+		diffCmd.file(df).order(NewToOld);
 		final HgChangelog clog = repo.getChangelog();
 		final int[] toTest = new int[] { 
 			clog.getRevisionIndex(Nodeid.fromAscii("946b131962521f9199e1fedbdc2487d3aaef5e46")), // 539
@@ -100,7 +104,8 @@
 		for (int cs : toTest) {
 			ar.run(cs, false);
 			FileAnnotateInspector fa = new FileAnnotateInspector();
-			FileAnnotation.annotate(df, cs, fa);
+			diffCmd.range(0, cs);
+			diffCmd.executeAnnotate(new FileAnnotation(fa));
 			doAnnotateLineCheck(cs, ar.getLines(), Arrays.asList(fa.lineRevisions), Arrays.asList(fa.lines));
 		}
 	}
@@ -111,10 +116,12 @@
 		HgDataFile df = repo.getFileNode("file1");
 		AnnotateRunner ar = new AnnotateRunner(df.getPath(), repo.getWorkingDir());
 
+		final HgDiffCommand diffCmd = new HgDiffCommand(repo).file(df).order(NewToOld);
 		for (int cs : new int[] { 4, 6 /*, 8 see below*/, TIP}) {
 			ar.run(cs, false);
 			FileAnnotateInspector fa = new FileAnnotateInspector();
-			FileAnnotation.annotate(df, cs, fa);
+			diffCmd.range(0, cs);
+			diffCmd.executeAnnotate(new FileAnnotation(fa));
 			doAnnotateLineCheck(cs, ar.getLines(), Arrays.asList(fa.lineRevisions), Arrays.asList(fa.lines));
 		}
 		/*`hg annotate -r 8` and HgBlameFacility give different result
@@ -138,7 +145,9 @@
 		HgDataFile df = repo.getFileNode("file1");
 		ByteArrayOutputStream bos = new ByteArrayOutputStream();
 		DiffOutInspector dump = new DiffOutInspector(new PrintStream(bos));
-		df.annotate(0, TIP, dump, HgIterateDirection.OldToNew);
+		HgDiffCommand diffCmd = new HgDiffCommand(repo);
+		diffCmd.file(df).range(0, TIP).order(OldToNew);
+		diffCmd.executeAnnotate(dump);
 		LinkedList<String> apiResult = new LinkedList<String>(Arrays.asList(splitLines(bos.toString())));
 		
 		/*
@@ -206,19 +215,22 @@
 		// earlier than rev2 shall be reported as new from change3
 		int[] change_2_8_new2old = new int[] {4, 6, 3, 4, -1, 3}; 
 		int[] change_2_8_old2new = new int[] {-1, 3, 3, 4, 4, 6 };
-		df.annotate(2, 8, insp, NewToOld);
+		final HgDiffCommand cmd = new HgDiffCommand(repo);
+		cmd.file(df);
+		cmd.range(2, 8).order(NewToOld);
+		cmd.executeAnnotate(insp);
 		Assert.assertArrayEquals(change_2_8_new2old, insp.getReportedRevisionPairs());
 		insp.reset();
-		df.annotate(2, 8, insp, OldToNew);
+		cmd.order(OldToNew).executeAnnotate(insp);
 		Assert.assertArrayEquals(change_2_8_old2new, insp.getReportedRevisionPairs());
 		// same as 2 to 8, with addition of rev9 changes rev7  (rev6 to rev7 didn't change content, only name)
 		int[] change_3_9_new2old = new int[] {7, 9, 4, 6, 3, 4, -1, 3 }; 
 		int[] change_3_9_old2new = new int[] {-1, 3, 3, 4, 4, 6, 7, 9 };
 		insp.reset();
-		df.annotate(3, 9, insp, NewToOld);
+		cmd.range(3, 9).order(NewToOld).executeAnnotate(insp);
 		Assert.assertArrayEquals(change_3_9_new2old, insp.getReportedRevisionPairs());
 		insp.reset();
-		df.annotate(3, 9, insp, OldToNew);
+		cmd.order(OldToNew).executeAnnotate(insp);
 		Assert.assertArrayEquals(change_3_9_old2new, insp.getReportedRevisionPairs());
 	}
 
@@ -269,14 +281,15 @@
 		LineGrepOutputParser gp = new LineGrepOutputParser("^@@.+");
 		ExecHelper eh = new ExecHelper(gp, repo.getWorkingDir());
 		int[] toTest = { 3, 4, 5 }; // p1 ancestry line, p2 ancestry line, not in ancestry line
+		final HgDiffCommand diffCmd = new HgDiffCommand(repo).file(df);
 		for (int cs : toTest) {
 			ByteArrayOutputStream bos = new ByteArrayOutputStream();
-			df.diff(cs, 8, new DiffOutInspector(new PrintStream(bos)));
+			diffCmd.range(cs, 8).executeDiff(new DiffOutInspector(new PrintStream(bos)));
 			eh.run("hg", "diff", "-r", String.valueOf(cs), "-r", "8", "-U", "0", df.getPath().toString());
 			//
 			String[] apiResult = splitLines(bos.toString());
 			String[] expected = splitLines(gp.result());
-			Assert.assertArrayEquals("diff -r " + cs + " - r 8", expected, apiResult);
+			Assert.assertArrayEquals("diff -r " + cs + "-r 8", expected, apiResult);
 			gp.reset();
 		}
 	}
@@ -293,7 +306,8 @@
 		eh.run("hg", "diff", "-c", "0", "-U", "0", df.getPath().toString());
 		//
 		ByteArrayOutputStream bos = new ByteArrayOutputStream();
-		df.annotateSingleRevision(0, new DiffOutInspector(new PrintStream(bos)));
+		HgDiffCommand diffCmd = new HgDiffCommand(repo).file(df);
+		diffCmd.changeset(0).executeAnnotateSingleRevision(new DiffOutInspector(new PrintStream(bos)));
 		//
 		String[] apiResult = splitLines(bos.toString());
 		String[] expected = splitLines(gp.result());
@@ -335,7 +349,10 @@
 		HgDataFile df = repo.getFileNode("file.txt");
 		DiffOutInspector dump = new DiffOutInspector(System.out);
 		dump.needRevisions(true);
-		df.annotate(0, 8, dump, HgIterateDirection.NewToOld);
+		HgDiffCommand diffCmd = new HgDiffCommand(repo);
+		diffCmd.file(df);
+		diffCmd.range(0, 8).order(NewToOld);
+		diffCmd.executeAnnotate(dump);
 //		af.annotateSingleRevision(df, 113, dump);
 //		System.out.println();
 //		af.annotate(df, TIP, new LineDumpInspector(true), HgIterateDirection.NewToOld);
@@ -351,7 +368,8 @@
 		errorCollector.verify();
 		*/
 		FileAnnotateInspector fa = new FileAnnotateInspector();
-		FileAnnotation.annotate(df, 8, fa);
+		diffCmd.range(0, 8).order(NewToOld);
+		diffCmd.executeAnnotate(new FileAnnotation(fa));
 		for (int i = 0; i < fa.lineRevisions.length; i++) {
 			System.out.printf("%d: %s", fa.lineRevisions[i], fa.line(i) == null ? "null\n" : fa.line(i));
 		}