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