Mercurial > hg4j
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(); |
