comparison src/org/tmatesoft/hg/internal/BlameHelper.java @ 678:8625cba0a5a8

Towards better blame of merge revisions: refactor merge handling strategy
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Fri, 19 Jul 2013 15:36:29 +0200
parents cce0387c6041
children 58a6900f845d
comparison
equal deleted inserted replaced
677:1c49c0cee540 678:8625cba0a5a8
105 EqualBlocksCollector p2MergeCommon = new EqualBlocksCollector(); 105 EqualBlocksCollector p2MergeCommon = new EqualBlocksCollector();
106 pg.findMatchingBlocks(p2MergeCommon); 106 pg.findMatchingBlocks(p2MergeCommon);
107 // 107 //
108 pg.init(p1Lines); 108 pg.init(p1Lines);
109 BlameBlockInspector bbi = new BlameBlockInspector(targetFile, fileRevIndex, insp, p1ClogIndex, csetRevIndex); 109 BlameBlockInspector bbi = new BlameBlockInspector(targetFile, fileRevIndex, insp, p1ClogIndex, csetRevIndex);
110 bbi.setMergeParent2(p2MergeCommon, p2ClogIndex); 110 bbi.setMergeParent2(new MergeStrategy1(p2MergeCommon.matches), p2ClogIndex);
111 pg.findMatchingBlocks(bbi); 111 pg.findMatchingBlocks(bbi);
112 bbi.checkErrors(); 112 bbi.checkErrors();
113 } else if (fileParentClogRevs[0] == fileParentClogRevs[1]) { 113 } else if (fileParentClogRevs[0] == fileParentClogRevs[1]) {
114 // may be equal iff both are unset 114 // may be equal iff both are unset
115 assert fileParentClogRevs[0] == NO_REVISION; 115 assert fileParentClogRevs[0] == NO_REVISION;
216 216
217 private static class BlameBlockInspector extends DiffHelper.DeltaInspector<LineSequence> { 217 private static class BlameBlockInspector extends DiffHelper.DeltaInspector<LineSequence> {
218 private final HgBlameInspector insp; 218 private final HgBlameInspector insp;
219 private final int csetOrigin; 219 private final int csetOrigin;
220 private final int csetTarget; 220 private final int csetTarget;
221 private EqualBlocksCollector p2MergeCommon; 221 private MergeResolutionStrategy p2MergeCommon;
222 private int csetMergeParent; 222 private int csetMergeParent;
223 private IntSliceSeq mergeRanges;
224 private final AnnotateRev annotatedRevision; 223 private final AnnotateRev annotatedRevision;
225 private HgCallbackTargetException error; 224 private HgCallbackTargetException error;
226 225
227 public BlameBlockInspector(HgDataFile df, int fileRevIndex, HgBlameInspector inspector, int originCset, int targetCset) { 226 public BlameBlockInspector(HgDataFile df, int fileRevIndex, HgBlameInspector inspector, int originCset, int targetCset) {
228 assert inspector != null; 227 assert inspector != null;
231 annotatedRevision.set(df, fileRevIndex); 230 annotatedRevision.set(df, fileRevIndex);
232 csetOrigin = originCset; 231 csetOrigin = originCset;
233 csetTarget = targetCset; 232 csetTarget = targetCset;
234 } 233 }
235 234
236 public void setMergeParent2(EqualBlocksCollector p2Merge, int parentCset2) { 235 public void setMergeParent2(MergeResolutionStrategy p2MergeStrategy, int parentCset2) {
237 p2MergeCommon = p2Merge; 236 p2MergeCommon = p2MergeStrategy;
238 csetMergeParent = parentCset2; 237 csetMergeParent = parentCset2;
239 mergeRanges = new IntSliceSeq(3, 10, 10);
240 } 238 }
241 239
242 @Override 240 @Override
243 public void begin(LineSequence s1, LineSequence s2) { 241 public void begin(LineSequence s1, LineSequence s2) {
244 super.begin(s1, s2); 242 super.begin(s1, s2);
281 if (shallStop()) { 279 if (shallStop()) {
282 return; 280 return;
283 } 281 }
284 try { 282 try {
285 if (p2MergeCommon != null) { 283 if (p2MergeCommon != null) {
286 mergeRanges.clear(); 284 IntSliceSeq mergeRanges = p2MergeCommon.combineAndMarkRangesWithSource(s2From, s2To - s2From, csetOrigin, csetMergeParent);
287 p2MergeCommon.combineAndMarkRangesWithTarget(s2From, s2To - s2From, csetOrigin, csetMergeParent, mergeRanges);
288 285
289 /* 286 /*
290 * Usecases, how it USED TO BE initially: 287 * Usecases, how it USED TO BE initially:
291 * 3 lines changed to 10 lines. range of 10 lines breaks down to 2 from p2, 3 from p1, and 5 from p2. 288 * 3 lines changed to 10 lines. range of 10 lines breaks down to 2 from p2, 3 from p1, and 5 from p2.
292 * We report: 2 lines changed to 2(p2), then 1 line changed with 3(p1) and 5 lines added from p2. 289 * We report: 2 lines changed to 2(p2), then 1 line changed with 3(p1) and 5 lines added from p2.
313 block.setOriginAndTarget(rangeOrigin, csetTarget); 310 block.setOriginAndTarget(rangeOrigin, csetTarget);
314 insp.changed(block); 311 insp.changed(block);
315 s1ConsumedLines += s1LinesToBorrow; 312 s1ConsumedLines += s1LinesToBorrow;
316 s1Start += s1LinesToBorrow; 313 s1Start += s1LinesToBorrow;
317 } else { 314 } else {
318 int blockInsPoint = rangeOrigin != csetMergeParent ? s1Start : p2MergeCommon.reverseMapLine(rangeStart); 315 int blockInsPoint = rangeOrigin != csetMergeParent ? s1Start : p2MergeCommon.getLineInP2(rangeStart);
319 ChangeBlockImpl block = getAddBlock(rangeStart, rangeLen, blockInsPoint); 316 ChangeBlockImpl block = getAddBlock(rangeStart, rangeLen, blockInsPoint);
320 block.setOriginAndTarget(rangeOrigin, csetTarget); 317 block.setOriginAndTarget(rangeOrigin, csetTarget);
321 insp.added(block); 318 insp.added(block);
322 } 319 }
323 } 320 }
346 if (shallStop()) { 343 if (shallStop()) {
347 return; 344 return;
348 } 345 }
349 try { 346 try {
350 if (p2MergeCommon != null) { 347 if (p2MergeCommon != null) {
351 mergeRanges.clear(); 348 IntSliceSeq mergeRanges = p2MergeCommon.combineAndMarkRangesWithSource(s2From, s2To - s2From, csetOrigin, csetMergeParent);
352 p2MergeCommon.combineAndMarkRangesWithTarget(s2From, s2To - s2From, csetOrigin, csetMergeParent, mergeRanges);
353 int insPoint = s1InsertPoint; // track changes to insertion point 349 int insPoint = s1InsertPoint; // track changes to insertion point
354 for (IntTuple mergeRange : mergeRanges) { 350 for (IntTuple mergeRange : mergeRanges) {
355 int rangeOrigin = mergeRange.at(0); 351 int rangeOrigin = mergeRange.at(0);
356 int rangeStart = mergeRange.at(1); 352 int rangeStart = mergeRange.at(1);
357 int rangeLen = mergeRange.at(2); 353 int rangeLen = mergeRange.at(2);
354 // XXX likely need somewhat similar to the code above:
355 // int blockInsPoint = rangeOrigin != csetMergeParent ? s1Start : p2MergeCommon.reverseMapLine(rangeStart);
356 //
358 ChangeBlockImpl block = getAddBlock(rangeStart, rangeLen, insPoint); 357 ChangeBlockImpl block = getAddBlock(rangeStart, rangeLen, insPoint);
359 block.setOriginAndTarget(rangeOrigin, csetTarget); 358 block.setOriginAndTarget(rangeOrigin, csetTarget);
360 insp.added(block); 359 insp.added(block);
361 // indicate insPoint moved down number of lines we just reported 360 // indicate insPoint moved down number of lines we just reported
362 insPoint += rangeLen; 361 insPoint += rangeLen;
617 public byte[] asArray() { 616 public byte[] asArray() {
618 return contentBlock.seq.data(from, from + length); 617 return contentBlock.seq.data(from, from + length);
619 } 618 }
620 } 619 }
621 620
622 621
622 interface MergeResolutionStrategy {
623 /**
624 * breaks region [start..start+length) into ranges according to deduced (or simply guessed)
625 * matching of these lines to lines in source1 and source2
626 * @return list of tuples (source, start, length), where source is one of the identifiers supplied
627 */
628 public IntSliceSeq combineAndMarkRangesWithSource(int start, int length, int source1, int source2);
629 public int getLineInP2(int mergeLine);
630 }
631
623 private static class EqualBlocksCollector implements DiffHelper.MatchInspector<LineSequence> { 632 private static class EqualBlocksCollector implements DiffHelper.MatchInspector<LineSequence> {
624 private final RangePairSeq matches = new RangePairSeq(); 633 private final RangePairSeq matches = new RangePairSeq();
625 634
626 public void begin(LineSequence s1, LineSequence s2) { 635 public void begin(LineSequence s1, LineSequence s2) {
627 } 636 }
629 public void match(int startSeq1, int startSeq2, int matchLength) { 638 public void match(int startSeq1, int startSeq2, int matchLength) {
630 matches.add(startSeq1, startSeq2, matchLength); 639 matches.add(startSeq1, startSeq2, matchLength);
631 } 640 }
632 641
633 public void end() { 642 public void end() {
634 }
635
636 public int reverseMapLine(int ln) {
637 return matches.reverseMapLine(ln);
638 } 643 }
639 644
640 public void intersectWithTarget(int start, int length, IntVector result) { 645 public void intersectWithTarget(int start, int length, IntVector result) {
641 int s = start; 646 int s = start;
642 for (int l = start, x = start + length; l < x; l++) { 647 for (int l = start, x = start + length; l < x; l++) {
652 result.add(s); 657 result.add(s);
653 result.add((start + length) - s); 658 result.add((start + length) - s);
654 } 659 }
655 } 660 }
656 661
662 }
663
664 private static class MergeStrategy1 implements MergeResolutionStrategy {
665 // equal ranges in p2 and merged revision
666 private final RangePairSeq matches;
667 private final IntSliceSeq mergeRanges;
668
669 public MergeStrategy1(RangePairSeq p2EqualToM) {
670 matches = p2EqualToM;
671 mergeRanges = new IntSliceSeq(3, 10, 10);
672 }
673
657 /* 674 /*
658 * intersects [start..start+length) with ranges of target lines, and based on the intersection 675 * intersects [start..start+length) with ranges of target lines, and based on the intersection
659 * breaks initial range into smaller ranges and records them into result, with marker to indicate 676 * breaks initial range into smaller ranges and records them into result, with marker to indicate
660 * whether the range is from initial range (markerSource) or is a result of the intersection with target 677 * whether the range is from initial range (markerSource) or is a result of the intersection with target
661 * (markerTarget) 678 * (markerTarget)
662 */ 679 */
663 public void combineAndMarkRangesWithTarget(int start, int length, int markerSource, int markerTarget, IntSliceSeq result) { 680 public IntSliceSeq combineAndMarkRangesWithSource(int start, int length, int markerSource, int markerTarget) {
664 assert result.sliceSize() == 3; 681 mergeRanges.clear();
682 assert mergeRanges.sliceSize() == 3;
665 int sourceStart = start, targetStart = start, sourceEnd = start + length; 683 int sourceStart = start, targetStart = start, sourceEnd = start + length;
666 for (int l = sourceStart; l < sourceEnd; l++) { 684 for (int l = sourceStart; l < sourceEnd; l++) {
667 if (matches.includesTargetLine(l)) { 685 if (matches.includesTargetLine(l)) {
668 // l is from target 686 // l is from target
669 if (sourceStart < l) { 687 if (sourceStart < l) {
670 // few lines from source range were not in the target, report them 688 // few lines from source range were not in the target, report them
671 result.add(markerSource, sourceStart, l - sourceStart); 689 mergeRanges.add(markerSource, sourceStart, l - sourceStart);
672 } 690 }
673 // indicate the earliest line from source range to use 691 // indicate the earliest line from source range to use
674 sourceStart = l + 1; 692 sourceStart = l + 1;
675 } else { 693 } else {
676 // l is not in target 694 // l is not in target
677 if (targetStart < l) { 695 if (targetStart < l) {
678 // report lines from target range 696 // report lines from target range
679 result.add(markerTarget, targetStart, l - targetStart); 697 mergeRanges.add(markerTarget, targetStart, l - targetStart);
680 } 698 }
681 // next line *may* be from target 699 // next line *may* be from target
682 targetStart = l + 1; 700 targetStart = l + 1;
683 } 701 }
684 } 702 }
685 // if source range end with line from target, sourceStart would be == sourceEnd, and we need to add range with markerTarget 703 // if source range end with line from target, sourceStart would be == sourceEnd, and we need to add range with markerTarget
686 // if source range doesn't end with target line, targetStart == sourceEnd, while sourceStart < sourceEnd 704 // if source range doesn't end with target line, targetStart == sourceEnd, while sourceStart < sourceEnd
687 if (sourceStart < sourceEnd) { 705 if (sourceStart < sourceEnd) {
688 assert targetStart == sourceEnd; 706 assert targetStart == sourceEnd;
689 // something left from the source range 707 // something left from the source range
690 result.add(markerSource, sourceStart, sourceEnd - sourceStart); 708 mergeRanges.add(markerSource, sourceStart, sourceEnd - sourceStart);
691 } else if (targetStart < sourceEnd) { 709 } else if (targetStart < sourceEnd) {
692 assert sourceStart == sourceEnd; 710 assert sourceStart == sourceEnd;
693 result.add(markerTarget, targetStart, sourceEnd - targetStart); 711 mergeRanges.add(markerTarget, targetStart, sourceEnd - targetStart);
694 } 712 }
713 return mergeRanges;
714 }
715
716 public int getLineInP2(int mergeLine) {
717 return matches.reverseMapLine(mergeLine);
695 } 718 }
696 } 719 }
697 720
698 private static class AnnotateRev implements RevisionDescriptor { 721 private static class AnnotateRev implements RevisionDescriptor {
699 public ContentBlock origin, target; 722 public ContentBlock origin, target;
763 bc.intersectWithTarget(7, 10, r); 786 bc.intersectWithTarget(7, 10, r);
764 for (int i = 0; i < r.size(); i+=2) { 787 for (int i = 0; i < r.size(); i+=2) {
765 System.out.printf("[%d..%d) ", r.get(i), r.get(i) + r.get(i+1)); 788 System.out.printf("[%d..%d) ", r.get(i), r.get(i) + r.get(i+1));
766 } 789 }
767 System.out.println(); 790 System.out.println();
768 IntSliceSeq mr = new IntSliceSeq(3); 791 MergeStrategy1 ms = new MergeStrategy1(bc.matches);
769 bc.combineAndMarkRangesWithTarget(0, 16, 508, 514, mr); 792 IntSliceSeq mr = ms.combineAndMarkRangesWithSource(0, 16, 508, 514);
770 for (IntTuple t : mr) { 793 for (IntTuple t : mr) {
771 System.out.printf("%d:[%d..%d) ", t.at(0), t.at(1), t.at(1) + t.at(2)); 794 System.out.printf("%d:[%d..%d) ", t.at(0), t.at(1), t.at(1) + t.at(2));
772 } 795 }
773 } 796 }
774 } 797 }