comparison src/org/tmatesoft/hg/internal/AnnotateFacility.java @ 548:ab21ac7dd833

Line-by-line annotation API and support code in place
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Mon, 18 Feb 2013 19:58:51 +0100
parents cd78e8b9d7bc
children 83afa680555d
comparison
equal deleted inserted replaced
547:66fc86e8c0dd 548:ab21ac7dd833
15 * contact TMate Software at support@hg4j.com 15 * contact TMate Software at support@hg4j.com
16 */ 16 */
17 package org.tmatesoft.hg.internal; 17 package org.tmatesoft.hg.internal;
18 18
19 import static org.tmatesoft.hg.repo.HgRepository.NO_REVISION; 19 import static org.tmatesoft.hg.repo.HgRepository.NO_REVISION;
20 import static org.tmatesoft.hg.repo.HgRepository.TIP;
20 21
21 import org.tmatesoft.hg.core.Nodeid; 22 import org.tmatesoft.hg.core.Nodeid;
22 import org.tmatesoft.hg.internal.PatchGenerator.LineSequence; 23 import org.tmatesoft.hg.internal.PatchGenerator.LineSequence;
23 import org.tmatesoft.hg.repo.HgDataFile; 24 import org.tmatesoft.hg.repo.HgDataFile;
24 import org.tmatesoft.hg.repo.HgInvalidStateException; 25 import org.tmatesoft.hg.repo.HgInvalidStateException;
26 import org.tmatesoft.hg.repo.HgRepository;
25 import org.tmatesoft.hg.util.CancelledException; 27 import org.tmatesoft.hg.util.CancelledException;
26 28
27 /** 29 /**
28 * 30 *
29 * @author Artem Tikhomirov 31 * @author Artem Tikhomirov
30 * @author TMate Software Ltd. 32 * @author TMate Software Ltd.
31 */ 33 */
32 @Experimental(reason="work in progress") 34 @Experimental(reason="work in progress")
33 public class AnnotateFacility { 35 public class AnnotateFacility {
34 36
37 /**
38 * Annotate file revision, line by line.
39 */
40 public void annotate(HgDataFile df, int changesetRevisionIndex, LineInspector insp) {
41 if (!df.exists()) {
42 return;
43 }
44 Nodeid fileRev = df.getRepo().getManifest().getFileRevision(changesetRevisionIndex, df.getPath());
45 int fileRevIndex = df.getRevisionIndex(fileRev);
46 int[] fileRevParents = new int[2];
47 FileAnnotation fa = new FileAnnotation(insp);
48 do {
49 // also covers changesetRevisionIndex == TIP, #implAnnotateChange doesn't tolerate constants
50 changesetRevisionIndex = df.getChangesetRevisionIndex(fileRevIndex);
51 df.parents(fileRevIndex, fileRevParents, null, null);
52 implAnnotateChange(df, changesetRevisionIndex, fileRevIndex, fileRevParents, fa);
53 fileRevIndex = fileRevParents[0];
54 } while (fileRevIndex != NO_REVISION);
55 }
56
35 /** 57 /**
36 * Annotates changes of the file against its parent(s) 58 * Annotates changes of the file against its parent(s)
37 */ 59 */
38 public void annotateChange(HgDataFile df, int changesetRevisionIndex, BlockInspector insp) { 60 public void annotateChange(HgDataFile df, int changesetRevisionIndex, BlockInspector insp) {
39 // TODO detect if file is text/binary (e.g. looking for chars < ' ' and not \t\r\n\f 61 // TODO detect if file is text/binary (e.g. looking for chars < ' ' and not \t\r\n\f
40 Nodeid fileRev = df.getRepo().getManifest().getFileRevision(changesetRevisionIndex, df.getPath()); 62 Nodeid fileRev = df.getRepo().getManifest().getFileRevision(changesetRevisionIndex, df.getPath());
41 int fileRevIndex = df.getRevisionIndex(fileRev); 63 int fileRevIndex = df.getRevisionIndex(fileRev);
42 int[] fileRevParents = new int[2]; 64 int[] fileRevParents = new int[2];
43 df.parents(fileRevIndex, fileRevParents, null, null); 65 df.parents(fileRevIndex, fileRevParents, null, null);
66 if (changesetRevisionIndex == TIP) {
67 changesetRevisionIndex = df.getChangesetRevisionIndex(fileRevIndex);
68 }
69 implAnnotateChange(df, changesetRevisionIndex, fileRevIndex, fileRevParents, insp);
70 }
71
72 private void implAnnotateChange(HgDataFile df, int csetRevIndex, int fileRevIndex, int[] fileParentRevs, BlockInspector insp) {
44 try { 73 try {
45 if (fileRevParents[0] != NO_REVISION && fileRevParents[1] != NO_REVISION) { 74 if (fileParentRevs[0] != NO_REVISION && fileParentRevs[1] != NO_REVISION) {
46 // merge 75 // merge
47 } else if (fileRevParents[0] == fileRevParents[1]) { 76 } else if (fileParentRevs[0] == fileParentRevs[1]) {
48 // may be equal iff both are unset 77 // may be equal iff both are unset
49 assert fileRevParents[0] == NO_REVISION; 78 assert fileParentRevs[0] == NO_REVISION;
50 // everything added 79 // everything added
51 ByteArrayChannel c; 80 ByteArrayChannel c;
52 df.content(fileRevIndex, c = new ByteArrayChannel()); 81 df.content(fileRevIndex, c = new ByteArrayChannel());
53 BlameBlockInspector bbi = new BlameBlockInspector(insp, NO_REVISION, changesetRevisionIndex); 82 BlameBlockInspector bbi = new BlameBlockInspector(insp, NO_REVISION, csetRevIndex);
54 LineSequence cls = LineSequence.newlines(c.toArray()); 83 LineSequence cls = LineSequence.newlines(c.toArray());
55 bbi.begin(LineSequence.newlines(new byte[0]), cls); 84 bbi.begin(LineSequence.newlines(new byte[0]), cls);
56 bbi.match(0, cls.chunkCount()-1, 0); 85 bbi.match(0, cls.chunkCount()-1, 0);
57 bbi.end(); 86 bbi.end();
58 } else { 87 } else {
59 int soleParent = fileRevParents[0] == NO_REVISION ? fileRevParents[1] : fileRevParents[0]; 88 int soleParent = fileParentRevs[0] == NO_REVISION ? fileParentRevs[1] : fileParentRevs[0];
60 assert soleParent != NO_REVISION; 89 assert soleParent != NO_REVISION;
61 ByteArrayChannel c1, c2; 90 ByteArrayChannel c1, c2;
62 df.content(soleParent, c1 = new ByteArrayChannel()); 91 df.content(soleParent, c1 = new ByteArrayChannel());
63 df.content(fileRevIndex, c2 = new ByteArrayChannel()); 92 df.content(fileRevIndex, c2 = new ByteArrayChannel());
64 int parentChangesetRevIndex = df.getChangesetRevisionIndex(soleParent); 93 int parentChangesetRevIndex = df.getChangesetRevisionIndex(soleParent);
65 PatchGenerator<LineSequence> pg = new PatchGenerator<LineSequence>(); 94 PatchGenerator<LineSequence> pg = new PatchGenerator<LineSequence>();
66 pg.init(LineSequence.newlines(c1.toArray()), LineSequence.newlines(c2.toArray())); 95 pg.init(LineSequence.newlines(c1.toArray()), LineSequence.newlines(c2.toArray()));
67 pg.findMatchingBlocks(new BlameBlockInspector(insp, parentChangesetRevIndex, changesetRevisionIndex)); 96 pg.findMatchingBlocks(new BlameBlockInspector(insp, parentChangesetRevIndex, csetRevIndex));
68 } 97 }
69 } catch (CancelledException ex) { 98 } catch (CancelledException ex) {
70 // TODO likely it was bad idea to throw cancelled exception from content() 99 // TODO likely it was bad idea to throw cancelled exception from content()
71 // deprecate and provide alternative? 100 // deprecate and provide alternative?
72 HgInvalidStateException ise = new HgInvalidStateException("ByteArrayChannel never throws CancelledException"); 101 HgInvalidStateException ise = new HgInvalidStateException("ByteArrayChannel never throws CancelledException");
121 public interface ChangeBlock extends AddBlock, DeleteBlock { 150 public interface ChangeBlock extends AddBlock, DeleteBlock {
122 } 151 }
123 152
124 @Callback 153 @Callback
125 public interface LineInspector { 154 public interface LineInspector {
155 /**
156 * Not necessarily invoked sequentially by line numbers
157 */
126 void line(int lineNumber, int changesetRevIndex, LineDescriptor ld); 158 void line(int lineNumber, int changesetRevIndex, LineDescriptor ld);
127 } 159 }
128 160
129 public interface LineDescriptor { 161 public interface LineDescriptor {
130 int totalLines(); 162 int totalLines();