Edgewall Software

Ticket #2028: trac-diff-for-0.9.3.patch

File trac-diff-for-0.9.3.patch, 50.7 KB (added by jeremie.corbier@…, 3 years ago)

Adds the TracDiff features on top of the 0.9.3

  • htdocs/css/browser.css

    Seulement dans trac-0.9.3-trac-diff: build
    diff -ru trac-0.9.3/htdocs/css/browser.css trac-0.9.3-trac-diff/htdocs/css/browser.css
    old new  
    4646#dirlist td.rev { text-align: right } 
    4747#dirlist td.change { font-size: 85%; vertical-align: middle; white-space: nowrap } 
    4848 
     49/* Log */ 
     50tr.diff input {  
     51 padding: 0 1em 0 1em; 
     52 margin: 0;  
     53} 
     54 
     55div.buttons { 
     56 clear: left; 
     57} 
     58 
     59#anydiff { 
     60 margin: 0 0 1em; 
     61 float: left; 
     62} 
     63#anydiff form, #anydiff div, #anydiff h2 { 
     64 display: inline; 
     65} 
     66#anydiff input {  
     67 vertical-align: baseline; 
     68 margin: 0 -0.5em 0 1em; 
     69} 
     70 
     71 
    4972/* Styles for the revision log table 
    5073   (extends the styles for "table.listing") */ 
    5174#chglist { margin-top: 0 } 
  • htdocs/css/changeset.css

    diff -ru trac-0.9.3/htdocs/css/changeset.css trac-0.9.3-trac-diff/htdocs/css/changeset.css
    old new  
    2626 
    2727.diff ul.props { font-size: 90%; list-style: disc; margin: .5em 0 0; padding: 0 .5em 1em 2em } 
    2828.diff ul.props li { margin: 0; padding: 0 } 
     29 
     30 
     31#title dl { 
     32 display: inline; 
     33 font-size: 110% 
     34} 
     35#title dt {  
     36  font-size: 110%; 
     37  font-weight: bold; 
     38  display: inline;  
     39  margin-left: 3em; 
     40} 
     41#title dd {  
     42  display: inline; 
     43  margin-left: 0.4em; 
     44} 
  • templates/browser.cs

    Seulement dans trac-0.9.3-trac-diff/templates: anydiff.cs
    diff -ru trac-0.9.3/templates/browser.cs trac-0.9.3-trac-diff/templates/browser.cs
    old new  
    33 
    44<div id="ctxtnav" class="nav"> 
    55 <ul> 
    6   <li class="last"><a href="<?cs var:browser.log_href ?>">Revision Log</a></li> 
     6  <li class="first"><a href="<?cs var:browser.restr_changeset_href ?>"> 
     7   Last Change</a></li> 
     8  <li class="last"><a href="<?cs var:browser.log_href ?>"> 
     9   Revision Log</a></li> 
    710 </ul> 
    811</div> 
    912 
     13 
    1014<div id="content" class="browser"> 
    1115 <h1><?cs call:browser_path_links(browser.path, browser) ?></h1> 
    1216 
    1317 <div id="jumprev"> 
    14   <form action="" method="get"><div> 
    15    <label for="rev">View revision:</label> 
    16    <input type="text" id="rev" name="rev" value="<?cs 
    17      var:browser.revision ?>" size="4" /> 
    18   </div></form> 
     18  <form action="" method="get"> 
     19   <div> 
     20    <label for="rev">View revision:</label> 
     21    <input type="text" id="rev" name="rev" value="<?cs 
     22       var:browser.revision ?>" size="4" /> 
     23   </div> 
     24  </form> 
    1925 </div> 
    2026 
    2127 <?cs if:browser.is_dir ?> 
     
    114120  ?>/TracBrowser">TracBrowser</a> for help on using the browser. 
    115121 </div> 
    116122 
     123  <div id="anydiff"><?cs 
     124   if len(browser.path) > #1 ?> 
     125    <form action="<?cs var:browser.anydiff_href ?>" method="get"> 
     126     <input type="hidden" name="new_path" value="<?cs var:browser.path ?>" /> 
     127     <input type="hidden" name="old_path" value="<?cs var:browser.path ?>" /> 
     128     <input type="hidden" name="new_rev" value="<?cs var:browser.revision ?>" /> 
     129     <input type="hidden" name="old_rev" value="<?cs var:browser.revision ?>" /> 
     130     <div class="buttons"> 
     131      <input type="submit" value="View changes..." title="Prepare an Arbitrary Diff" /> 
     132     </div> 
     133    </form><?cs 
     134   /if ?> 
     135  </div> 
     136 
    117137</div> 
    118138<?cs include:"footer.cs"?> 
  • templates/changeset.cs

    diff -ru trac-0.9.3/templates/changeset.cs trac-0.9.3-trac-diff/templates/changeset.cs
    old new  
    1 <?cs include "header.cs"?> 
    2 <?cs include "macros.cs"?> 
    3  
    4 <div id="ctxtnav" class="nav"> 
    5  <h2>Changeset Navigation</h2><?cs 
    6  with:links = chrome.links ?> 
    7   <ul><?cs 
    8    if:len(links.prev) ?> 
    9     <li class="first<?cs if:!len(links.next) ?> last<?cs /if ?>"> 
    10      &larr; <a class="prev" href="<?cs var:links.prev.0.href ?>" title="<?cs 
    11        var:links.prev.0.title ?>">Previous Changeset</a> 
    12     </li><?cs 
    13    /if ?><?cs 
    14    if:len(links.next) ?> 
    15     <li class="<?cs if:len(links.prev) ?>first <?cs /if ?>last"> 
    16      <a class="next" href="<?cs var:links.next.0.href ?>" title="<?cs 
    17        var:links.next.0.title ?>">Next Changeset</a> &rarr; 
    18     </li><?cs 
    19    /if ?> 
    20   </ul><?cs 
    21  /with ?> 
    22 </div> 
    23  
    24 <div id="content" class="changeset"> 
    25 <h1>Changeset <?cs var:changeset.revision ?></h1> 
    26  
    27 <?cs each:change = changeset.changes ?><?cs 
    28  if:len(change.diff) ?><?cs 
    29   set:has_diffs = 1 ?><?cs 
    30  /if ?><?cs 
    31 /each ?><?cs if:has_diffs || diff.options.ignoreblanklines  
    32   || diff.options.ignorecase || diff.options.ignorewhitespace ?> 
    33 <form method="post" id="prefs" action=""> 
    34  <div> 
    35   <label for="style">View differences</label> 
    36   <select id="style" name="style"> 
    37    <option value="inline"<?cs 
    38      if:diff.style == 'inline' ?> selected="selected"<?cs 
    39      /if ?>>inline</option> 
    40    <option value="sidebyside"<?cs 
    41      if:diff.style == 'sidebyside' ?> selected="selected"<?cs 
    42      /if ?>>side by side</option> 
    43   </select> 
    44   <div class="field"> 
    45    Show <input type="text" name="contextlines" id="contextlines" size="2" 
    46      maxlength="3" value="<?cs var:diff.options.contextlines ?>" /> 
    47    <label for="contextlines">lines around each change</label> 
    48   </div> 
    49   <fieldset id="ignore"> 
    50    <legend>Ignore:</legend> 
    51    <div class="field"> 
    52     <input type="checkbox" id="blanklines" name="ignoreblanklines"<?cs 
    53       if:diff.options.ignoreblanklines ?> checked="checked"<?cs /if ?> /> 
    54     <label for="blanklines">Blank lines</label> 
    55    </div> 
    56    <div class="field"> 
    57     <input type="checkbox" id="case" name="ignorecase"<?cs 
    58       if:diff.options.ignorecase ?> checked="checked"<?cs /if ?> /> 
    59     <label for="case">Case changes</label> 
    60    </div> 
    61    <div class="field"> 
    62     <input type="checkbox" id="whitespace" name="ignorewhitespace"<?cs 
    63       if:diff.options.ignorewhitespace ?> checked="checked"<?cs /if ?> /> 
    64     <label for="whitespace">White space changes</label> 
    65    </div> 
    66   </fieldset> 
    67   <div class="buttons"> 
    68    <input type="submit" name="update" value="Update" /> 
    69   </div> 
    70  </div> 
    71 </form><?cs /if ?> 
    72  
    73 <?cs def:node_change(item,cl,kind) ?><?cs  
    74   set:ndiffs = len(item.diff) ?><?cs 
    75   set:nprops = len(item.props) ?> 
    76   <div class="<?cs var:cl ?>"></div><?cs  
    77   if:cl == "rem" ?> 
    78    <a title="Show what was removed (rev. <?cs var:item.rev.old ?>)" href="<?cs 
    79      var:item.browser_href.old ?>"><?cs var:item.path.old ?></a><?cs 
    80   else ?> 
    81    <a title="Show entry in browser" href="<?cs 
    82      var:item.browser_href.new ?>"><?cs var:item.path.new ?></a><?cs 
    83   /if ?> 
    84   <span class="comment">(<?cs var:kind ?>)</span><?cs 
    85   if:item.path.old && item.change == 'copy' || item.change == 'move' ?> 
    86    <small><em>(<?cs var:kind ?> from <a href="<?cs 
    87     var:item.browser_href.old ?>" title="Show original file (rev. <?cs 
    88     var:item.rev.old ?>)"><?cs var:item.path.old ?></a>)</em></small><?cs 
    89   /if ?><?cs 
    90   if:$ndiffs + $nprops > #0 ?> 
    91     (<a href="#file<?cs var:name(item) ?>" title="Show differences"><?cs 
    92       if:$ndiffs > #0 ?><?cs var:ndiffs ?>&nbsp;diff<?cs if:$ndiffs > #1 ?>s<?cs /if ?><?cs  
    93       /if ?><?cs 
    94       if:$ndiffs && $nprops ?>, <?cs /if ?><?cs  
    95       if:$nprops > #0 ?><?cs var:nprops ?>&nbsp;prop<?cs if:$nprops > #1 ?>s<?cs /if ?><?cs 
    96       /if ?></a>)<?cs 
    97   elif:cl == "mod" ?> 
    98     (<a href="<?cs var:item.browser_href.old ?>" 
    99         title="Show previous version in browser">previous</a>)<?cs 
    100   /if ?> 
    101 <?cs /def ?> 
    102  
    103 <dl id="overview"> 
    104  <dt class="time">Timestamp:</dt> 
    105  <dd class="time"><?cs var:changeset.time ?></dd> 
    106  <dt class="author">Author:</dt> 
    107  <dd class="author"><?cs var:changeset.author ?></dd> 
    108  <dt class="message">Message:</dt> 
    109  <dd class="message" id="searchable"><?cs 
    110   alt:changeset.message ?>&nbsp;<?cs /alt ?></dd> 
    111  <dt class="files">Files:</dt> 
    112  <dd class="files"> 
    113   <ul><?cs each:item = changeset.changes ?> 
    114    <li><?cs 
    115     if:item.change == 'add' ?><?cs 
    116      call:node_change(item, 'add', 'added') ?><?cs 
    117     elif:item.change == 'delete' ?><?cs 
    118      call:node_change(item, 'rem', 'deleted') ?><?cs 
    119     elif:item.change == 'copy' ?><?cs 
    120      call:node_change(item, 'cp', 'copied') ?><?cs 
    121     elif:item.change == 'move' ?><?cs 
    122      call:node_change(item, 'mv', 'moved') ?><?cs 
    123     elif:item.change == 'edit' ?><?cs 
    124      call:node_change(item, 'mod', 'modified') ?><?cs 
    125     /if ?> 
    126    </li> 
    127   <?cs /each ?></ul> 
    128  </dd> 
    129 </dl> 
    130  
    131 <div class="diff"> 
    132  <div id="legend"> 
    133   <h3>Legend:</h3> 
    134   <dl> 
    135    <dt class="unmod"></dt><dd>Unmodified</dd> 
    136    <dt class="add"></dt><dd>Added</dd> 
    137    <dt class="rem"></dt><dd>Removed</dd> 
    138    <dt class="mod"></dt><dd>Modified</dd> 
    139    <dt class="cp"></dt><dd>Copied</dd> 
    140    <dt class="mv"></dt><dd>Moved</dd> 
    141   </dl> 
    142  </div> 
    143  <ul class="entries"><?cs 
    144  each:item = changeset.changes ?><?cs 
    145   if:len(item.diff) || len(item.props) ?><li class="entry" id="file<?cs 
    146    var:name(item) ?>"><h2><a href="<?cs 
    147    var:item.browser_href.new ?>" title="Show new revision <?cs 
    148    var:item.rev.new ?> of this file in browser"><?cs 
    149    var:item.path.new ?></a></h2><?cs 
    150    if:len(item.props) ?><ul class="props"><?cs 
    151     each:prop = item.props ?><li>Property <strong><?cs 
    152      var:name(prop) ?></strong> <?cs 
    153      if:prop.old && prop.new ?>changed from <?cs 
    154      elif:!prop.old ?>set<?cs 
    155      else ?>deleted<?cs 
    156      /if ?><?cs 
    157      if:prop.old && prop.new ?><em><tt><?cs var:prop.old ?></tt></em><?cs /if ?><?cs 
    158      if:prop.new ?> to <em><tt><?cs var:prop.new ?></tt></em><?cs /if ?></li><?cs 
    159     /each ?></ul><?cs 
    160    /if ?><?cs 
    161    if:len(item.diff) ?><table class="<?cs 
    162     var:diff.style ?>" summary="Differences" cellspacing="0"><?cs 
    163     if:diff.style == 'sidebyside' ?> 
    164      <colgroup class="l"><col class="lineno" /><col class="content" /></colgroup> 
    165      <colgroup class="r"><col class="lineno" /><col class="content" /></colgroup> 
    166      <thead><tr> 
    167       <th colspan="2"><a href="<?cs 
    168        var:item.browser_href.old ?>" title="Show old rev. <?cs 
    169        var:item.rev.old ?> of <?cs var:item.path.old ?>">Revision <?cs 
    170        var:item.rev.old ?></a></th> 
    171       <th colspan="2"><a href="<?cs 
    172        var:item.browser_href.new ?>" title="Show new rev. <?cs 
    173        var:item.rev.new ?> of <?cs var:item.path.new ?>">Revision <?cs 
    174        var:item.rev.new ?></a></th> 
    175       </tr> 
    176      </thead><?cs 
    177      each:change = item.diff ?><tbody><?cs 
    178       call:diff_display(change, diff.style) ?></tbody><?cs 
    179       if:name(change) < len(item.diff) - 1 ?><tbody class="skipped"><tr> 
    180        <th>&hellip;</th><td>&nbsp;</td><th>&hellip;</th><td>&nbsp;</td> 
    181       </tr></tbody><?cs /if ?><?cs 
    182      /each ?><?cs 
    183     else ?> 
    184      <colgroup><col class="lineno" /><col class="lineno" /><col class="content" /></colgroup> 
    185      <thead><tr> 
    186       <th title="Revision <?cs var:item.rev.old ?>"><a href="<?cs 
    187        var:item.browser_href.old ?>" title="Show old version of <?cs 
    188        var:item.path.old ?>">r<?cs var:item.rev.old ?></a></th> 
    189       <th title="Revision <?cs var:item.rev.new ?>"><a href="<?cs 
    190        var:item.browser_href.new ?>" title="Show new version of <?cs 
    191        var:item.path.new ?>">r<?cs var:item.rev.new ?></a></th> 
    192       <th>&nbsp;</th></tr> 
    193      </thead><?cs 
    194      each:change = item.diff ?><?cs 
    195       call:diff_display(change, diff.style) ?><?cs 
    196       if:name(change) < len(item.diff) - 1 ?><tbody class="skipped"><tr> 
    197        <th>&hellip;</th><th>&hellip;</th><td>&nbsp;</td> 
    198       </tr></tbody><?cs /if ?><?cs 
    199      /each ?><?cs 
    200     /if ?></table><?cs 
    201    /if ?></li><?cs 
    202   /if ?><?cs 
    203  /each ?></ul> 
    204 </div> 
    205  
    206 </div> 
    207 <?cs include "footer.cs"?> 
  • templates/log.cs

    Seulement dans trac-0.9.3-trac-diff/templates: diff.cs
    diff -ru trac-0.9.3/templates/log.cs trac-0.9.3-trac-diff/templates/log.cs
    old new  
    33 
    44<div id="ctxtnav" class="nav"> 
    55 <ul> 
    6   <li class="last"><a href="<?cs 
    7     var:log.browser_href ?>">View Latest Revision</a></li><?cs 
     6  <li class="last"> 
     7   <a href="<?cs var:log.browser_href ?>">View Latest Revision</a> 
     8  </li><?cs 
    89  if:len(chrome.links.prev) ?> 
    910   <li class="first<?cs if:!len(chrome.links.next) ?> last<?cs /if ?>"> 
    1011    &larr; <a href="<?cs var:chrome.links.prev.0.href ?>" title="<?cs 
     
    6162          title="Warning: by updating, you will clear the page history" /> 
    6263  </div> 
    6364 </form> 
     65 
    6466 <div class="diff"> 
    6567  <div id="legend"> 
    6668   <h3>Legend:</h3> 
     
    7476   </dl> 
    7577  </div> 
    7678 </div> 
     79 
     80 <form action="<?cs var:log.href ?>" method="post"> 
     81  <div class="buttons"><input type="submit" value="View changes"  
     82       title="Diff from Old Revision to New Revision (select them below)" /> 
     83 </div> 
    7784 <table id="chglist" class="listing"> 
    7885  <thead> 
    7986   <tr> 
     87    <th>Old</th> 
     88    <th>New</th> 
    8089    <th class="change"></th> 
    8190    <th class="data">Date</th> 
    8291    <th class="rev">Rev</th> 
     
    8796  </thead> 
    8897  <tbody><?cs 
    8998   set:indent = #1 ?><?cs 
     99   set:idx = #0 ?><?cs 
    90100   each:item = log.items ?><?cs 
    91101    if:item.copyfrom_path ?> 
    92102     <tr class="<?cs if:name(item) % #2 ?>even<?cs else ?>odd<?cs /if ?>"> 
    93       <td class="copyfrom_path" colspan="6" style="padding-left: <?cs var:indent ?>em"> 
     103      <td class="copyfrom_path" colspan="8" style="padding-left: <?cs var:indent ?>em"> 
    94104       copied from <a href="<?cs var:item.browser_href ?>"?><?cs var:item.copyfrom_path ?></a>: 
    95105      </td> 
    96106     </tr><?cs 
     
    99109      set:indent = #1 ?><?cs 
    100110    /if ?> 
    101111    <tr class="<?cs if:name(item) % #2 ?>even<?cs else ?>odd<?cs /if ?>"> 
     112     <td><input type="radio" name="old"  
     113                value="<?cs var:item.path ?>#<?cs var:item.rev ?>" <?cs 
     114          if:idx == #1 ?> checked="checked" <?cs /if ?> /></td> 
     115     <td><input type="radio" name="new"  
     116                value="<?cs var:item.path ?>#<?cs var:item.rev ?>" <?cs 
     117          if:idx == #0 ?> checked="checked" <?cs /if ?> /></td> 
    102118     <td class="change" style="padding-left:<?cs var:indent ?>em"> 
    103119      <a title="View log starting at this revision" href="<?cs var:item.log_href ?>"> 
    104120       <span class="<?cs var:item.change ?>"></span> 
     
    117133     <td class="author"><?cs var:log.changes[item.rev].author ?></td> 
    118134     <td class="summary"><?cs var:log.changes[item.rev].message ?></td> 
    119135    </tr><?cs 
     136    set:idx = idx + 1 ?><?cs 
    120137   /each ?> 
    121138  </tbody> 
    122  </table><?cs 
     139 </table> 
     140 <div class="buttons"><input type="submit" value="View changes"  
     141      title="Diff from Old Revision to New Revision (select them above)" /> 
     142 </div> 
     143 </form><?cs 
    123144 if:len(links.prev) || len(links.next) ?><div id="paging" class="nav"><ul><?cs 
    124145  if:len(links.prev) ?><li class="first<?cs 
    125146   if:!len(links.next) ?> last<?cs /if ?>">&larr; <a href="<?cs 
  • templates/wiki.cs

    diff -ru trac-0.9.3/templates/wiki.cs trac-0.9.3-trac-diff/templates/wiki.cs
    old new  
    154154    var:wiki.page_name ?></a></h1> 
    155155  <?cs if:len(wiki.history) ?><form method="get" action=""> 
    156156   <input type="hidden" name="action" value="diff" /> 
     157   <div class="buttons"> 
     158    <input type="submit" value="View changes" /> 
     159   </div> 
    157160   <table id="wikihist" class="listing" summary="Change history"> 
    158161    <thead><tr> 
    159162     <th class="diff"></th> 
  • trac/__init__.py

    diff -ru trac-0.9.3/trac/__init__.py trac-0.9.3-trac-diff/trac/__init__.py
    old new  
    1010""" 
    1111__docformat__ = 'epytext en' 
    1212 
    13 __version__ = '0.9.3' 
     13__version__ = '0.9.3-trac-diff' 
    1414__url__ = 'http://trac.edgewall.com/' 
    1515__copyright__ = '(C) 2003-2006 Edgewall Software' 
    1616__license__ = 'BSD' 
  • trac/siteconfig.py

    Seulement dans trac-0.9.3-trac-diff/trac: __init__.pyc
    diff -ru trac-0.9.3/trac/siteconfig.py trac-0.9.3-trac-diff/trac/siteconfig.py
    old new  
    11 
    22# PLEASE DO NOT EDIT THIS FILE! 
    3 # This file was autogenerated when installing Trac 0.9.2. 
     3# This file was autogenerated when installing Trac 0.9.3-trac-diff. 
    44# 
    55__default_conf_dir__ = '/usr/share/trac/conf' 
    66__default_templates_dir__ = '/usr/share/trac/templates' 
  • trac/versioncontrol/api.py

    diff -ru trac-0.9.3/trac/versioncontrol/api.py trac-0.9.3-trac-diff/trac/versioncontrol/api.py
    old new  
    122122        'None' is a valid revision value and represents the youngest revision. 
    123123        """ 
    124124        return NotImplementedError 
    125          
     125 
     126    def get_changes(self, old_path, old_rev, new_path, new_rev, ignore_ancestry=1): 
     127        """ 
     128        Generator that yields change tuples (old_node, new_node, kind, change) 
     129        for each node change between the two arbitrary (path,rev) pairs. 
     130 
     131        The old_node is assumed to be None when the change is an ADD, 
     132        the new_node is assumed to be None when the change is a DELETE. 
     133        """ 
     134        raise NotImplementedError 
     135 
    126136 
    127137class Node(object): 
    128138    """ 
     
    164174        """ 
    165175        raise NotImplementedError 
    166176 
     177    def get_previous(self): 
     178        """ 
     179        Return the (path, rev, chg) tuple corresponding to the previous 
     180        revision for that node. 
     181        """ 
     182        skip = True 
     183        for p in self.get_history(2): 
     184            if skip: 
     185                skip = False 
     186            else: 
     187                return p 
     188 
    167189    def get_properties(self): 
    168190        """ 
    169191        Returns a dictionary containing the properties (meta-data) of the node. 
  • trac/versioncontrol/cache.py

    diff -ru trac-0.9.3/trac/versioncontrol/cache.py trac-0.9.3-trac-diff/trac/versioncontrol/cache.py
    old new  
    129129    def normalize_rev(self, rev): 
    130130        return self.repos.normalize_rev(rev) 
    131131 
     132    def get_changes(self, old_path, old_rev, new_path, new_rev, ignore_ancestry=1): 
     133        return self.repos.get_changes(old_path, old_rev, new_path, new_rev, ignore_ancestry) 
     134 
    132135 
    133136class CachedChangeset(Changeset): 
    134137 
  • trac/versioncontrol/diff.py

    diff -ru trac-0.9.3/trac/versioncontrol/diff.py trac-0.9.3-trac-diff/trac/versioncontrol/diff.py
    old new  
    217217                           ignore_space_changes) 
    218218    for group in _group_opcodes(opcodes, context): 
    219219        i1, i2, j1, j2 = group[0][1], group[-1][2], group[0][3], group[-1][4] 
     220        if i1 == 0 and i2 == 0: 
     221            i1, i2 = -1, -1 # support for 'A'dd changes 
    220222        yield '@@ -%d,%d +%d,%d @@' % (i1 + 1, i2 - i1, j1 + 1, j2 - j1) 
    221223        for tag, i1, i2, j1, j2 in group: 
    222224            if tag == 'equal': 
  • trac/versioncontrol/svn_fs.py

    diff -ru trac-0.9.3/trac/versioncontrol/svn_fs.py trac-0.9.3-trac-diff/trac/versioncontrol/svn_fs.py
    old new  
    346346                expect_deletion = True 
    347347                rev = self.previous_rev(rev) 
    348348 
     349    def get_changes(self, old_path, old_rev, new_path, new_rev, 
     350                   ignore_ancestry=0): 
     351        old_node = new_node = None 
     352        old_rev = self.normalize_rev(old_rev) 
     353        new_rev = self.normalize_rev(new_rev) 
     354        if self.has_node(old_path, old_rev): 
     355            old_node = self.get_node(old_path, old_rev) 
     356        else: 
     357            raise TracError, ('The Base for Diff is invalid: path %s' 
     358                              ' doesn\'t exist in revision %s' \ 
     359                              % (old_path, old_rev)) 
     360        if self.has_node(new_path, new_rev): 
     361            new_node = self.get_node(new_path, new_rev) 
     362        else: 
     363            raise TracError, ('The Target for Diff is invalid: path %s' 
     364                              ' doesn\'t exist in revision %s' \ 
     365                              % (new_path, new_rev)) 
     366        if new_node.kind != old_node.kind: 
     367            raise TracError, ('Diff mismatch: Base is a %s (%s in revision %s) ' 
     368                              'and Target is a %s (%s in revision %s).' \ 
     369                              % (old_node.kind, old_path, old_rev, 
     370                                 new_node.kind, new_path, new_rev)) 
     371        subpool = Pool(self.pool) 
     372        if new_node.isdir: 
     373            editor = DiffChangeEditor() 
     374            e_ptr, e_baton = delta.make_editor(editor, subpool()) 
     375            old_root = fs.revision_root(self.fs_ptr, old_rev, subpool()) 
     376            new_root = fs.revision_root(self.fs_ptr, new_rev, subpool()) 
     377            def authz_cb(root, path, pool): return 1 
     378            text_deltas = 0 # as this is anyway re-done in Diff.py... 
     379            entry_props = 0 # "... typically used only for working copy updates" 
     380            repos.svn_repos_dir_delta(old_root, 
     381                                      (self.scope + old_path).strip('/'), '', 
     382                                      new_root, 
     383                                      (self.scope + new_path).strip('/'), 
     384                                      e_ptr, e_baton, authz_cb, 
     385                                      text_deltas, 
     386                                      1, # directory 
     387                                      entry_props, 
     388                                      ignore_ancestry, 
     389                                      subpool()) 
     390            for path, kind, change in editor.deltas: 
     391                old_node = new_node = None 
     392                if change != Changeset.ADD: 
     393                    old_node = self.get_node(posixpath.join(old_path, path), 
     394                                             old_rev) 
     395                if change != Changeset.DELETE: 
     396                    new_node = self.get_node(posixpath.join(new_path, path), 
     397                                             new_rev) 
     398                else: 
     399                    kind = _kindmap[fs.check_path(old_root, 
     400                                                  self.scope + old_node.path, 
     401                                                  subpool())] 
     402                yield  (old_node, new_node, kind, change) 
     403        else: 
     404            old_root = fs.revision_root(self.fs_ptr, old_rev, subpool()) 
     405            new_root = fs.revision_root(self.fs_ptr, new_rev, subpool()) 
     406            if fs.contents_changed(old_root, self.scope + old_path, 
     407                                   new_root, self.scope + new_path, 
     408                                   subpool()): 
     409                yield (old_node, new_node, Node.FILE, Changeset.EDIT) 
     410 
    349411 
    350412class SubversionNode(Node): 
    351413 
     
    368430                                               self.pool()) 
    369431        self.created_path = fs.node_created_path(self.root, self.scoped_path, 
    370432                                                 self.pool()) 
    371         # 'created_path' differs from 'path' if the last operation is a copy, 
    372         # and furthermore, 'path' might not exist at 'create_rev' 
     433        # Note: 'created_path' differs from 'path' if the last change was a copy, 
     434        #        and furthermore, 'path' might not exist at 'create_rev'. 
     435        #        The only guarantees are: 
     436        #          * this node exists at (path,rev) 
     437        #          * the node existed at (created_path,created_rev) 
     438        # TODO: check node id 
    373439        self.rev = self.created_rev 
    374440         
    375441        Node.__init__(self, path, self.rev, _kindmap[node_type]) 
     
    414480        if newer: 
    415481            yield newer 
    416482 
     483#    def get_previous(self): 
     484#        # FIXME: redo it with fs.node_history 
     485 
    417486    def get_properties(self): 
    418487        props = fs.node_proplist(self.root, self.scoped_path, self.pool()) 
    419488        for name,value in props.items(): 
     
    513582 
    514583    def _get_prop(self, name): 
    515584        return fs.revision_prop(self.fs_ptr, self.rev, name, self.pool()) 
     585 
     586 
     587# 
     588# Delta editor for diffs between arbitrary nodes 
     589# 
     590# Note 1: the 'copyfrom_path' and 'copyfrom_rev' information is not used 
     591#         because 'repos.svn_repos_dir_delta' *doesn't* provide it. 
     592# 
     593# Note 2: the 'dir_baton' is the path of the parent directory 
     594# 
     595 
     596class DiffChangeEditor(delta.Editor):  
     597 
     598    def __init__(self): 
     599        self.deltas = [] 
     600     
     601    # -- svn.delta.Editor callbacks 
     602 
     603    def open_root(self, base_revision, dir_pool): 
     604        return ('/', Changeset.EDIT) 
     605 
     606    def add_directory(self, path, dir_baton, copyfrom_path, copyfrom_rev, 
     607                      dir_pool): 
     608        self.deltas.append((path, Node.DIRECTORY, Changeset.ADD)) 
     609        return (path, Changeset.ADD) 
     610 
     611    def open_directory(self, path, dir_baton, base_revision, dir_pool): 
     612        return (path, dir_baton[1]) 
     613 
     614    def change_dir_prop(self, dir_baton, name, value, pool): 
     615        path, change = dir_baton 
     616        if change != Changeset.ADD: 
     617            self.deltas.append((path, Node.DIRECTORY, change)) 
     618 
     619    def delete_entry(self, path, revision, dir_baton, pool): 
     620        self.deltas.append((path, None, Changeset.DELETE)) 
     621 
     622    def add_file(self, path, dir_baton, copyfrom_path, copyfrom_revision, 
     623                 dir_pool): 
     624        self.deltas.append((path, Node.FILE, Changeset.ADD)) 
     625 
     626    def open_file(self, path, dir_baton, dummy_rev, file_pool): 
     627        self.deltas.append((path, Node.FILE, Changeset.EDIT)) 
     628 
  • trac/versioncontrol/tests/svn_fs.py

    diff -ru trac-0.9.3/trac/versioncontrol/tests/svn_fs.py trac-0.9.3-trac-diff/trac/versioncontrol/tests/svn_fs.py
    old new  
    221221        self.assertEqual(('tags/v1', 7, 'unknown'), history.next()) 
    222222        self.assertRaises(StopIteration, history.next) 
    223223 
     224    # Diffs 
     225 
     226    def _cmp_diff(self, expected, got): 
     227        if expected[0]: 
     228            old = self.repos.get_node(*expected[0]) 
     229            self.assertEqual((old.path, old.rev), (got[0].path, got[0].rev)) 
     230        if expected[1]: 
     231            new = self.repos.get_node(*expected[1]) 
     232            self.assertEqual((new.path, new.rev), (got[1].path, got[1].rev)) 
     233        self.assertEqual(expected[2], (got[2], got[3])) 
     234         
     235    def test_diff_file_different_revs(self): 
     236        diffs = self.repos.get_changes('trunk/README.txt', 2, 'trunk/README.txt', 3) 
     237        self._cmp_diff((('trunk/README.txt', 2), 
     238                        ('trunk/README.txt', 3), 
     239                        (Node.FILE, Changeset.EDIT)), diffs.next()) 
     240        self.assertRaises(StopIteration, diffs.next) 
     241 
     242    def test_diff_file_different_files(self): 
     243        diffs = self.repos.get_changes('branches/v1x/README.txt', 12, 
     244                                      'branches/v1x/README2.txt', 12) 
     245        self._cmp_diff((('branches/v1x/README.txt', 12), 
     246                        ('branches/v1x/README2.txt', 12), 
     247                        (Node.FILE, Changeset.EDIT)), diffs.next()) 
     248        self.assertRaises(StopIteration, diffs.next) 
     249 
     250    def test_diff_file_no_change(self): 
     251        diffs = self.repos.get_changes('trunk/README.txt', 7, 
     252                                      'tags/v1/README.txt', 7) 
     253        self.assertRaises(StopIteration, diffs.next) 
     254  
     255    def test_diff_dir_different_revs(self): 
     256        diffs = self.repos.get_changes('trunk', 4, 'trunk', 8) 
     257        self._cmp_diff((None, ('trunk/dir1/dir2', 8), 
     258                        (Node.DIRECTORY, Changeset.ADD)), diffs.next()) 
     259        self._cmp_diff((None, ('trunk/dir1/dir3', 8),