diff src/org/tmatesoft/hg/core/LogCommand.java @ 80:4222b04f34ee

Follow history of a file
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Tue, 25 Jan 2011 03:54:32 +0100
parents c677e1593919
children 61eedab3eb3e
line wrap: on
line diff
--- a/src/org/tmatesoft/hg/core/LogCommand.java	Tue Jan 25 02:26:06 2011 +0100
+++ b/src/org/tmatesoft/hg/core/LogCommand.java	Tue Jan 25 03:54:32 2011 +0100
@@ -27,6 +27,7 @@
 import java.util.TreeSet;
 
 import org.tmatesoft.hg.repo.Changeset;
+import org.tmatesoft.hg.repo.HgDataFile;
 import org.tmatesoft.hg.repo.HgRepository;
 import org.tmatesoft.hg.repo.StatusCollector;
 import org.tmatesoft.hg.util.PathPool;
@@ -51,8 +52,9 @@
 	private Handler delegate;
 	private Calendar date;
 	private Path file;
+	private boolean followHistory; // makes sense only when file != null
 	private Cset changeset;
-
+	
 	public LogCommand(HgRepository hgRepo) {
 		this.repo = hgRepo;
 	}
@@ -133,11 +135,12 @@
 	/**
 	 * Visit history of a given file only.
 	 * @param file path relative to repository root. Pass <code>null</code> to reset.
+	 * @param followCopyRename true to report changesets of the original file(-s), if copy/rename ever occured to the file. 
 	 */
-	public LogCommand file(Path file) {
+	public LogCommand file(Path file, boolean followCopyRename) {
 		// multiple? Bad idea, would need to include extra method into Handler to tell start of next file
-		// implicit --follow in this case
 		this.file = file;
+		followHistory = followCopyRename;
 		return this;
 	}
 
@@ -170,7 +173,24 @@
 			if (file == null) {
 				repo.getChangelog().range(startRev, endRev, this);
 			} else {
-				repo.getFileNode(file).history(startRev, endRev, this);
+				HgDataFile fileNode = repo.getFileNode(file);
+				fileNode.history(startRev, endRev, this);
+				if (handler instanceof FileHistoryHandler && fileNode.isCopy()) {
+					// even if we do not follow history, report file rename
+					do {
+						FileRevision src = new FileRevision(repo, fileNode.getCopySourceRevision(), fileNode.getCopySourceName());
+						FileRevision dst = new FileRevision(repo, fileNode.getRevisionNumber(0), fileNode.getPath());
+						((FileHistoryHandler) handler).copy(src, dst);
+						if (limit > 0 && count >= limit) {
+							// if limit reach, follow is useless.
+							break;
+						}
+						if (followHistory) {
+							fileNode = repo.getFileNode(src.getPath());
+							fileNode.history(this);
+						}
+					} while (followHistory && fileNode.isCopy());
+				}
 			}
 		} finally {
 			delegate = null;
@@ -215,6 +235,23 @@
 		void next(Cset changeset);
 	}
 	
+	/**
+	 * When {@link LogCommand} is executed against file, handler passed to {@link LogCommand#execute(Handler)} may optionally
+	 * implement this interface to get information about file renames. Method {@link #copy(FileRevision, FileRevision)} would
+	 * get invoked prior any changeset of the original file (if file history being followed) is reported via {@link #next(Cset)}.
+	 * 
+	 * For {@link LogCommand#file(Path, boolean)} with renamed file path and follow argument set to false, 
+	 * {@link #copy(FileRevision, FileRevision)} would be invoked for the first copy/rename in the history of the file, but not 
+	 * followed by any changesets. 
+	 *
+	 * @author Artem Tikhomirov
+	 * @author TMate Software Ltd.
+	 */
+	public interface FileHistoryHandler extends Handler {
+		// XXX perhaps, should distinguish copy from rename? And what about merged revisions and following them?
+		void copy(FileRevision from, FileRevision to);
+	}
+	
 	public static class CollectHandler implements Handler {
 		private final List<Cset> result = new LinkedList<Cset>();
 
@@ -232,7 +269,7 @@
 		private final Nodeid revision;
 		private final Path path;
 		
-		public FileRevision(HgRepository hgRepo, Nodeid rev, Path p) {
+		/*package-local*/FileRevision(HgRepository hgRepo, Nodeid rev, Path p) {
 			if (hgRepo == null || rev == null || p == null) {
 				throw new IllegalArgumentException();
 			}
@@ -249,7 +286,7 @@
 		}
 		public byte[] getContent() {
 			// XXX Content wrapper, to allow formats other than byte[], e.g. Stream, DataAccess, etc?
-			return repo.getFileNode(path).content();
+			return repo.getFileNode(path).content(revision);
 		}
 	}
 }