Changeset 702 for trunk/forum/includes/diff/diff.php
- Timestamp:
- Mar 31, 2010, 6:32:40 PM (15 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/forum/includes/diff/diff.php
r400 r702 3 3 * 4 4 * @package diff 5 * @version $Id : diff.php 8765 2008-08-16 22:18:25Z aptx$5 * @version $Id$ 6 6 * @copyright (c) 2006 phpBB Group 7 7 * @license http://opensource.org/licenses/gpl-license.php GNU Public License … … 18 18 19 19 /** 20 * Code from pear.php.net, Text_Diff-1. 0.0 package20 * Code from pear.php.net, Text_Diff-1.1.0 package 21 21 * http://pear.php.net/package/Text_Diff/ 22 22 * … … 59 59 { 60 60 return $this->_edits; 61 } 62 63 /** 64 * returns the number of new (added) lines in a given diff. 65 * 66 * @since Text_Diff 1.1.0 67 * 68 * @return integer The number of new lines 69 */ 70 function count_added_lines() 71 { 72 $count = 0; 73 74 for ($i = 0, $size = sizeof($this->_edits); $i < $size; $i++) 75 { 76 $edit = $this->_edits[$i]; 77 78 if (is_a($edit, 'diff_op_add') || is_a($edit, 'diff_op_change')) 79 { 80 $count += $edit->nfinal(); 81 } 82 } 83 return $count; 84 } 85 86 /** 87 * Returns the number of deleted (removed) lines in a given diff. 88 * 89 * @since Text_Diff 1.1.0 90 * 91 * @return integer The number of deleted lines 92 */ 93 function count_deleted_lines() 94 { 95 $count = 0; 96 97 for ($i = 0, $size = sizeof($this->_edits); $i < $size; $i++) 98 { 99 $edit = $this->_edits[$i]; 100 101 if (is_a($edit, 'diff_op_delete') || is_a($edit, 'diff_op_change')) 102 { 103 $count += $edit->norig(); 104 } 105 } 106 return $count; 61 107 } 62 108 … … 87 133 $rev->_edits = array(); 88 134 89 foreach ($this->_edits as $edit) 90 { 135 for ($i = 0, $size = sizeof($this->_edits); $i < $size; $i++) 136 { 137 $edit = $this->_edits[$i]; 91 138 $rev->_edits[] = $edit->reverse(); 92 139 } … … 102 149 function is_empty() 103 150 { 104 foreach ($this->_edits as $edit) 105 { 106 if (!is_a($edit, 'diff_op_copy')) 107 { 151 for ($i = 0, $size = sizeof($this->_edits); $i < $size; $i++) 152 { 153 $edit = $this->_edits[$i]; 154 155 // skip diff_op_copy 156 if (is_a($edit, 'diff_op_copy')) 157 { 158 continue; 159 } 160 161 if (is_a($edit, 'diff_op_delete') || is_a($edit, 'diff_op_add')) 162 { 163 $orig = $edit->orig; 164 $final = $edit->final; 165 166 // We can simplify one case where the array is usually supposed to be empty... 167 if (sizeof($orig) == 1 && trim($orig[0]) === '') $orig = array(); 168 if (sizeof($final) == 1 && trim($final[0]) === '') $final = array(); 169 170 if (!$orig && !$final) 171 { 172 continue; 173 } 174 108 175 return false; 109 176 } 110 } 177 178 return false; 179 } 180 111 181 return true; 112 182 } … … 123 193 $lcs = 0; 124 194 125 foreach ($this->_edits as $edit) 126 { 195 for ($i = 0, $size = sizeof($this->_edits); $i < $size; $i++) 196 { 197 $edit = $this->_edits[$i]; 198 127 199 if (is_a($edit, 'diff_op_copy')) 128 200 { … … 144 216 $lines = array(); 145 217 146 foreach ($this->_edits as $edit) 147 { 218 for ($i = 0, $size = sizeof($this->_edits); $i < $size; $i++) 219 { 220 $edit = $this->_edits[$i]; 221 148 222 if ($edit->orig) 149 223 { … … 165 239 $lines = array(); 166 240 167 foreach ($this->_edits as $edit) 168 { 241 for ($i = 0, $size = sizeof($this->_edits); $i < $size; $i++) 242 { 243 $edit = $this->_edits[$i]; 244 169 245 if ($edit->final) 170 246 { … … 217 293 $prevtype = null; 218 294 219 foreach ($this->_edits as $edit) 220 { 295 for ($i = 0, $size = sizeof($this->_edits); $i < $size; $i++) 296 { 297 $edit = $this->_edits[$i]; 298 221 299 if ($prevtype == get_class($edit)) 222 300 { … … 415 493 * @param array $final2 The second version to compare to. 416 494 */ 417 function diff3(&$orig, &$final1, &$final2 )495 function diff3(&$orig, &$final1, &$final2, $preserve_cr = true) 418 496 { 419 497 $diff_engine = new diff_engine(); 420 498 421 $diff_1 = $diff_engine->diff($orig, $final1 );422 $diff_2 = $diff_engine->diff($orig, $final2 );423 424 unset($ engine);499 $diff_1 = $diff_engine->diff($orig, $final1, $preserve_cr); 500 $diff_2 = $diff_engine->diff($orig, $final2, $preserve_cr); 501 502 unset($diff_engine); 425 503 426 504 $this->_edits = $this->_diff3($diff_1, $diff_2); … … 428 506 429 507 /** 430 * Return merged output 508 * Return number of conflicts 509 */ 510 function get_num_conflicts() 511 { 512 $conflicts = 0; 513 514 for ($i = 0, $size = sizeof($this->_edits); $i < $size; $i++) 515 { 516 $edit = $this->_edits[$i]; 517 518 if ($edit->is_conflict()) 519 { 520 $conflicts++; 521 } 522 } 523 524 return $conflicts; 525 } 526 527 /** 528 * Get conflicts content for download. This is generally a merged file, but preserving conflicts and adding explanations to it. 529 * A user could then go through this file, search for the conflicts and changes the code accordingly. 431 530 * 432 531 * @param string $label1 the cvs file version/label from the original set of lines 433 532 * @param string $label2 the cvs file version/label from the new set of lines 434 533 * @param string $label_sep the explanation between label1 and label2 - more of a helper for the user 435 * @param bool $get_conflicts if set to true only the number of conflicts is returned436 * @param bool $merge_new if set to true the merged output will have the new file contents on a conflicting merge437 534 * 438 535 * @return mixed the merged output 439 536 */ 440 function merged_output($label1 = 'CURRENT_FILE', $label2 = 'NEW_FILE', $label_sep = 'DIFF_SEP_EXPLAIN', $get_conflicts = false, $merge_new = false)537 function get_conflicts_content($label1 = 'CURRENT_FILE', $label2 = 'NEW_FILE', $label_sep = 'DIFF_SEP_EXPLAIN') 441 538 { 442 539 global $user; 443 444 if ($get_conflicts)445 {446 foreach ($this->_edits as $edit)447 {448 if ($edit->is_conflict())449 {450 $this->_conflicting_blocks++;451 }452 }453 454 return $this->_conflicting_blocks;455 }456 540 457 541 $label1 = (!empty($user->lang[$label1])) ? $user->lang[$label1] : $label1; … … 461 545 $lines = array(); 462 546 463 foreach ($this->_edits as $edit) 464 { 547 for ($i = 0, $size = sizeof($this->_edits); $i < $size; $i++) 548 { 549 $edit = $this->_edits[$i]; 550 465 551 if ($edit->is_conflict()) 466 552 { 467 if (!$merge_new) 468 { 469 $lines = array_merge($lines, array('<<<<<<<' . ($label1 ? ' ' . $label1 : '')), $edit->final1, array('=======' . ($label_sep ? ' ' . $label_sep : '')), $edit->final2, array('>>>>>>>' . ($label2 ? ' ' . $label2 : ''))); 470 } 471 else 472 { 473 $lines = array_merge($lines, $edit->final1); 474 } 553 // Start conflict label 554 $label_start = array('<<<<<<< ' . $label1); 555 $label_mid = array('======= ' . $label_sep); 556 $label_end = array('>>>>>>> ' . $label2); 557 558 $lines = array_merge($lines, $label_start, $edit->final1, $label_mid, $edit->final2, $label_end); 475 559 $this->_conflicting_blocks++; 476 560 } … … 485 569 486 570 /** 571 * Return merged output (used by the renderer) 572 * 573 * @return mixed the merged output 574 */ 575 function merged_output() 576 { 577 return $this->get_conflicts_content(); 578 } 579 580 /** 487 581 * Merge the output and use the new file code for conflicts 488 582 */ … … 491 585 $lines = array(); 492 586 493 foreach ($this->_edits as $edit) 494 { 587 for ($i = 0, $size = sizeof($this->_edits); $i < $size; $i++) 588 { 589 $edit = $this->_edits[$i]; 590 495 591 if ($edit->is_conflict()) 496 592 { … … 513 609 $lines = array(); 514 610 515 foreach ($this->_edits as $edit) 516 { 611 for ($i = 0, $size = sizeof($this->_edits); $i < $size; $i++) 612 { 613 $edit = $this->_edits[$i]; 614 517 615 if ($edit->is_conflict()) 518 616 { … … 535 633 $conflicts = array(); 536 634 537 foreach ($this->_edits as $edit) 538 { 635 for ($i = 0, $size = sizeof($this->_edits); $i < $size; $i++) 636 { 637 $edit = $this->_edits[$i]; 638 539 639 if ($edit->is_conflict()) 540 640 { … … 660 760 if (!isset($this->_merged)) 661 761 { 762 // Prepare the arrays before we compare them. ;) 763 $this->solve_prepare(); 764 662 765 if ($this->final1 === $this->final2) 663 766 { … … 674 777 else 675 778 { 779 // The following tries to aggressively solve conflicts... 676 780 $this->_merged = false; 781 $this->solve_conflict(); 677 782 } 678 783 } … … 684 789 { 685 790 return ($this->merged() === false) ? true : false; 791 } 792 793 /** 794 * Function to prepare the arrays for comparing - we want to skip over newline changes 795 * @author acydburn 796 */ 797 function solve_prepare() 798 { 799 // We can simplify one case where the array is usually supposed to be empty... 800 if (sizeof($this->orig) == 1 && trim($this->orig[0]) === '') $this->orig = array(); 801 if (sizeof($this->final1) == 1 && trim($this->final1[0]) === '') $this->final1 = array(); 802 if (sizeof($this->final2) == 1 && trim($this->final2[0]) === '') $this->final2 = array(); 803 804 // Now we only can have the case where the only difference between arrays are newlines, so compare all cases 805 806 // First, some strings we can compare... 807 $orig = $final1 = $final2 = ''; 808 809 foreach ($this->orig as $null => $line) $orig .= trim($line); 810 foreach ($this->final1 as $null => $line) $final1 .= trim($line); 811 foreach ($this->final2 as $null => $line) $final2 .= trim($line); 812 813 // final1 === final2 814 if ($final1 === $final2) 815 { 816 // We preserve the part which will be used in the merge later 817 $this->final2 = $this->final1; 818 } 819 // final1 === orig 820 else if ($final1 === $orig) 821 { 822 // Here it does not really matter what we choose, but we will use the new code 823 $this->orig = $this->final1; 824 } 825 // final2 === orig 826 else if ($final2 === $orig) 827 { 828 // Here it does not really matter too (final1 will be used), but we will use the new code 829 $this->orig = $this->final2; 830 } 831 } 832 833 /** 834 * Find code portions from $orig in $final1 and use $final2 as merged instance if provided 835 * @author acydburn 836 */ 837 function _compare_conflict_seq($orig, $final1, $final2 = false) 838 { 839 $result = array('merge_found' => false, 'merge' => array()); 840 841 $_orig = &$this->$orig; 842 $_final1 = &$this->$final1; 843 844 // Ok, we basically search for $orig in $final1 845 $compare_seq = sizeof($_orig); 846 847 // Go through the conflict code 848 for ($i = 0, $j = 0, $size = sizeof($_final1); $i < $size; $i++, $j = $i) 849 { 850 $line = $_final1[$i]; 851 $skip = 0; 852 853 for ($x = 0; $x < $compare_seq; $x++) 854 { 855 // Try to skip all matching lines 856 if (trim($line) === trim($_orig[$x])) 857 { 858 $line = (++$j < $size) ? $_final1[$j] : $line; 859 $skip++; 860 } 861 } 862 863 if ($skip === $compare_seq) 864 { 865 $result['merge_found'] = true; 866 867 if ($final2 !== false) 868 { 869 $result['merge'] = array_merge($result['merge'], $this->$final2); 870 } 871 $i += ($skip - 1); 872 } 873 else if ($final2 !== false) 874 { 875 $result['merge'][] = $line; 876 } 877 } 878 879 return $result; 880 } 881 882 /** 883 * Tries to solve conflicts aggressively based on typical "assumptions" 884 * @author acydburn 885 */ 886 function solve_conflict() 887 { 888 $this->_merged = false; 889 890 // CASE ONE: orig changed into final2, but modified/unknown code in final1. 891 // IF orig is found "as is" in final1 we replace the code directly in final1 and populate this as final2/merge 892 if (sizeof($this->orig) && sizeof($this->final2)) 893 { 894 $result = $this->_compare_conflict_seq('orig', 'final1', 'final2'); 895 896 if ($result['merge_found']) 897 { 898 $this->final2 = $result['merge']; 899 $this->_merged = &$this->final2; 900 return; 901 } 902 903 $result = $this->_compare_conflict_seq('final2', 'final1'); 904 905 if ($result['merge_found']) 906 { 907 $this->_merged = &$this->final1; 908 return; 909 } 910 911 // Try to solve $Id$ issues. ;) 912 if (sizeof($this->orig) == 1 && sizeof($this->final1) == 1 && sizeof($this->final2) == 1) 913 { 914 $match = '#^' . preg_quote('* @version $Id: ', '#') . '[a-z\._\- ]+[0-9]+ [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9\:Z]+ [a-z0-9_\- ]+\$$#'; 915 916 if (preg_match($match, $this->orig[0]) && preg_match($match, $this->final1[0]) && preg_match($match, $this->final2[0])) 917 { 918 $this->_merged = &$this->final2; 919 return; 920 } 921 } 922 923 $second_run = false; 924 925 // Try to solve issues where the only reason why the above did not work is a newline being removed in the final1 code but exist in the orig/final2 code 926 if (trim($this->orig[0]) === '' && trim($this->final2[0]) === '') 927 { 928 unset($this->orig[0], $this->final2[0]); 929 $this->orig = array_values($this->orig); 930 $this->final2 = array_values($this->final2); 931 932 $second_run = true; 933 } 934 935 // The same is true for a line at the end. ;) 936 if (sizeof($this->orig) && sizeof($this->final2) && sizeof($this->orig) === sizeof($this->final2) && trim($this->orig[sizeof($this->orig)-1]) === '' && trim($this->final2[sizeof($this->final2)-1]) === '') 937 { 938 unset($this->orig[sizeof($this->orig)-1], $this->final2[sizeof($this->final2)-1]); 939 $this->orig = array_values($this->orig); 940 $this->final2 = array_values($this->final2); 941 942 $second_run = true; 943 } 944 945 if ($second_run) 946 { 947 $result = $this->_compare_conflict_seq('orig', 'final1', 'final2'); 948 949 if ($result['merge_found']) 950 { 951 $this->final2 = $result['merge']; 952 $this->_merged = &$this->final2; 953 return; 954 } 955 956 $result = $this->_compare_conflict_seq('final2', 'final1'); 957 958 if ($result['merge_found']) 959 { 960 $this->_merged = &$this->final1; 961 return; 962 } 963 } 964 965 return; 966 } 967 968 // CASE TWO: Added lines from orig to final2 but final1 had added lines too. Just merge them. 969 if (!sizeof($this->orig) && $this->final1 !== $this->final2 && sizeof($this->final1) && sizeof($this->final2)) 970 { 971 $result = $this->_compare_conflict_seq('final2', 'final1'); 972 973 if ($result['merge_found']) 974 { 975 $this->final2 = $this->final1; 976 $this->_merged = &$this->final1; 977 } 978 else 979 { 980 $result = $this->_compare_conflict_seq('final1', 'final2'); 981 982 if (!$result['merge_found']) 983 { 984 $this->final2 = array_merge($this->final1, $this->final2); 985 $this->_merged = &$this->final2; 986 } 987 else 988 { 989 $this->final2 = $this->final1; 990 $this->_merged = &$this->final1; 991 } 992 } 993 994 return; 995 } 996 997 // CASE THREE: Removed lines (orig has the to-remove line(s), but final1 has additional lines which does not need to be removed). Just remove orig from final1 and then use final1 as final2/merge 998 if (!sizeof($this->final2) && sizeof($this->orig) && sizeof($this->final1) && $this->orig !== $this->final1) 999 { 1000 $result = $this->_compare_conflict_seq('orig', 'final1'); 1001 1002 if (!$result['merge_found']) 1003 { 1004 return; 1005 } 1006 1007 // First of all, try to find the code in orig in final1. ;) 1008 $compare_seq = sizeof($this->orig); 1009 $begin = $end = -1; 1010 $j = 0; 1011 1012 for ($i = 0, $size = sizeof($this->final1); $i < $size; $i++) 1013 { 1014 $line = $this->final1[$i]; 1015 1016 if (trim($line) === trim($this->orig[$j])) 1017 { 1018 // Mark begin 1019 if ($begin === -1) 1020 { 1021 $begin = $i; 1022 } 1023 1024 // End is always $i, the last found line 1025 $end = $i; 1026 1027 if (isset($this->orig[$j+1])) 1028 { 1029 $j++; 1030 } 1031 } 1032 } 1033 1034 if ($begin !== -1 && $begin + ($compare_seq - 1) == $end) 1035 { 1036 foreach ($this->final1 as $i => $line) 1037 { 1038 if ($i < $begin || $i > $end) 1039 { 1040 $merged[] = $line; 1041 } 1042 } 1043 1044 $this->final2 = $merged; 1045 $this->_merged = &$this->final2; 1046 } 1047 1048 return; 1049 } 1050 1051 return; 686 1052 } 687 1053 }
Note:
See TracChangeset
for help on using the changeset viewer.