Mercurial > hg4j
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 } |