# HG changeset patch # User Artem Tikhomirov # Date 1365601553 -7200 # Node ID 8ed4f4f4f0a6c5f7f94e3ffe732de2f4e3acaea7 # Parent 88f04c7cfedb02bbe7cecab90727b5031066e691 Blame facility refactored, get ready for follow/no-follow support diff -r 88f04c7cfedb -r 8ed4f4f4f0a6 src/org/tmatesoft/hg/internal/FileAnnotation.java --- a/src/org/tmatesoft/hg/internal/FileAnnotation.java Tue Apr 09 19:27:32 2013 +0200 +++ b/src/org/tmatesoft/hg/internal/FileAnnotation.java Wed Apr 10 15:45:53 2013 +0200 @@ -58,8 +58,8 @@ return; } FileAnnotation fa = new FileAnnotation(insp); - HgBlameFacility af = new HgBlameFacility(); - af.annotate(df, changelogRevisionIndex, fa, HgIterateDirection.NewToOld); + HgBlameFacility af = new HgBlameFacility(df); + af.annotate(changelogRevisionIndex, fa, HgIterateDirection.NewToOld); } // keeps of equal blocks, origin to target, from some previous step diff -r 88f04c7cfedb -r 8ed4f4f4f0a6 src/org/tmatesoft/hg/repo/HgBlameFacility.java --- a/src/org/tmatesoft/hg/repo/HgBlameFacility.java Tue Apr 09 19:27:32 2013 +0200 +++ b/src/org/tmatesoft/hg/repo/HgBlameFacility.java Wed Apr 10 15:45:53 2013 +0200 @@ -32,6 +32,7 @@ import org.tmatesoft.hg.internal.Experimental; import org.tmatesoft.hg.internal.IntMap; import org.tmatesoft.hg.internal.IntVector; +import org.tmatesoft.hg.internal.Internals; import org.tmatesoft.hg.internal.DiffHelper.LineSequence; import org.tmatesoft.hg.internal.DiffHelper.LineSequence.ByteChain; import org.tmatesoft.hg.internal.RangeSeq; @@ -48,11 +49,19 @@ */ @Experimental(reason="Unstable API") public final class HgBlameFacility { + private final HgDataFile df; + + public HgBlameFacility(HgDataFile file) { + if (file == null) { + throw new IllegalArgumentException(); + } + df = file; + } /** * mimic 'hg diff -r clogRevIndex1 -r clogRevIndex2' */ - public void diff(HgDataFile df, int clogRevIndex1, int clogRevIndex2, Inspector insp) throws HgCallbackTargetException { + public void diff(int clogRevIndex1, int clogRevIndex2, Inspector insp) throws HgCallbackTargetException { int fileRevIndex1 = fileRevIndex(df, clogRevIndex1); int fileRevIndex2 = fileRevIndex(df, clogRevIndex2); FileLinesCache fileInfoCache = new FileLinesCache(df, 5); @@ -66,59 +75,23 @@ } /** - * Walk file history up to revision at given changeset and report changes for each revision + * Walk file history up/down to revision at given changeset and report changes for each revision */ - public void annotate(HgDataFile df, int changelogRevisionIndex, Inspector insp, HgIterateDirection iterateOrder) throws HgCallbackTargetException { + public void annotate(int changelogRevisionIndex, Inspector insp, HgIterateDirection iterateOrder) throws HgCallbackTargetException { if (!df.exists()) { return; } // Note, changelogRevisionIndex may be TIP, while #implAnnotateChange doesn't tolerate constants // - // XXX df.indexWalk(0, fileRevIndex, ) might be more effective - int fileRevIndex = fileRevIndex(df, changelogRevisionIndex); + FileRevisionHistoryChunk fileHistory = new FileRevisionHistoryChunk(df); + fileHistory.init(changelogRevisionIndex); +// fileHistory.linkTo(null); FIXME + int[] fileRevParents = new int[2]; - IntVector fileParentRevs = new IntVector((fileRevIndex+1) * 2, 0); - fileParentRevs.add(NO_REVISION, NO_REVISION); - for (int i = 1; i <= fileRevIndex; i++) { - df.parents(i, fileRevParents, null, null); - fileParentRevs.add(fileRevParents[0], fileRevParents[1]); - } - // collect file revisions to visit, from newest to oldest: - // traverse parents, starting from the given file revision - // this ignores all file revision made in parallel to the one of interest - IntVector fileRevsToVisit = new IntVector(fileRevIndex + 1, 0); - LinkedList queue = new LinkedList(); - BitSet seen = new BitSet(fileRevIndex + 1); - queue.add(fileRevIndex); - do { - int x = queue.removeFirst(); - if (seen.get(x)) { - continue; - } - seen.set(x); - fileRevsToVisit.add(x); - int p1 = fileParentRevs.get(2*x); - int p2 = fileParentRevs.get(2*x + 1); - if (p1 != NO_REVISION) { - queue.addLast(p1); - } - if (p2 != NO_REVISION) { - queue.addLast(p2); - } - } while (!queue.isEmpty()); FileLinesCache fileInfoCache = new FileLinesCache(df, 10); - // make sure no child is processed before we handled all (grand-)parents of the element - fileRevsToVisit.sort(false); - // fileRevsToVisit now { r10, r7, r6, r5, r0 } - // and we'll iterate it from behind, e.g. old to new unless reversed - if (iterateOrder == HgIterateDirection.NewToOld) { - fileRevsToVisit.reverse(); - } - for (int i = fileRevsToVisit.size() - 1; i >= 0; i--) { - int fri = fileRevsToVisit.get(i); + for (int fri : fileHistory.fileRevisions(iterateOrder)) { int clogRevIndex = df.getChangesetRevisionIndex(fri); - fileRevParents[0] = fileParentRevs.get(fri * 2); - fileRevParents[1] = fileParentRevs.get(fri * 2 + 1); + fileHistory.getParents(fri, fileRevParents); implAnnotateChange(fileInfoCache, clogRevIndex, fri, fileRevParents, insp); } } @@ -128,7 +101,7 @@ * 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(HgDataFile df, int changelogRevisionIndex, Inspector insp) throws HgCallbackTargetException { + public void annotateSingleRevision(int changelogRevisionIndex, Inspector insp) throws HgCallbackTargetException { // 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]; @@ -183,6 +156,74 @@ Nodeid fileRev = df.getRepo().getManifest().getFileRevision(csetRevIndex, df.getPath()); return df.getRevisionIndex(fileRev); } + + private static class FileRevisionHistoryChunk { + private final HgDataFile df; + private IntVector fileRevsToVisit; + private IntVector fileParentRevs; + + public FileRevisionHistoryChunk(HgDataFile file) { + df = file; + } + + public void getParents(int fileRevIndex, int[] fileRevParents) { + fileRevParents[0] = fileParentRevs.get(fileRevIndex * 2); + fileRevParents[1] = fileParentRevs.get(fileRevIndex * 2 + 1); + } + + public void init (int changelogRevisionIndex) { + // XXX df.indexWalk(0, fileRevIndex, ) might be more effective + int fileRevIndex = fileRevIndex(df, changelogRevisionIndex); + int[] fileRevParents = new int[2]; + fileParentRevs = new IntVector((fileRevIndex+1) * 2, 0); + fileParentRevs.add(NO_REVISION, NO_REVISION); // parents of fileRevIndex == 0 + for (int i = 1; i <= fileRevIndex; i++) { + df.parents(i, fileRevParents, null, null); + fileParentRevs.add(fileRevParents[0], fileRevParents[1]); + } + fileRevsToVisit = new IntVector(fileRevIndex + 1, 0); + LinkedList queue = new LinkedList(); + BitSet seen = new BitSet(fileRevIndex + 1); + queue.add(fileRevIndex); + do { + int x = queue.removeFirst(); + if (seen.get(x)) { + continue; + } + seen.set(x); + fileRevsToVisit.add(x); + int p1 = fileParentRevs.get(2*x); + int p2 = fileParentRevs.get(2*x + 1); + if (p1 != NO_REVISION) { + queue.addLast(p1); + } + if (p2 != NO_REVISION) { + queue.addLast(p2); + } + } while (!queue.isEmpty()); + // make sure no child is processed before we handled all (grand-)parents of the element + fileRevsToVisit.sort(false); + // now fileRevsToVisit keep file change ancestry from new to old + } + + public void linkTo(FileRevisionHistoryChunk origin) { + Internals.notImplemented(); + } + + public int[] fileRevisions(HgIterateDirection iterateOrder) { + // fileRevsToVisit is { r10, r7, r6, r5, r0 }, new to old + int[] rv = fileRevsToVisit.toArray(); + if (iterateOrder == HgIterateDirection.OldToNew) { + // reverse return value + for (int a = 0, b = rv.length-1; a < b; a++, b--) { + int t = rv[b]; + rv[b] = rv[a]; + rv[a] = t; + } + } + return rv; + } + } private static class FileLinesCache { private final HgDataFile df; diff -r 88f04c7cfedb -r 8ed4f4f4f0a6 test/org/tmatesoft/hg/test/TestBlame.java --- a/test/org/tmatesoft/hg/test/TestBlame.java Tue Apr 09 19:27:32 2013 +0200 +++ b/test/org/tmatesoft/hg/test/TestBlame.java Wed Apr 10 15:45:53 2013 +0200 @@ -70,7 +70,7 @@ final int checkChangeset = 539; HgDataFile df = repo.getFileNode(fname); ByteArrayOutputStream bos = new ByteArrayOutputStream(); - new HgBlameFacility().annotateSingleRevision(df, checkChangeset, new DiffOutInspector(new PrintStream(bos))); + new HgBlameFacility(df).annotateSingleRevision(checkChangeset, 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); @@ -141,10 +141,10 @@ public void testComplexHistoryAnnotate() throws Exception { HgRepository repo = Configuration.get().find("test-annotate"); HgDataFile df = repo.getFileNode("file1"); - HgBlameFacility af = new HgBlameFacility(); + HgBlameFacility af = new HgBlameFacility(df); ByteArrayOutputStream bos = new ByteArrayOutputStream(); DiffOutInspector dump = new DiffOutInspector(new PrintStream(bos)); - af.annotate(df, TIP, dump, HgIterateDirection.OldToNew); + af.annotate(TIP, dump, HgIterateDirection.OldToNew); LinkedList apiResult = new LinkedList(Arrays.asList(splitLines(bos.toString()))); /* @@ -227,18 +227,18 @@ final String fname = "src/org/tmatesoft/hg/internal/PatchGenerator.java"; final int checkChangeset = 539; HgDataFile df = repo.getFileNode(fname); - HgBlameFacility af = new HgBlameFacility(); + HgBlameFacility af = new HgBlameFacility(df); DiffOutInspector dump = new DiffOutInspector(System.out); System.out.println("541 -> 543"); - af.annotateSingleRevision(df, 543, dump); + af.annotateSingleRevision(543, dump); System.out.println("539 -> 541"); - af.annotateSingleRevision(df, 541, dump); + af.annotateSingleRevision(541, dump); System.out.println("536 -> 539"); - af.annotateSingleRevision(df, checkChangeset, dump); + af.annotateSingleRevision(checkChangeset, dump); System.out.println("531 -> 536"); - af.annotateSingleRevision(df, 536, dump); + af.annotateSingleRevision(536, dump); System.out.println(" -1 -> 531"); - af.annotateSingleRevision(df, 531, dump); + af.annotateSingleRevision(531, dump); FileAnnotateInspector fai = new FileAnnotateInspector(); FileAnnotation.annotate(df, 541, fai); @@ -252,7 +252,7 @@ final String fname = "src/org/tmatesoft/hg/repo/HgManifest.java"; final int checkChangeset = 415; HgDataFile df = repo.getFileNode(fname); - HgBlameFacility af = new HgBlameFacility(); + HgBlameFacility af = new HgBlameFacility(df); DiffOutInspector dump = new DiffOutInspector(System.out); // System.out.println("413 -> 415"); // af.diff(df, 413, 415, dump); @@ -262,16 +262,16 @@ // dump.needRevisions(true); // af.annotateChange(df, checkChangeset, dump); dump.needRevisions(true); - af.annotate(df, checkChangeset, dump, HgIterateDirection.OldToNew); + af.annotate(checkChangeset, dump, HgIterateDirection.OldToNew); } private void ccc() throws Throwable { HgRepository repo = new HgLookup().detect("/home/artem/hg/hgtest-annotate-merge/"); HgDataFile df = repo.getFileNode("file.txt"); - HgBlameFacility af = new HgBlameFacility(); + HgBlameFacility af = new HgBlameFacility(df); DiffOutInspector dump = new DiffOutInspector(System.out); dump.needRevisions(true); - af.annotate(df, 8, dump, HgIterateDirection.NewToOld); + af.annotate(8, dump, HgIterateDirection.NewToOld); // af.annotateSingleRevision(df, 113, dump); // System.out.println(); // af.annotate(df, TIP, new LineDumpInspector(true), HgIterateDirection.NewToOld); diff -r 88f04c7cfedb -r 8ed4f4f4f0a6 test/org/tmatesoft/hg/test/TestDiffHelper.java --- a/test/org/tmatesoft/hg/test/TestDiffHelper.java Tue Apr 09 19:27:32 2013 +0200 +++ b/test/org/tmatesoft/hg/test/TestDiffHelper.java Wed Apr 10 15:45:53 2013 +0200 @@ -172,7 +172,6 @@ } @Override protected void added(int s1InsertPoint, int s2From, int s2To) { - // TODO Auto-generated method stub added.add(s1InsertPoint, s2From, s2To - s2From); } @Override