comparison src/org/tmatesoft/hg/internal/ReverseAnnotateInspector.java @ 676:3219cfadda49

Switch to alternative annotate producer (walks from parents to children). Refactor FileAnnotation to match updated annotate approach
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Thu, 18 Jul 2013 18:03:51 +0200
parents src/org/tmatesoft/hg/internal/FileAnnotation.java@cce0387c6041
children 1c49c0cee540
comparison
equal deleted inserted replaced
675:a20121a2bba6 676:3219cfadda49
1 /*
2 * Copyright (c) 2013 TMate Software Ltd
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 2 of the License.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * For information on how to redistribute this software under
14 * the terms of a license other than GNU General Public License
15 * contact TMate Software at support@hg4j.com
16 */
17 package org.tmatesoft.hg.internal;
18
19
20 import static org.tmatesoft.hg.repo.HgRepository.NO_REVISION;
21
22 import java.util.Arrays;
23
24 import org.tmatesoft.hg.core.HgAnnotateCommand;
25 import org.tmatesoft.hg.core.HgBlameInspector;
26 import org.tmatesoft.hg.core.HgIterateDirection;
27 import org.tmatesoft.hg.core.HgBlameInspector.RevisionDescriptor;
28 import org.tmatesoft.hg.core.HgCallbackTargetException;
29 import org.tmatesoft.hg.repo.HgInvalidStateException;
30 import org.tmatesoft.hg.util.CancelSupport;
31 import org.tmatesoft.hg.util.CancelledException;
32 import org.tmatesoft.hg.util.ProgressSupport;
33
34 /**
35 * Produce output like 'hg annotate' does.
36 * Expects revisions to come in order from child to parent.
37 * Unlike {@link ForwardAnnotateInspector}, can be easily modified to report lines as soon as its origin is detected.
38 *
39 * (+) Handles annotate of partial history, at any moment lines with ({@link #knownLines} == <code>false</code> indicate lines
40 * that were added prior to any revision already visited.
41 *
42 * @author Artem Tikhomirov
43 * @author TMate Software Ltd.
44 */
45 public class ReverseAnnotateInspector implements HgBlameInspector, RevisionDescriptor.Recipient {
46
47 // keeps <startSeq1, startSeq2, len> of equal blocks, origin to target, from some previous step
48 private RangePairSeq activeEquals;
49 // equal blocks of the current iteration, to be recalculated before next step
50 // to track line number (current target to ultimate target) mapping
51 private RangePairSeq intermediateEquals = new RangePairSeq();
52
53 private boolean[] knownLines;
54 private RevisionDescriptor revisionDescriptor;
55 private BlockData lineContent;
56
57 private IntMap<RangePairSeq> mergedRanges = new IntMap<RangePairSeq>(10);
58 private IntMap<RangePairSeq> equalRanges = new IntMap<RangePairSeq>(10);
59 private boolean activeEqualsComesFromMerge = false;
60
61 private int[] lineRevisions;
62
63 /**
64 * @return desired order of iteration for diff
65 */
66 public HgIterateDirection iterateDirection() {
67 return HgIterateDirection.NewToOld;
68 }
69
70 public void report(int annotateRevIndex, HgAnnotateCommand.Inspector insp, ProgressSupport progress, CancelSupport cancel) throws HgCallbackTargetException, CancelledException {
71 LineImpl li = new LineImpl();
72 progress.start(lineRevisions.length);
73 for (int i = 0; i < lineRevisions.length; i++) {
74 byte[] c = lineContent.elementAt(i).asArray();
75 li.init(i+1, lineRevisions[i], c);
76 insp.next(li);
77 progress.worked(1);
78 cancel.checkCancelled();
79 }
80 progress.done();
81 }
82
83 public void start(RevisionDescriptor rd) {
84 revisionDescriptor = rd;
85 if (knownLines == null) {
86 lineContent = rd.target();
87 knownLines = new boolean[lineContent.elementCount()];
88 lineRevisions = new int [lineContent.elementCount()];
89 Arrays.fill(lineRevisions, NO_REVISION);
90 activeEquals = new RangePairSeq();
91 activeEquals.add(0, 0, knownLines.length);
92 equalRanges.put(rd.targetChangesetIndex(), activeEquals);
93 } else {
94 activeEquals = equalRanges.get(rd.targetChangesetIndex());
95 if (activeEquals == null) {
96 // we didn't see this target revision as origin yet
97 // the only way this may happen is that this revision was a merge parent
98 activeEquals = mergedRanges.get(rd.targetChangesetIndex());
99 activeEqualsComesFromMerge = true;
100 if (activeEquals == null) {
101 throw new HgInvalidStateException(String.format("Can't find previously visited revision %d (while in %d->%1$d diff)", rd.targetChangesetIndex(), rd.originChangesetIndex()));
102 }
103 }
104 }
105 }
106
107 public void done(RevisionDescriptor rd) {
108 // update line numbers of the intermediate target to point to ultimate target's line numbers
109 RangePairSeq v = intermediateEquals.intersect(activeEquals);
110 if (activeEqualsComesFromMerge) {
111 mergedRanges.put(rd.originChangesetIndex(), v);
112 } else {
113 equalRanges.put(rd.originChangesetIndex(), v);
114 }
115 if (rd.isMerge() && !mergedRanges.containsKey(rd.mergeChangesetIndex())) {
116 // seen merge, but no lines were merged from p2.
117 // Add empty range to avoid uncertainty when a parent of p2 pops in
118 mergedRanges.put(rd.mergeChangesetIndex(), new RangePairSeq());
119 }
120 intermediateEquals.clear();
121 activeEquals = null;
122 activeEqualsComesFromMerge = false;
123 revisionDescriptor = null;
124 }
125
126 public void same(EqualBlock block) {
127 intermediateEquals.add(block.originStart(), block.targetStart(), block.length());
128 }
129
130 public void added(AddBlock block) {
131 RangePairSeq rs = null;
132 if (revisionDescriptor.isMerge() && block.originChangesetIndex() == revisionDescriptor.mergeChangesetIndex()) {
133 rs = mergedRanges.get(revisionDescriptor.mergeChangesetIndex());
134 if (rs == null) {
135 mergedRanges.put(revisionDescriptor.mergeChangesetIndex(), rs = new RangePairSeq());
136 }
137 }
138 if (activeEquals.size() == 0) {
139 return;
140 }
141 for (int i = 0, ln = block.firstAddedLine(), x = block.totalAddedLines(); i < x; i++, ln++) {
142 int lnInFinal = activeEquals.mapLineIndex(ln);
143 if (lnInFinal != -1/* && !knownLines[lnInFinal]*/) {
144 if (rs != null) {
145 rs.add(block.insertedAt() + i, lnInFinal, 1);
146 } else {
147 line(lnInFinal, block.targetChangesetIndex());
148 }
149 knownLines[lnInFinal] = true;
150 }
151 }
152 }
153
154 public void changed(ChangeBlock block) {
155 added(block);
156 }
157
158 public void deleted(DeleteBlock block) {
159 }
160
161 private void line(int lineNumber, int changesetRevIndex) {
162 lineRevisions[lineNumber] = changesetRevIndex;
163 }
164 }