# HG changeset patch # User Artem Tikhomirov # Date 1369233975 -7200 # Node ID 5f52074707b2a1402fc1827f20ab7f90ba88ce01 # Parent 6526d8adbc0fb11575bf87b6641e28a24ec4dca5 Diff/blame methods as command, their residence in HgDataFile was a mistake diff -r 6526d8adbc0f -r 5f52074707b2 src/org/tmatesoft/hg/core/HgAnnotateCommand.java --- a/src/org/tmatesoft/hg/core/HgAnnotateCommand.java Wed May 22 15:52:31 2013 +0200 +++ b/src/org/tmatesoft/hg/core/HgAnnotateCommand.java Wed May 22 16:46:15 2013 +0200 @@ -20,12 +20,12 @@ import java.util.Arrays; +import org.tmatesoft.hg.core.HgBlameInspector.BlockData; import org.tmatesoft.hg.internal.Callback; import org.tmatesoft.hg.internal.CsetParamKeeper; import org.tmatesoft.hg.internal.FileAnnotation; import org.tmatesoft.hg.internal.FileAnnotation.LineDescriptor; import org.tmatesoft.hg.internal.FileAnnotation.LineInspector; -import org.tmatesoft.hg.repo.HgBlameInspector.BlockData; import org.tmatesoft.hg.repo.HgDataFile; import org.tmatesoft.hg.repo.HgRepository; import org.tmatesoft.hg.repo.HgRuntimeException; @@ -116,7 +116,10 @@ final int changesetStart = followRename ? 0 : df.getChangesetRevisionIndex(0); Collector c = new Collector(cancellation); FileAnnotation fa = new FileAnnotation(c); - df.annotate(changesetStart, annotateRevision.get(), fa, HgIterateDirection.NewToOld); + HgDiffCommand cmd = new HgDiffCommand(repo); + cmd.file(df).order(HgIterateDirection.NewToOld); + cmd.range(changesetStart, annotateRevision.get()); + cmd.executeAnnotate(fa); progress.worked(1); c.throwIfCancelled(); cancellation.checkCancelled(); diff -r 6526d8adbc0f -r 5f52074707b2 src/org/tmatesoft/hg/core/HgBlameInspector.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/tmatesoft/hg/core/HgBlameInspector.java Wed May 22 16:46:15 2013 +0200 @@ -0,0 +1,189 @@ +/* + * Copyright (c) 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 + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For information on how to redistribute this software under + * the terms of a license other than GNU General Public License + * contact TMate Software at support@hg4j.com + */ +package org.tmatesoft.hg.core; + +import org.tmatesoft.hg.core.HgCallbackTargetException; +import org.tmatesoft.hg.internal.Callback; +import org.tmatesoft.hg.internal.Experimental; +import org.tmatesoft.hg.repo.HgDataFile; +import org.tmatesoft.hg.util.Adaptable; + +/** + * Client's sink for revision differences, diff/annotate functionality. + * + * When implemented, clients shall not expect new {@link Block blocks} instances in each call. + * + * In case more information about annotated revision is needed, inspector instances may supply + * {@link RevisionDescriptor.Recipient} through {@link Adaptable}. + * + * @author Artem Tikhomirov + * @author TMate Software Ltd. + * @since 1.1 + */ +@Callback +@Experimental(reason="Unstable API") +public interface HgBlameInspector { + + void same(EqualBlock block) throws HgCallbackTargetException; + void added(AddBlock block) throws HgCallbackTargetException; + void changed(ChangeBlock block) throws HgCallbackTargetException; + void deleted(DeleteBlock block) throws HgCallbackTargetException; + + /** + * Represents content of a block, either as a sequence of bytes or a + * sequence of smaller blocks (lines), if appropriate (according to usage context). + * + * This approach allows line-by-line access to content data along with complete byte sequence for the whole block, i.e. + *
+	 *    BlockData bd = addBlock.addedLines()
+	 *    // bd describes data from the addition completely.
+	 *    // elements of the BlockData are lines
+	 *    bd.elementCount() == addBlock.totalAddedLines();
+	 *    // one cat obtain complete addition with
+	 *    byte[] everythingAdded = bd.asArray();
+	 *    // or iterate line by line
+	 *    for (int i = 0; i < bd.elementCount(); i++) {
+	 *    	 byte[] lineContent = bd.elementAt(i);
+	 *       String line = new String(lineContent, fileEncodingCharset);
+	 *    }
+	 *    where bd.elementAt(0) is the line at index addBlock.firstAddedLine() 
+	 * 
+ * + * LineData or ChunkData? + */ + public interface BlockData { + BlockData elementAt(int index); + int elementCount(); + byte[] asArray(); + } + + /** + * {@link HgBlameInspector} may optionally request extra information about revisions + * being inspected, denoting itself as a {@link RevisionDescriptor.Recipient}. This class + * provides complete information about file revision under annotation now. + */ + public interface RevisionDescriptor { + /** + * @return complete source of the diff origin, never null + */ + BlockData origin(); + /** + * @return complete source of the diff target, never null + */ + BlockData target(); + /** + * @return changeset revision index of original file, or {@link HgRepository#NO_REVISION} if it's the very first revision + */ + int originChangesetIndex(); + /** + * @return changeset revision index of the target file + */ + int targetChangesetIndex(); + /** + * @return true if this revision is merge + */ + boolean isMerge(); + /** + * @return changeset revision index of the second, merged parent + */ + int mergeChangesetIndex(); + /** + * @return revision index of the change in target file's revlog + */ + int fileRevisionIndex(); + + /** + * @return file object under blame (target file) + */ + HgDataFile file(); + + /** + * Implement to indicate interest in {@link RevisionDescriptor}. + * + * Note, instance of {@link RevisionDescriptor} is the same for + * {@link #start(RevisionDescriptor)} and {@link #done(RevisionDescriptor)} + * methods, and not necessarily a new one (i.e. ==) for the next + * revision announced. + */ + @Callback + public interface Recipient { + /** + * Comes prior to any change {@link Block blocks} + */ + void start(RevisionDescriptor revisionDescription) throws HgCallbackTargetException; + /** + * Comes after all change {@link Block blocks} were dispatched + */ + void done(RevisionDescriptor revisionDescription) throws HgCallbackTargetException; + } + } + + /** + * Each change block comes from a single origin, blocks that are result of a merge + * have {@link #originChangesetIndex()} equal to {@link RevisionDescriptor#mergeChangesetIndex()}. + */ + public interface Block { + int originChangesetIndex(); + int targetChangesetIndex(); + } + + public interface EqualBlock extends Block { + int originStart(); + int targetStart(); + int length(); + BlockData content(); + } + + public interface AddBlock extends Block { + /** + * @return line index in the origin where this block is inserted + */ + int insertedAt(); + /** + * @return line index of the first added line in the target revision + */ + int firstAddedLine(); + /** + * @return number of added lines in this block + */ + int totalAddedLines(); + /** + * @return content of added lines + */ + BlockData addedLines(); + } + public interface DeleteBlock extends Block { + /** + * @return line index in the target revision were this deleted block would be + */ + int removedAt(); + /** + * @return line index of the first removed line in the original revision + */ + int firstRemovedLine(); + /** + * @return number of deleted lines in this block + */ + int totalRemovedLines(); + /** + * @return content of deleted lines + */ + BlockData removedLines(); + } + public interface ChangeBlock extends AddBlock, DeleteBlock { + } +} diff -r 6526d8adbc0f -r 5f52074707b2 src/org/tmatesoft/hg/core/HgDiffCommand.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/tmatesoft/hg/core/HgDiffCommand.java Wed May 22 16:46:15 2013 +0200 @@ -0,0 +1,171 @@ +/* + * Copyright (c) 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 + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For information on how to redistribute this software under + * the terms of a license other than GNU General Public License + * contact TMate Software at support@hg4j.com + */ +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.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; +import org.tmatesoft.hg.util.Path; + +/** + * 'hg diff' counterpart, with similar, diff-based, functionality + * + * @since 1.1 + * @author Artem Tikhomirov + * @author TMate Software Ltd. + */ +@Experimental(reason="#execute* methods might get renamed") +public class HgDiffCommand extends HgAbstractCommand { + + private final HgRepository repo; + private HgDataFile df; + private int clogRevIndexStart, clogRevIndexEnd; + private int clogRevIndexToParents; + private HgIterateDirection iterateDirection = HgIterateDirection.NewToOld; + + public HgDiffCommand(HgRepository hgRepo) { + repo = hgRepo; + } + + public HgDiffCommand file(Path file) { + df = repo.getFileNode(file); + return this; + } + + public HgDiffCommand file(HgDataFile file) { + df = file; + return this; + } + + public HgDiffCommand range(int changelogRevIndexStart, int changelogRevIndexEnd) { + clogRevIndexStart = changelogRevIndexStart; + clogRevIndexEnd = changelogRevIndexEnd; + return this; + } + + // FIXME javadoc when needed and difference with range + public HgDiffCommand changeset(int changelogRevIndex) { + clogRevIndexToParents = changelogRevIndex; + return this; + } + + // FIXME javadoc when needed + public HgDiffCommand order(HgIterateDirection order) { + iterateDirection = order; + return this; + } + + // FIXME progress and cancellation + + /** + * mimic 'hg diff -r clogRevIndex1 -r clogRevIndex2' + */ + public void executeDiff(HgBlameInspector insp) throws HgCallbackTargetException, CancelledException, HgException { + try { + int fileRevIndex1 = fileRevIndex(df, clogRevIndexStart); + int fileRevIndex2 = fileRevIndex(df, clogRevIndexEnd); + BlameHelper bh = new BlameHelper(insp); + bh.prepare(df, clogRevIndexStart, clogRevIndexEnd); + bh.diff(fileRevIndex1, clogRevIndexStart, fileRevIndex2, clogRevIndexEnd); + } catch (HgRuntimeException ex) { + throw new HgLibraryFailureException(ex); + } + } + + /** + * Walk file history range and report changes (diff) for each revision + */ + public void executeAnnotate(HgBlameInspector insp) throws HgCallbackTargetException, CancelledException, HgException { + if (wrongRevisionIndex(clogRevIndexStart) || wrongRevisionIndex(clogRevIndexEnd)) { + throw new IllegalArgumentException(); + } + // FIXME check file and range are set + 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); + + int[] fileClogParentRevs = new int[2]; + int[] fileParentRevs = new int[2]; + for (FileRevisionHistoryChunk fhc : fileHistory.iterate(iterateDirection)) { + 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; + fhc.fillFileParents(fri, fileParentRevs); + fhc.fillCsetParents(fri, fileClogParentRevs); + bh.annotateChange(fri, clogRevIndex, fileParentRevs, fileClogParentRevs); + } + } + } catch (HgRuntimeException ex) { + throw new HgLibraryFailureException(ex); + } + } + + /** + * 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 executeAnnotateSingleRevision(HgBlameInspector insp) throws HgCallbackTargetException, CancelledException, HgException { + try { + int changelogRevisionIndex = clogRevIndexToParents; + // 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]; + df.parents(fileRevIndex, fileRevParents, null, null); + if (changelogRevisionIndex == TIP) { + changelogRevisionIndex = df.getChangesetRevisionIndex(fileRevIndex); + } + int[] fileClogParentRevs = new int[2]; + fileClogParentRevs[0] = fileRevParents[0] == NO_REVISION ? NO_REVISION : df.getChangesetRevisionIndex(fileRevParents[0]); + fileClogParentRevs[1] = fileRevParents[1] == NO_REVISION ? NO_REVISION : df.getChangesetRevisionIndex(fileRevParents[1]); + BlameHelper bh = new BlameHelper(insp); + int clogIndexStart = fileClogParentRevs[0] == NO_REVISION ? (fileClogParentRevs[1] == NO_REVISION ? 0 : fileClogParentRevs[1]) : fileClogParentRevs[0]; + bh.prepare(df, clogIndexStart, changelogRevisionIndex); + bh.annotateChange(fileRevIndex, changelogRevisionIndex, fileRevParents, fileClogParentRevs); + } catch (HgRuntimeException ex) { + throw new HgLibraryFailureException(ex); + } + } + + + private static int fileRevIndex(HgDataFile df, int csetRevIndex) throws HgRuntimeException { + Nodeid fileRev = df.getRepo().getManifest().getFileRevision(csetRevIndex, df.getPath()); + return df.getRevisionIndex(fileRev); + } +} diff -r 6526d8adbc0f -r 5f52074707b2 src/org/tmatesoft/hg/internal/BlameHelper.java --- a/src/org/tmatesoft/hg/internal/BlameHelper.java Wed May 22 15:52:31 2013 +0200 +++ b/src/org/tmatesoft/hg/internal/BlameHelper.java Wed May 22 16:46:15 2013 +0200 @@ -25,13 +25,8 @@ import org.tmatesoft.hg.core.HgCallbackTargetException; import org.tmatesoft.hg.internal.DiffHelper.LineSequence; import org.tmatesoft.hg.internal.DiffHelper.LineSequence.ByteChain; -import org.tmatesoft.hg.repo.HgBlameInspector.Block; -import org.tmatesoft.hg.repo.HgBlameInspector.BlockData; -import org.tmatesoft.hg.repo.HgBlameInspector.ChangeBlock; -import org.tmatesoft.hg.repo.HgBlameInspector.EqualBlock; -import org.tmatesoft.hg.repo.HgBlameInspector.RevisionDescriptor; -import org.tmatesoft.hg.repo.HgBlameInspector.RevisionDescriptor.Recipient; -import org.tmatesoft.hg.repo.HgBlameInspector; +import org.tmatesoft.hg.core.HgBlameInspector; +import org.tmatesoft.hg.core.HgBlameInspector.*; import org.tmatesoft.hg.repo.HgDataFile; import org.tmatesoft.hg.repo.HgInvalidStateException; import org.tmatesoft.hg.repo.HgRuntimeException; @@ -253,7 +248,7 @@ ContentBlock targetContent = new ContentBlock(s2); annotatedRevision.set(originContent, targetContent); annotatedRevision.set(csetOrigin, csetTarget, p2MergeCommon != null ? csetMergeParent : NO_REVISION); - Recipient curious = Adaptable.Factory.getAdapter(insp, Recipient.class, null); + RevisionDescriptor.Recipient curious = Adaptable.Factory.getAdapter(insp, RevisionDescriptor.Recipient.class, null); if (curious != null) { try { curious.start(annotatedRevision); @@ -269,7 +264,7 @@ if (shallStop()) { return; } - Recipient curious = Adaptable.Factory.getAdapter(insp, Recipient.class, null); + RevisionDescriptor.Recipient curious = Adaptable.Factory.getAdapter(insp, RevisionDescriptor.Recipient.class, null); if (curious != null) { try { curious.done(annotatedRevision); diff -r 6526d8adbc0f -r 5f52074707b2 src/org/tmatesoft/hg/internal/FileAnnotation.java --- a/src/org/tmatesoft/hg/internal/FileAnnotation.java Wed May 22 15:52:31 2013 +0200 +++ b/src/org/tmatesoft/hg/internal/FileAnnotation.java Wed May 22 16:46:15 2013 +0200 @@ -19,8 +19,8 @@ import org.tmatesoft.hg.core.HgCallbackTargetException; import org.tmatesoft.hg.core.HgIterateDirection; -import org.tmatesoft.hg.repo.HgBlameInspector; -import org.tmatesoft.hg.repo.HgBlameInspector.RevisionDescriptor; +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; @@ -54,7 +54,7 @@ return; } FileAnnotation fa = new FileAnnotation(insp); - df.annotate(changelogRevisionIndex, fa, HgIterateDirection.NewToOld); + df.annotate(0, changelogRevisionIndex, fa, HgIterateDirection.NewToOld); } // keeps of equal blocks, origin to target, from some previous step diff -r 6526d8adbc0f -r 5f52074707b2 src/org/tmatesoft/hg/repo/HgBlameInspector.java --- a/src/org/tmatesoft/hg/repo/HgBlameInspector.java Wed May 22 15:52:31 2013 +0200 +++ b/src/org/tmatesoft/hg/repo/HgBlameInspector.java Wed May 22 16:46:15 2013 +0200 @@ -16,173 +16,9 @@ */ package org.tmatesoft.hg.repo; -import org.tmatesoft.hg.core.HgCallbackTargetException; -import org.tmatesoft.hg.internal.Callback; -import org.tmatesoft.hg.internal.Experimental; -import org.tmatesoft.hg.util.Adaptable; - /** - * Client's sink for revision differences, diff/annotate functionality. - * - * When implemented, clients shall not expect new {@link Block blocks} instances in each call. - * - * In case more information about annotated revision is needed, inspector instances may supply - * {@link RevisionDescriptor.Recipient} through {@link Adaptable}. - * - * @author Artem Tikhomirov - * @author TMate Software Ltd. - * @since 1.1 + * @deprecated use {@link org.tmatesoft.hg.core.HgBlameInspector} instead */ -@Callback -@Experimental(reason="Unstable API") -public interface HgBlameInspector { - - void same(EqualBlock block) throws HgCallbackTargetException; - void added(AddBlock block) throws HgCallbackTargetException; - void changed(ChangeBlock block) throws HgCallbackTargetException; - void deleted(DeleteBlock block) throws HgCallbackTargetException; - - /** - * Represents content of a block, either as a sequence of bytes or a - * sequence of smaller blocks (lines), if appropriate (according to usage context). - * - * This approach allows line-by-line access to content data along with complete byte sequence for the whole block, i.e. - *
-	 *    BlockData bd = addBlock.addedLines()
-	 *    // bd describes data from the addition completely.
-	 *    // elements of the BlockData are lines
-	 *    bd.elementCount() == addBlock.totalAddedLines();
-	 *    // one cat obtain complete addition with
-	 *    byte[] everythingAdded = bd.asArray();
-	 *    // or iterate line by line
-	 *    for (int i = 0; i < bd.elementCount(); i++) {
-	 *    	 byte[] lineContent = bd.elementAt(i);
-	 *       String line = new String(lineContent, fileEncodingCharset);
-	 *    }
-	 *    where bd.elementAt(0) is the line at index addBlock.firstAddedLine() 
-	 * 
- * - * LineData or ChunkData? - */ - public interface BlockData { - BlockData elementAt(int index); - int elementCount(); - byte[] asArray(); - } - - /** - * {@link HgBlameInspector} may optionally request extra information about revisions - * being inspected, denoting itself as a {@link RevisionDescriptor.Recipient}. This class - * provides complete information about file revision under annotation now. - */ - public interface RevisionDescriptor { - /** - * @return complete source of the diff origin, never null - */ - BlockData origin(); - /** - * @return complete source of the diff target, never null - */ - BlockData target(); - /** - * @return changeset revision index of original file, or {@link HgRepository#NO_REVISION} if it's the very first revision - */ - int originChangesetIndex(); - /** - * @return changeset revision index of the target file - */ - int targetChangesetIndex(); - /** - * @return true if this revision is merge - */ - boolean isMerge(); - /** - * @return changeset revision index of the second, merged parent - */ - int mergeChangesetIndex(); - /** - * @return revision index of the change in target file's revlog - */ - int fileRevisionIndex(); - - /** - * @return file object under blame (target file) - */ - HgDataFile file(); - - /** - * Implement to indicate interest in {@link RevisionDescriptor}. - * - * Note, instance of {@link RevisionDescriptor} is the same for - * {@link #start(RevisionDescriptor)} and {@link #done(RevisionDescriptor)} - * methods, and not necessarily a new one (i.e. ==) for the next - * revision announced. - */ - @Callback - public interface Recipient { - /** - * Comes prior to any change {@link Block blocks} - */ - void start(RevisionDescriptor revisionDescription) throws HgCallbackTargetException; - /** - * Comes after all change {@link Block blocks} were dispatched - */ - void done(RevisionDescriptor revisionDescription) throws HgCallbackTargetException; - } - } - - /** - * Each change block comes from a single origin, blocks that are result of a merge - * have {@link #originChangesetIndex()} equal to {@link RevisionDescriptor#mergeChangesetIndex()}. - */ - public interface Block { - int originChangesetIndex(); - int targetChangesetIndex(); - } - - public interface EqualBlock extends Block { - int originStart(); - int targetStart(); - int length(); - BlockData content(); - } - - public interface AddBlock extends Block { - /** - * @return line index in the origin where this block is inserted - */ - int insertedAt(); - /** - * @return line index of the first added line in the target revision - */ - int firstAddedLine(); - /** - * @return number of added lines in this block - */ - int totalAddedLines(); - /** - * @return content of added lines - */ - BlockData addedLines(); - } - public interface DeleteBlock extends Block { - /** - * @return line index in the target revision were this deleted block would be - */ - int removedAt(); - /** - * @return line index of the first removed line in the original revision - */ - int firstRemovedLine(); - /** - * @return number of deleted lines in this block - */ - int totalRemovedLines(); - /** - * @return content of deleted lines - */ - BlockData removedLines(); - } - public interface ChangeBlock extends AddBlock, DeleteBlock { - } +@Deprecated +public interface HgBlameInspector extends org.tmatesoft.hg.core.HgBlameInspector { } diff -r 6526d8adbc0f -r 5f52074707b2 src/org/tmatesoft/hg/repo/HgDataFile.java --- a/src/org/tmatesoft/hg/repo/HgDataFile.java Wed May 22 15:52:31 2013 +0200 +++ b/src/org/tmatesoft/hg/repo/HgDataFile.java Wed May 22 16:46:15 2013 +0200 @@ -27,14 +27,15 @@ import java.nio.channels.FileChannel; import java.util.Arrays; +import org.tmatesoft.hg.core.HgBlameInspector; import org.tmatesoft.hg.core.HgCallbackTargetException; import org.tmatesoft.hg.core.HgChangesetFileSneaker; +import org.tmatesoft.hg.core.HgDiffCommand; +import org.tmatesoft.hg.core.HgException; import org.tmatesoft.hg.core.HgIterateDirection; +import org.tmatesoft.hg.core.HgLibraryFailureException; 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.FileUtils; import org.tmatesoft.hg.internal.FilterByteChannel; import org.tmatesoft.hg.internal.FilterDataAccess; @@ -426,80 +427,53 @@ int changesetRevIndex = getChangesetRevisionIndex(fileRevisionIndex); return getRepo().getManifest().getFileFlags(changesetRevIndex, getPath()); } - + /** - * mimic 'hg diff -r clogRevIndex1 -r clogRevIndex2' + * @deprecated use {@link HgDiffCommand} instead */ + @Deprecated public void diff(int clogRevIndex1, int clogRevIndex2, HgBlameInspector insp) throws HgRuntimeException, HgCallbackTargetException { - int fileRevIndex1 = fileRevIndex(this, clogRevIndex1); - int fileRevIndex2 = fileRevIndex(this, clogRevIndex2); - BlameHelper bh = new BlameHelper(insp); - bh.prepare(this, clogRevIndex1, clogRevIndex2); - bh.diff(fileRevIndex1, clogRevIndex1, fileRevIndex2, clogRevIndex2); + try { + new HgDiffCommand(getRepo()).file(this).range(clogRevIndex1, clogRevIndex2).executeDiff(insp); + } catch (HgLibraryFailureException ex) { + throw ex.getCause(); + } catch (HgException ex) { + throw new HgInvalidStateException(ex.getMessage()); + } catch (CancelledException ex) { + throw new HgInvalidStateException("Cancellatin is not expected"); + } } /** - * 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 HgRuntimeException, HgCallbackTargetException { - annotate(0, changelogRevisionIndex, insp, iterateOrder); - } - - /** - * Walk file history range and report changes for each revision + * @deprecated use {@link HgDiffCommand} instead */ - public void annotate(int changelogRevIndexStart, int changelogRevIndexEnd, HgBlameInspector insp, HgIterateDirection iterateOrder) throws HgRuntimeException, 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; - } - BlameHelper bh = new BlameHelper(insp); - FileHistory fileHistory = bh.prepare(this, changelogRevIndexStart, changelogRevIndexEnd); - - 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); - } + @Deprecated + public void annotate(int clogRevIndex1, int clogRevIndex2, HgBlameInspector insp, HgIterateDirection iterateOrder) throws HgRuntimeException, HgCallbackTargetException { + try { + new HgDiffCommand(getRepo()).file(this).range(clogRevIndex1, clogRevIndex2).order(iterateOrder).executeAnnotate(insp); + } catch (HgLibraryFailureException ex) { + throw ex.getCause(); + } catch (HgException ex) { + throw new HgInvalidStateException(ex.getMessage()); + } catch (CancelledException ex) { + throw new HgInvalidStateException("Cancellatin is not expected"); } } - + /** - * 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). + * @deprecated use {@link HgDiffCommand} instead */ + @Deprecated public void annotateSingleRevision(int changelogRevisionIndex, HgBlameInspector insp) throws HgRuntimeException, 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); + try { + new HgDiffCommand(getRepo()).file(this).changeset(changelogRevisionIndex).executeAnnotateSingleRevision(insp); + } catch (HgLibraryFailureException ex) { + throw ex.getCause(); + } catch (HgException ex) { + throw new HgInvalidStateException(ex.getMessage()); + } catch (CancelledException ex) { + throw new HgInvalidStateException("Cancellatin is not expected"); } - 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]); - BlameHelper bh = new BlameHelper(insp); - int clogIndexStart = fileClogParentRevs[0] == NO_REVISION ? (fileClogParentRevs[1] == NO_REVISION ? 0 : fileClogParentRevs[1]) : fileClogParentRevs[0]; - bh.prepare(this, clogIndexStart, changelogRevisionIndex); - bh.annotateChange(fileRevIndex, changelogRevisionIndex, fileRevParents, fileClogParentRevs); } @Override @@ -519,13 +493,6 @@ RevlogStream.Inspector insp = new MetadataInspector(metadata, null); super.content.iterate(localRev, localRev, true, insp); } - - - private static int fileRevIndex(HgDataFile df, int csetRevIndex) throws HgRuntimeException { - 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; diff -r 6526d8adbc0f -r 5f52074707b2 test/org/tmatesoft/hg/test/TestBlame.java --- a/test/org/tmatesoft/hg/test/TestBlame.java Wed May 22 15:52:31 2013 +0200 +++ b/test/org/tmatesoft/hg/test/TestBlame.java Wed May 22 16:46:15 2013 +0200 @@ -51,7 +51,6 @@ import org.tmatesoft.hg.internal.FileAnnotation.LineInspector; import org.tmatesoft.hg.internal.IntVector; import org.tmatesoft.hg.repo.HgBlameInspector; -import org.tmatesoft.hg.repo.HgBlameInspector.BlockData; import org.tmatesoft.hg.repo.HgChangelog; import org.tmatesoft.hg.repo.HgDataFile; import org.tmatesoft.hg.repo.HgLookup; @@ -139,7 +138,7 @@ HgDataFile df = repo.getFileNode("file1"); ByteArrayOutputStream bos = new ByteArrayOutputStream(); DiffOutInspector dump = new DiffOutInspector(new PrintStream(bos)); - df.annotate(TIP, dump, HgIterateDirection.OldToNew); + df.annotate(0, TIP, dump, HgIterateDirection.OldToNew); LinkedList apiResult = new LinkedList(Arrays.asList(splitLines(bos.toString()))); /* @@ -336,7 +335,7 @@ HgDataFile df = repo.getFileNode("file.txt"); DiffOutInspector dump = new DiffOutInspector(System.out); dump.needRevisions(true); - df.annotate(8, dump, HgIterateDirection.NewToOld); + df.annotate(0, 8, dump, HgIterateDirection.NewToOld); // af.annotateSingleRevision(df, 113, dump); // System.out.println(); // af.annotate(df, TIP, new LineDumpInspector(true), HgIterateDirection.NewToOld); @@ -466,7 +465,7 @@ FileAnnotateInspector() { } - public void line(int lineNumber, int changesetRevIndex, BlockData lineContent, LineDescriptor ld) { + public void line(int lineNumber, int changesetRevIndex, HgBlameInspector.BlockData lineContent, LineDescriptor ld) { if (lineRevisions == null) { lineRevisions = new Integer[ld.totalLines()]; Arrays.fill(lineRevisions, NO_REVISION);