diff src/org/tmatesoft/hg/repo/HgDataFile.java @ 603:707b5c7c6fa4

Refactor HgBlameFacility: relevant action methods moved to proper home (HgDataFile), as facility doesn't provide anything but packaging of relevant methods/interfaces
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Mon, 06 May 2013 18:29:57 +0200
parents e3717fc7d26f
children e1b29756f901
line wrap: on
line diff
--- a/src/org/tmatesoft/hg/repo/HgDataFile.java	Mon May 06 17:11:29 2013 +0200
+++ b/src/org/tmatesoft/hg/repo/HgDataFile.java	Mon May 06 18:29:57 2013 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010-2012 TMate Software Ltd
+ * Copyright (c) 2010-2013 TMate Software Ltd
  *  
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -16,6 +16,7 @@
  */
 package org.tmatesoft.hg.repo;
 
+import static org.tmatesoft.hg.core.HgIterateDirection.OldToNew;
 import static org.tmatesoft.hg.repo.HgInternals.wrongRevisionIndex;
 import static org.tmatesoft.hg.repo.HgRepository.*;
 import static org.tmatesoft.hg.util.LogFacility.Severity.Info;
@@ -28,14 +29,20 @@
 import java.nio.channels.FileChannel;
 import java.util.Arrays;
 
+import org.tmatesoft.hg.core.HgCallbackTargetException;
 import org.tmatesoft.hg.core.HgChangesetFileSneaker;
+import org.tmatesoft.hg.core.HgIterateDirection;
 import org.tmatesoft.hg.core.Nodeid;
+import org.tmatesoft.hg.internal.BlameHelper;
 import org.tmatesoft.hg.internal.DataAccess;
+import org.tmatesoft.hg.internal.FileHistory;
+import org.tmatesoft.hg.internal.FileRevisionHistoryChunk;
 import org.tmatesoft.hg.internal.FilterByteChannel;
 import org.tmatesoft.hg.internal.FilterDataAccess;
 import org.tmatesoft.hg.internal.Internals;
 import org.tmatesoft.hg.internal.Metadata;
 import org.tmatesoft.hg.internal.RevlogStream;
+import org.tmatesoft.hg.repo.HgBlameInspector;
 import org.tmatesoft.hg.util.ByteChannel;
 import org.tmatesoft.hg.util.CancelSupport;
 import org.tmatesoft.hg.util.CancelledException;
@@ -428,6 +435,86 @@
 		return getRepo().getManifest().getFileFlags(changesetRevIndex, getPath());
 	}
 	
+	/**
+	 * mimic 'hg diff -r clogRevIndex1 -r clogRevIndex2'
+	 */
+	public void diff(int clogRevIndex1, int clogRevIndex2, HgBlameInspector insp) throws HgCallbackTargetException {
+		// FIXME clogRevIndex1 and clogRevIndex2 may point to different files, need to decide whether to throw an exception
+		// or to attempt to look up correct file node (tricky)
+		int fileRevIndex1 = fileRevIndex(this, clogRevIndex1);
+		int fileRevIndex2 = fileRevIndex(this, clogRevIndex2);
+		BlameHelper bh = new BlameHelper(insp, 5);
+		bh.useFileUpTo(this, clogRevIndex2);
+		bh.diff(fileRevIndex1, clogRevIndex1, fileRevIndex2, clogRevIndex2);
+	}
+	
+	/**
+	 * Walk file history up/down to revision at given changeset and report changes for each revision
+	 */
+	public void annotate(int changelogRevisionIndex, HgBlameInspector insp, HgIterateDirection iterateOrder) throws HgCallbackTargetException {
+		annotate(0, changelogRevisionIndex, insp, iterateOrder);
+	}
+
+	/**
+	 * Walk file history range and report changes for each revision
+	 */
+	public void annotate(int changelogRevIndexStart, int changelogRevIndexEnd, HgBlameInspector insp, HgIterateDirection iterateOrder) throws HgCallbackTargetException {
+		if (wrongRevisionIndex(changelogRevIndexStart) || wrongRevisionIndex(changelogRevIndexEnd)) {
+			throw new IllegalArgumentException();
+		}
+		// Note, changelogRevIndexEnd may be TIP, while the code below doesn't tolerate constants
+		//
+		int lastRevision = getRepo().getChangelog().getLastRevision();
+		if (changelogRevIndexEnd == TIP) {
+			changelogRevIndexEnd = lastRevision;
+		}
+		HgInternals.checkRevlogRange(changelogRevIndexStart, changelogRevIndexEnd, lastRevision);
+		if (!exists()) {
+			return;
+		}
+		FileHistory fileHistory = new FileHistory(this, changelogRevIndexStart, changelogRevIndexEnd);
+		fileHistory.build();
+		BlameHelper bh = new BlameHelper(insp, 10);
+		for (FileRevisionHistoryChunk fhc : fileHistory.iterate(OldToNew)) {
+			// iteration order is not important here
+			bh.useFileUpTo(fhc.getFile(), fhc.getEndChangeset());
+		}
+		int[] fileClogParentRevs = new int[2];
+		int[] fileParentRevs = new int[2];
+		for (FileRevisionHistoryChunk fhc : fileHistory.iterate(iterateOrder)) {
+			for (int fri : fhc.fileRevisions(iterateOrder)) {
+				int clogRevIndex = fhc.changeset(fri);
+				// the way we built fileHistory ensures we won't walk past [changelogRevIndexStart..changelogRevIndexEnd]
+				assert clogRevIndex >= changelogRevIndexStart;
+				assert clogRevIndex <= changelogRevIndexEnd;
+				fhc.fillFileParents(fri, fileParentRevs);
+				fhc.fillCsetParents(fri, fileClogParentRevs);
+				bh.annotateChange(fri, clogRevIndex, fileParentRevs, fileClogParentRevs);
+			}
+		}
+	}
+
+	/**
+	 * 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).
+	 */
+	public void annotateSingleRevision(int changelogRevisionIndex, HgBlameInspector insp) throws HgCallbackTargetException {
+		// TODO detect if file is text/binary (e.g. looking for chars < ' ' and not \t\r\n\f
+		int fileRevIndex = fileRevIndex(this, changelogRevisionIndex);
+		int[] fileRevParents = new int[2];
+		parents(fileRevIndex, fileRevParents, null, null);
+		if (changelogRevisionIndex == TIP) {
+			changelogRevisionIndex = getChangesetRevisionIndex(fileRevIndex);
+		}
+		BlameHelper bh = new BlameHelper(insp, 5);
+		bh.useFileUpTo(this, changelogRevisionIndex);
+		int[] fileClogParentRevs = new int[2];
+		fileClogParentRevs[0] = fileRevParents[0] == NO_REVISION ? NO_REVISION : getChangesetRevisionIndex(fileRevParents[0]);
+		fileClogParentRevs[1] = fileRevParents[1] == NO_REVISION ? NO_REVISION : getChangesetRevisionIndex(fileRevParents[1]);
+		bh.annotateChange(fileRevIndex, changelogRevisionIndex, fileRevParents, fileClogParentRevs);
+	}
+
 	@Override
 	public String toString() {
 		StringBuilder sb = new StringBuilder(getClass().getSimpleName());
@@ -445,6 +532,13 @@
 		RevlogStream.Inspector insp = new MetadataInspector(metadata, null);
 		super.content.iterate(localRev, localRev, true, insp);
 	}
+	
+
+	private static int fileRevIndex(HgDataFile df, int csetRevIndex) {
+		Nodeid fileRev = df.getRepo().getManifest().getFileRevision(csetRevIndex, df.getPath());
+		return df.getRevisionIndex(fileRev);
+	}
+
 
 	private static class MetadataInspector extends ErrorHandlingInspector implements RevlogStream.Inspector {
 		private final Metadata metadata;