Ignore:
Timestamp:
Mar 31, 2010, 6:32:40 PM (14 years ago)
Author:
george
Message:
  • Upraveno: Aktualizace fóra.
Location:
trunk/forum/includes/diff
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/forum/includes/diff/diff.php

    r400 r702  
    33*
    44* @package diff
    5 * @version $Id: diff.php 8765 2008-08-16 22:18:25Z aptx $
     5* @version $Id$
    66* @copyright (c) 2006 phpBB Group
    77* @license http://opensource.org/licenses/gpl-license.php GNU Public License
     
    1818
    1919/**
    20 * Code from pear.php.net, Text_Diff-1.0.0 package
     20* Code from pear.php.net, Text_Diff-1.1.0 package
    2121* http://pear.php.net/package/Text_Diff/
    2222*
     
    5959        {
    6060                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;
    61107        }
    62108
     
    87133                $rev->_edits = array();
    88134
    89                 foreach ($this->_edits as $edit)
    90                 {
     135                for ($i = 0, $size = sizeof($this->_edits); $i < $size; $i++)
     136                {
     137                        $edit = $this->_edits[$i];
    91138                        $rev->_edits[] = $edit->reverse();
    92139                }
     
    102149        function is_empty()
    103150        {
    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
    108175                                return false;
    109176                        }
    110                 }
     177
     178                        return false;
     179                }
     180
    111181                return true;
    112182        }
     
    123193                $lcs = 0;
    124194
    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
    127199                        if (is_a($edit, 'diff_op_copy'))
    128200                        {
     
    144216                $lines = array();
    145217
    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
    148222                        if ($edit->orig)
    149223                        {
     
    165239                $lines = array();
    166240
    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
    169245                        if ($edit->final)
    170246                        {
     
    217293                $prevtype = null;
    218294
    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
    221299                        if ($prevtype == get_class($edit))
    222300                        {
     
    415493        * @param array $final2  The second version to compare to.
    416494        */
    417         function diff3(&$orig, &$final1, &$final2)
     495        function diff3(&$orig, &$final1, &$final2, $preserve_cr = true)
    418496        {
    419497                $diff_engine = new diff_engine();
    420498
    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);
    425503
    426504                $this->_edits = $this->_diff3($diff_1, $diff_2);
     
    428506
    429507        /**
    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.
    431530        *
    432531        * @param string $label1 the cvs file version/label from the original set of lines
    433532        * @param string $label2 the cvs file version/label from the new set of lines
    434533        * @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 returned
    436         * @param bool $merge_new if set to true the merged output will have the new file contents on a conflicting merge
    437534        *
    438535        * @return mixed the merged output
    439536        */
    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')
    441538        {
    442539                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                 }
    456540
    457541                $label1 = (!empty($user->lang[$label1])) ? $user->lang[$label1] : $label1;
     
    461545                $lines = array();
    462546
    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
    465551                        if ($edit->is_conflict())
    466552                        {
    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);
    475559                                $this->_conflicting_blocks++;
    476560                        }
     
    485569
    486570        /**
     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        /**
    487581        * Merge the output and use the new file code for conflicts
    488582        */
     
    491585                $lines = array();
    492586
    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
    495591                        if ($edit->is_conflict())
    496592                        {
     
    513609                $lines = array();
    514610
    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
    517615                        if ($edit->is_conflict())
    518616                        {
     
    535633                $conflicts = array();
    536634
    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
    539639                        if ($edit->is_conflict())
    540640                        {
     
    660760                if (!isset($this->_merged))
    661761                {
     762                        // Prepare the arrays before we compare them. ;)
     763                        $this->solve_prepare();
     764
    662765                        if ($this->final1 === $this->final2)
    663766                        {
     
    674777                        else
    675778                        {
     779                                // The following tries to aggressively solve conflicts...
    676780                                $this->_merged = false;
     781                                $this->solve_conflict();
    677782                        }
    678783                }
     
    684789        {
    685790                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;
    6861052        }
    6871053}
  • trunk/forum/includes/diff/engine.php

    r400 r702  
    33*
    44* @package diff
    5 * @version $Id: engine.php 8765 2008-08-16 22:18:25Z aptx $
     5* @version $Id$
    66* @copyright (c) 2006 phpBB Group
    77* @license http://opensource.org/licenses/gpl-license.php GNU Public License
     
    1818
    1919/**
    20 * Code from pear.php.net, Text_Diff-1.0.0 package
     20* Code from pear.php.net, Text_Diff-1.1.0 package
    2121* http://pear.php.net/package/Text_Diff/ (native engine)
    2222*
     
    5050class diff_engine
    5151{
     52        /**
     53        * If set to true we trim all lines before we compare them. This ensures that sole space/tab changes do not trigger diffs.
     54        */
     55        var $skip_whitespace_changes = true;
     56
    5257        function diff(&$from_lines, &$to_lines, $preserve_cr = true)
    5358        {
     
    8691                for ($skip = 0; $skip < $n_from && $skip < $n_to; $skip++)
    8792                {
    88                         if ($from_lines[$skip] !== $to_lines[$skip])
     93                        if (trim($from_lines[$skip]) !== trim($to_lines[$skip]))
    8994                        {
    9095                                break;
     
    99104                for ($endskip = 0; --$xi > $skip && --$yi > $skip; $endskip++)
    100105                {
    101                         if ($from_lines[$xi] !== $to_lines[$yi])
     106                        if (trim($from_lines[$xi]) !== trim($to_lines[$yi]))
    102107                        {
    103108                                break;
     
    109114                for ($xi = $skip; $xi < $n_from - $endskip; $xi++)
    110115                {
    111                         $xhash[$from_lines[$xi]] = 1;
     116                        if ($this->skip_whitespace_changes) $xhash[trim($from_lines[$xi])] = 1; else $xhash[$from_lines[$xi]] = 1;
    112117                }
    113118
    114119                for ($yi = $skip; $yi < $n_to - $endskip; $yi++)
    115120                {
    116                         $line = $to_lines[$yi];
     121                        $line = ($this->skip_whitespace_changes) ? trim($to_lines[$yi]) : $to_lines[$yi];
    117122
    118123                        if (($this->ychanged[$yi] = empty($xhash[$line])))
     
    127132                for ($xi = $skip; $xi < $n_from - $endskip; $xi++)
    128133                {
    129                         $line = $from_lines[$xi];
     134                        $line = ($this->skip_whitespace_changes) ? trim($from_lines[$xi]) : $from_lines[$xi];
    130135
    131136                        if (($this->xchanged[$xi] = empty($yhash[$line])))
     
    141146
    142147                // Merge edits when possible.
    143                 $this->_shift_boundaries($from_lines, $this->xchanged, $this->ychanged);
    144                 $this->_shift_boundaries($to_lines, $this->ychanged, $this->xchanged);
     148                if ($this->skip_whitespace_changes)
     149                {
     150                        $from_lines_clean = array_map('trim', $from_lines);
     151                        $to_lines_clean = array_map('trim', $to_lines);
     152
     153                        $this->_shift_boundaries($from_lines_clean, $this->xchanged, $this->ychanged);
     154                        $this->_shift_boundaries($to_lines_clean, $this->ychanged, $this->xchanged);
     155
     156                        unset($from_lines_clean, $to_lines_clean);
     157                }
     158                else
     159                {
     160                        $this->_shift_boundaries($from_lines, $this->xchanged, $this->ychanged);
     161                        $this->_shift_boundaries($to_lines, $this->ychanged, $this->xchanged);
     162                }
    145163
    146164                // Compute the edit operations.
  • trunk/forum/includes/diff/renderer.php

    r400 r702  
    33*
    44* @package diff
    5 * @version $Id: renderer.php 8766 2008-08-16 22:24:54Z aptx $
     5* @version $Id$
    66* @copyright (c) 2006 phpBB Group
    77* @license http://opensource.org/licenses/gpl-license.php GNU Public License
     
    1818
    1919/**
    20 * Code from pear.php.net, Text_Diff-1.0.0 package
     20* Code from pear.php.net, Text_Diff-1.1.0 package
    2121* http://pear.php.net/package/Text_Diff/
    2222*
     
    537537        function get_diff_content($diff)
    538538        {
    539                 return '<textarea style="height: 290px;" class="full">' . htmlspecialchars($this->render($diff)) . '</textarea>';
     539                return '<textarea style="height: 290px;" rows="15" cols="76" class="full">' . htmlspecialchars($this->render($diff)) . '</textarea>';
    540540        }
    541541
Note: See TracChangeset for help on using the changeset viewer.