# HG changeset patch # User Artem Tikhomirov # Date 1374166065 -7200 # Node ID 1c49c0cee5408077b03723d755a77d6d88a8ec74 # Parent 3219cfadda49438bd06aba7bc5818460243881ac Report line number at the first appearance, like 'hg annotate -l' does diff -r 3219cfadda49 -r 1c49c0cee540 cmdline/org/tmatesoft/hg/console/Annotate.java --- a/cmdline/org/tmatesoft/hg/console/Annotate.java Thu Jul 18 18:03:51 2013 +0200 +++ b/cmdline/org/tmatesoft/hg/console/Annotate.java Thu Jul 18 18:47:45 2013 +0200 @@ -60,7 +60,7 @@ public void next(LineInfo lineInfo) { if (lineNumbers) { - System.out.printf("%3d:%3d: %s", lineInfo.getChangesetIndex(), lineInfo.getLineNumber(), new String(lineInfo.getContent())); + System.out.printf("%3d:%3d: %s", lineInfo.getChangesetIndex(), lineInfo.getOriginLineNumber(), new String(lineInfo.getContent())); } else { System.out.printf("%3d: %s", lineInfo.getChangesetIndex(), new String(lineInfo.getContent())); } diff -r 3219cfadda49 -r 1c49c0cee540 src/org/tmatesoft/hg/core/HgAnnotateCommand.java --- a/src/org/tmatesoft/hg/core/HgAnnotateCommand.java Thu Jul 18 18:03:51 2013 +0200 +++ b/src/org/tmatesoft/hg/core/HgAnnotateCommand.java Thu Jul 18 18:47:45 2013 +0200 @@ -143,8 +143,23 @@ * Clients shall not implement this interface */ public interface LineInfo { + /** + * @return 1-based index of the line in the annotated revision + */ int getLineNumber(); + + /** + * @return 1-based line number at the first appearance, at changeset {@link #getChangesetIndex()} + */ + int getOriginLineNumber(); + /** + * @return changeset revision this line was introduced at + */ int getChangesetIndex(); + + /** + * @return line content + */ byte[] getContent(); } } diff -r 3219cfadda49 -r 1c49c0cee540 src/org/tmatesoft/hg/internal/ForwardAnnotateInspector.java --- a/src/org/tmatesoft/hg/internal/ForwardAnnotateInspector.java Thu Jul 18 18:03:51 2013 +0200 +++ b/src/org/tmatesoft/hg/internal/ForwardAnnotateInspector.java Thu Jul 18 18:47:45 2013 +0200 @@ -67,7 +67,7 @@ for (int i = 0, x = t.at(0); i < x; i++) { final int lineInRev = t.at(2) + i; final byte[] lc = revLines.get(lineInRev); - li.init(line++, t.at(1), lc); + li.init(line++, lineInRev+1, t.at(1), lc); insp.next(li); progress.worked(1); cancel.checkCancelled(); @@ -112,7 +112,7 @@ public void deleted(DeleteBlock block) throws HgCallbackTargetException { } - private void copyBlock(int originChangesetIndex, int originStart, int length) { + private void copyBlock(int originChangesetIndex, int blockStart, int length) { IntSliceSeq origin = all.get(originChangesetIndex); assert origin != null; // shall visit parents before came to this child int originPos = 0; @@ -120,17 +120,20 @@ for (IntTuple t : origin) { int originBlockLen = t.at(0); int originBlockEnd = originPos + originBlockLen; - if (originBlockEnd > originStart) { - int originBlockOverlap = Math.min(originBlockLen, originBlockEnd - originStart); + if (originBlockEnd > blockStart) { + // part of origin block from blockStart up to originBlockEnd, but not more + // than size of the block (when blockStart is out of block start, i.e. < originPos) + int originBlockOverlap = Math.min(originBlockLen, originBlockEnd - blockStart); assert originBlockOverlap > 0; - originBlockOverlap = Math.min(originBlockOverlap, targetBlockLen); + // eat as much as there's left in the block being copied + int originBlockConsumed = Math.min(originBlockOverlap, targetBlockLen); int originBlockLine = t.at(2); - if (originPos < originStart) { + if (originPos < blockStart) { originBlockLine += originBlockLen-originBlockOverlap; } // copy fragment of original block; - current.add(originBlockOverlap, t.at(1), originBlockLine); - targetBlockLen -= originBlockOverlap; + current.add(originBlockConsumed, t.at(1), originBlockLine); + targetBlockLen -= originBlockConsumed; if (targetBlockLen == 0) { break; } @@ -144,19 +147,17 @@ HgRepository repo = new HgLookup().detect("/home/artem/hg/junit-test-repos/test-annotate/"); HgDiffCommand cmd = new HgDiffCommand(repo); cmd.file(repo.getFileNode("file1")).order(HgIterateDirection.OldToNew); - cmd.range(0, 8); + final int cset = 8; + cmd.range(0, cset); final ForwardAnnotateInspector c2 = new ForwardAnnotateInspector(); cmd.executeAnnotate(c2); - for (IntTuple t : c2.all.get(8)) { - System.out.printf("Block %d lines from revision %d (starts with line %d in the origin)\n", t.at(0), t.at(1), t.at(2)); - } - for (IntTuple t : c2.all.get(8)) { + for (IntTuple t : c2.all.get(cset)) { System.out.printf("Block %d lines from revision %d (starts with line %d in the origin)\n", t.at(0), t.at(1), 1+t.at(2)); } - c2.report(8, new Inspector() { + c2.report(cset, new Inspector() { public void next(LineInfo lineInfo) throws HgCallbackTargetException { - System.out.printf("%3d:%3d: %s", lineInfo.getChangesetIndex(), lineInfo.getLineNumber(), new String(lineInfo.getContent())); + System.out.printf("%3d:%3d: %s", lineInfo.getChangesetIndex(), lineInfo.getOriginLineNumber(), new String(lineInfo.getContent())); } }, ProgressSupport.Factory.get(null), CancelSupport.Factory.get(null)); } diff -r 3219cfadda49 -r 1c49c0cee540 src/org/tmatesoft/hg/internal/IntTuple.java --- a/src/org/tmatesoft/hg/internal/IntTuple.java Thu Jul 18 18:03:51 2013 +0200 +++ b/src/org/tmatesoft/hg/internal/IntTuple.java Thu Jul 18 18:47:45 2013 +0200 @@ -53,4 +53,17 @@ throw new Error(ex); } } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append('('); + for (int i = 0; i < size; i++) { + sb.append(at(i)); + sb.append(", "); + } + sb.setLength(sb.length() - 2); + sb.append(')'); + return sb.toString(); + } } \ No newline at end of file diff -r 3219cfadda49 -r 1c49c0cee540 src/org/tmatesoft/hg/internal/LineImpl.java --- a/src/org/tmatesoft/hg/internal/LineImpl.java Thu Jul 18 18:03:51 2013 +0200 +++ b/src/org/tmatesoft/hg/internal/LineImpl.java Thu Jul 18 18:47:45 2013 +0200 @@ -24,11 +24,13 @@ */ final class LineImpl implements LineInfo { private int ln; + private int origLine; private int rev; private byte[] content; - void init(int line, int csetRev, byte[] cnt) { + void init(int line, int firstAppearance, int csetRev, byte[] cnt) { ln = line; + origLine = firstAppearance; rev = csetRev; content = cnt; } @@ -37,6 +39,11 @@ return ln; } + + public int getOriginLineNumber() { + return origLine; + } + public int getChangesetIndex() { return rev; } diff -r 3219cfadda49 -r 1c49c0cee540 src/org/tmatesoft/hg/internal/ReverseAnnotateInspector.java --- a/src/org/tmatesoft/hg/internal/ReverseAnnotateInspector.java Thu Jul 18 18:03:51 2013 +0200 +++ b/src/org/tmatesoft/hg/internal/ReverseAnnotateInspector.java Thu Jul 18 18:47:45 2013 +0200 @@ -59,6 +59,7 @@ private boolean activeEqualsComesFromMerge = false; private int[] lineRevisions; + private int[] lineNumbers; /** * @return desired order of iteration for diff @@ -72,7 +73,7 @@ progress.start(lineRevisions.length); for (int i = 0; i < lineRevisions.length; i++) { byte[] c = lineContent.elementAt(i).asArray(); - li.init(i+1, lineRevisions[i], c); + li.init(i+1, lineNumbers[i] + 1, lineRevisions[i], c); insp.next(li); progress.worked(1); cancel.checkCancelled(); @@ -85,8 +86,9 @@ if (knownLines == null) { lineContent = rd.target(); knownLines = new boolean[lineContent.elementCount()]; - lineRevisions = new int [lineContent.elementCount()]; + lineRevisions = new int [knownLines.length]; Arrays.fill(lineRevisions, NO_REVISION); + lineNumbers = new int[knownLines.length]; activeEquals = new RangePairSeq(); activeEquals.add(0, 0, knownLines.length); equalRanges.put(rd.targetChangesetIndex(), activeEquals); @@ -144,7 +146,7 @@ if (rs != null) { rs.add(block.insertedAt() + i, lnInFinal, 1); } else { - line(lnInFinal, block.targetChangesetIndex()); + line(lnInFinal, ln, block.targetChangesetIndex()); } knownLines[lnInFinal] = true; } @@ -158,7 +160,8 @@ public void deleted(DeleteBlock block) { } - private void line(int lineNumber, int changesetRevIndex) { + private void line(int lineNumber, int firstAppearance, int changesetRevIndex) { lineRevisions[lineNumber] = changesetRevIndex; + lineNumbers[lineNumber] = firstAppearance; } } \ No newline at end of file diff -r 3219cfadda49 -r 1c49c0cee540 test/org/tmatesoft/hg/test/TestBlame.java --- a/test/org/tmatesoft/hg/test/TestBlame.java Thu Jul 18 18:03:51 2013 +0200 +++ b/test/org/tmatesoft/hg/test/TestBlame.java Thu Jul 18 18:47:45 2013 +0200 @@ -31,7 +31,6 @@ import java.util.Arrays; import java.util.LinkedHashSet; import java.util.LinkedList; -import java.util.List; import java.util.ListIterator; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -108,7 +107,7 @@ final ReverseAnnotateInspector insp = new ReverseAnnotateInspector(); diffCmd.executeAnnotate(insp); AnnotateInspector fa = new AnnotateInspector().fill(cs, insp); - doAnnotateLineCheck(cs, ar.getLines(), fa.changesets, fa.lines); + doAnnotateLineCheck(cs, ar, fa); } } @@ -125,7 +124,7 @@ final ReverseAnnotateInspector insp = new ReverseAnnotateInspector(); diffCmd.executeAnnotate(insp); AnnotateInspector fa = new AnnotateInspector().fill(cs, insp); - doAnnotateLineCheck(cs, ar.getLines(), fa.changesets, fa.lines); + doAnnotateLineCheck(cs, ar, fa); } /*`hg annotate -r 8` and HgBlameFacility give different result * for "r0, line 5" line, which was deleted in rev2 and restored back in @@ -253,28 +252,30 @@ cmd.execute(ai); AnnotateRunner ar = new AnnotateRunner(fname, repo.getWorkingDir()); ar.run(changeset, true); - doAnnotateLineCheck(changeset, ar.getLines(), ai.changesets, ai.lines); + doAnnotateLineCheck(changeset, ar, ai); // no follow cmd.file(fname, false); ai = new AnnotateInspector(); cmd.execute(ai); ar.run(changeset, false); - doAnnotateLineCheck(changeset, ar.getLines(), ai.changesets, ai.lines); + doAnnotateLineCheck(changeset, ar, ai); } - // FIXME add originLineNumber to HgAnnotateCommand#LineInfo, pass it from FileAnnotate, test - - private void doAnnotateLineCheck(int cs, String[] hgAnnotateLines, List cmdChangesets, List cmdLines) { + private void doAnnotateLineCheck(int cs, AnnotateRunner ar, AnnotateInspector hg4jResult) { + String[] hgAnnotateLines = ar.getLines(); assertTrue("[sanity]", hgAnnotateLines.length > 0); - assertEquals("Number of lines reported by native annotate and our impl", hgAnnotateLines.length, cmdLines.size()); + assertEquals("Number of lines reported by native annotate and our impl", hgAnnotateLines.length, hg4jResult.getLineCount()); - for (int i = 0; i < cmdChangesets.size(); i++) { - int hgAnnotateRevIndex = Integer.parseInt(hgAnnotateLines[i].substring(0, hgAnnotateLines[i].indexOf(':')).trim()); - errorCollector.assertEquals(String.format("Revision mismatch for line %d (annotating rev: %d)", i+1, cs), hgAnnotateRevIndex, cmdChangesets.get(i)); - String hgAnnotateLine = hgAnnotateLines[i].substring(hgAnnotateLines[i].indexOf(':') + 1); - String apiLine = cmdLines.get(i).trim(); - errorCollector.assertEquals(hgAnnotateLine.trim(), apiLine); + for (int i = 0; i < hgAnnotateLines.length; i++) { + String[] hgLine = hgAnnotateLines[i].split(":"); + assertTrue(hgAnnotateLines[i], hgLine.length >= 3); + int hgAnnotateRevIndex = Integer.parseInt(hgLine[0].trim()); + int hgFirstAppLine = Integer.parseInt(hgLine[1].trim()); + String hgLineText = hgAnnotateLines[i].substring(hgLine[0].length() + hgLine[1].length() + 2).trim(); + errorCollector.assertEquals(String.format("Revision mismatch for line %d (annotating rev: %d)", i+1, cs), hgAnnotateRevIndex, hg4jResult.getChangeset(i)); + errorCollector.assertEquals(hgLineText, hg4jResult.getLine(i).trim()); + errorCollector.assertEquals(hgFirstAppLine, hg4jResult.getOriginLine(i)); } } @@ -525,10 +526,15 @@ } } + /** + * Note, this class expects lines coming in natural sequence (not the order they are detected - possible with {@link ReverseAnnotateInspector}) + * Once async lines are done, shall change implementation here + */ static class AnnotateInspector implements HgAnnotateCommand.Inspector { private int lineNumber = 1; - public final ArrayList lines = new ArrayList(); - public final ArrayList changesets = new ArrayList(); + private final ArrayList lines = new ArrayList(); + private final IntVector changesets = new IntVector(); + private final IntVector firstAppLines = new IntVector(); AnnotateInspector fill(int rev, ReverseAnnotateInspector ai) throws HgCallbackTargetException, CancelledException { ai.report(rev, this, ProgressSupport.Factory.get(null), CancelSupport.Factory.get(null)); @@ -544,6 +550,20 @@ lineNumber++; lines.add(new String(lineInfo.getContent())); changesets.add(lineInfo.getChangesetIndex()); + firstAppLines.add(lineInfo.getOriginLineNumber()); + } + + int getLineCount() { + return changesets.size(); + } + int getChangeset(int line) { + return changesets.get(line); + } + String getLine(int line) { + return lines.get(line); + } + int getOriginLine(int line) { + return firstAppLines.get(line); } } @@ -563,6 +583,7 @@ ArrayList args = new ArrayList(); args.add("hg"); args.add("annotate"); + args.add("--line-number"); args.add("-r"); args.add(cset == TIP ? "tip" : String.valueOf(cset)); if (!follow) {