Ticket #2028: trac-0.9b2-anydiff-r2306.patch
| File trac-0.9b2-anydiff-r2306.patch, 92.1 KB (added by cboos, 6 years ago) |
|---|
-
htdocs/css/browser.css
diff -ruN -x .svn trac-0.9b2/htdocs/css/browser.css anydiff-branch/htdocs/css/browser.css
old new 45 45 #dirlist td.name a, #dirlist td.rev a { border-bottom: none; display: block } 46 46 #dirlist td.change * { font-size: 9px } 47 47 48 /* Log */ 49 tr.diff input { 50 padding: 0 1em 0 1em; 51 margin: 0; 52 } 53 54 div.buttons { 55 clear: left; 56 } 57 58 #anydiff { 59 margin: 0 0 1em; 60 float: left; 61 } 62 #anydiff form, #anydiff div, #anydiff h2 { 63 display: inline; 64 } 65 #anydiff input { 66 vertical-align: baseline; 67 margin: 0 -0.5em 0 1em; 68 } 69 70 48 71 /* Styles for the revision log table 49 72 (extends the styles for "table.listing") */ 50 73 #chglist { margin-top: 0 } -
htdocs/css/changeset.css
diff -ruN -x .svn trac-0.9b2/htdocs/css/changeset.css anydiff-branch/htdocs/css/changeset.css
old new 26 26 27 27 .diff ul.props { font-size: 90%; list-style: disc; margin: .5em 0 0; padding: 0 .5em 1em 2em } 28 28 .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/anydiff.cs
diff -ruN -x .svn trac-0.9b2/templates/anydiff.cs anydiff-branch/templates/anydiff.cs
old new 1 <?cs include "header.cs"?> 2 3 <div id="ctxtnav" class="nav"> 4 <h2>Navigation</h2><?cs 5 with:links = chrome.links ?> 6 <ul> 7 </ul><?cs 8 /with ?> 9 </div> 10 11 <div id="content" class="changeset"> 12 <div id="title"> 13 <h1>Select Base and Target for Diff:</h1> 14 </div> 15 16 <div id="anydiff"> 17 <form action="<?cs var:anydiff.diff_href ?>" method="post"> 18 <table> 19 <tr> 20 <th><label for="old_path">From:</label></th> 21 <td> 22 <input type="text" id="old_path" name="old_path" value="<?cs 23 var:anydiff.old_path ?>" size="44" /> 24 <label for="old_rev">at Revision:</label> 25 <input type="text" id="old_rev" name="old" value="<?cs 26 var:anydiff.old_rev ?>" size="4" /> 27 </td> 28 </tr> 29 <tr> 30 <th><label for="new_path">To:</label></th> 31 <td> 32 <input type="text" id="new_path" name="path" value="<?cs 33 var:anydiff.new_path ?>" size="44" /> 34 <label for="new_rev">at Revision:</label> 35 <input type="text" id="new_rev" name="new" value="<?cs 36 var:anydiff.new_rev ?>" size="4" /> 37 </td> 38 </tr> 39 </table> 40 <div class="buttons"> 41 <input type="submit" value="View changes" /> 42 </div> 43 </form> 44 </div> 45 </div> 46 47 <?cs include "footer.cs"?> -
templates/browser.cs
diff -ruN -x .svn trac-0.9b2/templates/browser.cs anydiff-branch/templates/browser.cs
old new 3 3 4 4 <div id="ctxtnav" class="nav"> 5 5 <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> 7 10 </ul> 8 11 </div> 9 12 13 10 14 <div id="content" class="browser"> 11 15 <h1><?cs call:browser_path_links(browser.path, browser) ?></h1> 12 16 13 17 <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> 19 25 </div> 20 26 21 27 <?cs if:browser.is_dir ?> … … 114 120 ?>/TracBrowser">TracBrowser</a> for help on using the browser. 115 121 </div> 116 122 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 117 137 </div> 118 138 <?cs include:"footer.cs"?> -
templates/changeset.cs
diff -ruN -x .svn trac-0.9b2/templates/changeset.cs anydiff-branch/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><?cs6 with:links = chrome.links ?>7 <ul><?cs8 if:len(links.prev) ?>9 <li class="first<?cs if:!len(links.next) ?> last<?cs /if ?>">10 <a class="prev" href="<?cs var:links.prev.0.href ?>" title="<?cs11 var:links.prev.0.title ?>">Previous Changeset</a>12 </li><?cs13 /if ?><?cs14 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="<?cs17 var:links.next.0.title ?>">Next Changeset</a>18 </li><?cs19 /if ?>20 </ul><?cs21 /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 ?><?cs28 if:len(change.diff) ?><?cs29 set:has_diffs = 1 ?><?cs30 /if ?><?cs31 /each ?><?cs if:has_diffs || diff.options.ignoreblanklines32 || 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"<?cs38 if:diff.style == 'inline' ?> selected="selected"<?cs39 /if ?>>inline</option>40 <option value="sidebyside"<?cs41 if:diff.style == 'sidebyside' ?> selected="selected"<?cs42 /if ?>>side by side</option>43 </select>44 <div class="field">45 Show <input type="text" name="contextlines" id="contextlines" size="2"46 maxlength="2" 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"<?cs53 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"<?cs58 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"<?cs63 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) ?><?cs74 set:ndiffs = len(item.diff) ?><?cs75 set:nprops = len(item.props) ?>76 <div class="<?cs var:cl ?>"></div><?cs77 if:cl == "rem" ?>78 <a title="Show what was removed (rev. <?cs var:item.rev.old ?>)" href="<?cs79 var:item.browser_href.old ?>"><?cs var:item.path.old ?></a><?cs80 else ?>81 <a title="Show entry in browser" href="<?cs82 var:item.browser_href.new ?>"><?cs var:item.path.new ?></a><?cs83 /if ?>84 <span class="comment">(<?cs var:kind ?>)</span><?cs85 if:item.path.old && item.change == 'copy' || item.change == 'move' ?>86 <small><em>(<?cs var:kind ?> from <a href="<?cs87 var:item.browser_href.old ?>" title="Show original file (rev. <?cs88 var:item.rev.old ?>)"><?cs var:item.path.old ?></a>)</em></small><?cs89 /if ?><?cs90 if:$ndiffs + $nprops > #0 ?>91 (<a href="#file<?cs var:name(item) ?>" title="Show differences"><?cs92 if:$ndiffs > #0 ?><?cs var:ndiffs ?> diff<?cs if:$ndiffs > #1 ?>s<?cs /if ?><?cs93 /if ?><?cs94 if:$ndiffs && $nprops ?>, <?cs /if ?><?cs95 if:$nprops > #0 ?><?cs var:nprops ?> prop<?cs if:$nprops > #1 ?>s<?cs /if ?><?cs96 /if ?></a>)<?cs97 elif:cl == "mod" ?>98 (<a href="<?cs var:item.browser_href.old ?>"99 title="Show previous version in browser">previous</a>)<?cs100 /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 var:changeset.message ?></dd>110 <dt class="files">Files:</dt>111 <dd class="files">112 <ul><?cs each:item = changeset.changes ?>113 <li><?cs114 if:item.change == 'add' ?><?cs115 call:node_change(item, 'add', 'added') ?><?cs116 elif:item.change == 'delete' ?><?cs117 call:node_change(item, 'rem', 'deleted') ?><?cs118 elif:item.change == 'copy' ?><?cs119 call:node_change(item, 'cp', 'copied') ?><?cs120 elif:item.change == 'move' ?><?cs121 call:node_change(item, 'mv', 'moved') ?><?cs122 elif:item.change == 'edit' ?><?cs123 call:node_change(item, 'mod', 'modified') ?><?cs124 /if ?>125 </li>126 <?cs /each ?></ul>127 </dd>128 </dl>129 130 <div class="diff">131 <div id="legend">132 <h3>Legend:</h3>133 <dl>134 <dt class="unmod"></dt><dd>Unmodified</dd>135 <dt class="add"></dt><dd>Added</dd>136 <dt class="rem"></dt><dd>Removed</dd>137 <dt class="mod"></dt><dd>Modified</dd>138 <dt class="cp"></dt><dd>Copied</dd>139 <dt class="mv"></dt><dd>Moved</dd>140 </dl>141 </div>142 <ul class="entries"><?cs143 each:item = changeset.changes ?><?cs144 if:len(item.diff) || len(item.props) ?><li class="entry" id="file<?cs145 var:name(item) ?>"><h2><a href="<?cs146 var:item.browser_href.new ?>" title="Show new revision <?cs147 var:item.rev.new ?> of this file in browser"><?cs148 var:item.path.new ?></a></h2><?cs149 if:len(item.props) ?><ul class="props"><?cs150 each:prop = item.props ?><li>Property <strong><?cs151 var:name(prop) ?></strong> <?cs152 if:prop.old && prop.new ?>changed from <?cs153 elif:!prop.old ?>set<?cs154 else ?>deleted<?cs155 /if ?><?cs156 if:prop.old && prop.new ?><em><tt><?cs var:prop.old ?></tt></em><?cs /if ?><?cs157 if:prop.new ?> to <em><tt><?cs var:prop.new ?></tt></em><?cs /if ?></li><?cs158 /each ?></ul><?cs159 /if ?><?cs160 if:len(item.diff) ?><table class="<?cs161 var:diff.style ?>" summary="Differences" cellspacing="0"><?cs162 if:diff.style == 'sidebyside' ?>163 <colgroup class="l"><col class="lineno" /><col class="content" /></colgroup>164 <colgroup class="r"><col class="lineno" /><col class="content" /></colgroup>165 <thead><tr>166 <th colspan="2"><a href="<?cs167 var:item.browser_href.old ?>" title="Show old rev. <?cs168 var:item.rev.old ?> of <?cs var:item.path.old ?>">Revision <?cs169 var:item.rev.old ?></a></th>170 <th colspan="2"><a href="<?cs171 var:item.browser_href.new ?>" title="Show new rev. <?cs172 var:item.rev.new ?> of <?cs var:item.path.new ?>">Revision <?cs173 var:item.rev.new ?></a></th>174 </tr>175 </thead><?cs176 each:change = item.diff ?><tbody><?cs177 call:diff_display(change, diff.style) ?></tbody><?cs178 if:name(change) < len(item.diff) - 1 ?><tbody class="skipped"><tr>179 <th>…</th><td> </td><th>…</th><td> </td>180 </tr></tbody><?cs /if ?><?cs181 /each ?><?cs182 else ?>183 <colgroup><col class="lineno" /><col class="lineno" /><col class="content" /></colgroup>184 <thead><tr>185 <th title="Revision <?cs var:item.rev.old ?>"><a href="<?cs186 var:item.browser_href.old ?>" title="Show old version of <?cs187 var:item.path.old ?>">r<?cs var:item.rev.old ?></a></th>188 <th title="Revision <?cs var:item.rev.new ?>"><a href="<?cs189 var:item.browser_href.new ?>" title="Show new version of <?cs190 var:item.path.new ?>">r<?cs var:item.rev.new ?></a></th>191 <th> </th></tr>192 </thead><?cs193 each:change = item.diff ?><?cs194 call:diff_display(change, diff.style) ?><?cs195 if:name(change) < len(item.diff) - 1 ?><tbody class="skipped"><tr>196 <th>…</th><th>…</th><td> </td>197 </tr></tbody><?cs /if ?><?cs198 /each ?><?cs199 /if ?></table><?cs200 /if ?></li><?cs201 /if ?><?cs202 /each ?></ul>203 </div>204 205 </div>206 <?cs include "footer.cs"?> -
templates/diff.cs
diff -ruN -x .svn trac-0.9b2/templates/diff.cs anydiff-branch/templates/diff.cs
old new 1 <?cs include "header.cs"?> 2 <?cs include "macros.cs"?> 3 4 <div id="ctxtnav" class="nav"> 5 <h2>Navigation</h2><?cs 6 with:links = chrome.links ?> 7 <ul><?cs 8 if:diff.chgset ?><?cs 9 if:len(links.prev) ?> 10 <li class="first<?cs if:!len(links.next) ?> last<?cs /if ?>"> 11 <a class="prev" href="<?cs var:links.prev.0.href ?>" title="<?cs 12 var:links.prev.0.title ?>">Previous <?cs 13 if:diff.restricted ?>Change<?cs else ?>Changeset<?cs /if ?></a> 14 </li><?cs 15 /if ?><?cs 16 if:len(links.next) ?> 17 <li class="<?cs if:len(links.prev) ?>first <?cs /if ?>last"> 18 <a class="next" href="<?cs var:links.next.0.href ?>" title="<?cs 19 var:links.next.0.title ?>">Next <?cs 20 if:diff.restricted ?>Change<?cs else ?>Changeset<?cs /if ?></a> 21 </li><?cs 22 /if ?><?cs 23 else ?> 24 <li class="first"><a href="<?cs var:diff.reverse_href ?>">Reverse Diff</a></li><?cs 25 /if ?> 26 </ul><?cs 27 /with ?> 28 </div> 29 30 <div id="content" class="changeset"> 31 <div id="title"><?cs 32 if:diff.chgset ?><?cs 33 if:diff.restricted ?> 34 <h1>Changeset <a title="Show full changeset" href="<?cs var:diff.href.new_rev ?>"> 35 <?cs var:diff.new_rev ?></a> 36 for <a title="Show entry in browser" href="<?cs var:diff.href.new_path ?>"> 37 <?cs var:diff.new_path ?></a> 38 </h1><?cs 39 else ?> 40 <h1>Changeset <?cs var:diff.new_rev ?></h1><?cs 41 /if ?><?cs 42 else ?><?cs 43 if:diff.restricted ?> 44 <h1>Changes in <a title="Show entry in browser" href="<?cs var:diff.href.new_path ?>"> 45 <?cs var:diff.new_path ?></a> 46 from revision <a title="Show full changeset" href="<?cs var:diff.href.old_rev ?>"> 47 <?cs var:diff.old_rev ?></a> 48 to <a title="Show full changeset" href="<?cs var:diff.href.new_rev ?>"> 49 <?cs var:diff.new_rev ?></a> 50 </h1><?cs 51 else ?> 52 <h1>Changes from <a title="Show entry in browser" href="<?cs var:diff.href.old_path ?>"> 53 <?cs var:diff.old_path ?></a> 54 at revision <a title="Show full changeset" href="<?cs var:diff.href.old_rev ?>"> 55 <?cs var:diff.old_rev ?></a> 56 to <a title="Show entry in browser" href="<?cs var:diff.href.new_path ?>"> 57 <?cs var:diff.new_path ?></a> 58 at revision <a title="Show full changeset" href="<?cs var:diff.href.new_rev ?>"> 59 <?cs var:diff.new_rev ?></a> 60 </h1><?cs 61 /if ?><?cs 62 /if ?> 63 </div> 64 65 <?cs each:change = diff.changes ?><?cs 66 if:len(change.diff) ?><?cs 67 set:has_diffs = 1 ?><?cs 68 /if ?><?cs 69 /each ?><?cs if:has_diffs || diff.options.ignoreblanklines 70 || diff.options.ignorecase || diff.options.ignorewhitespace ?> 71 <form method="post" id="prefs" action=""> 72 <div><?cs 73 if:!diff.chgset ?> 74 <input type="hidden" name="old_path" value="<?cs var:diff.old_path ?>" /> 75 <input type="hidden" name="path" value="<?cs var:diff.new_path ?>" /> 76 <input type="hidden" name="old" value="<?cs var:diff.old_rev ?>" /> 77 <input type="hidden" name="new" value="<?cs var:diff.new_rev ?>" /><?cs 78 /if ?> 79 <label for="style">View differences</label> 80 <select id="style" name="style"> 81 <option value="inline"<?cs 82 if:diff.style == 'inline' ?> selected="selected"<?cs 83 /if ?>>inline</option> 84 <option value="sidebyside"<?cs 85 if:diff.style == 'sidebyside' ?> selected="selected"<?cs 86 /if ?>>side by side</option> 87 </select> 88 <div class="field"> 89 Show <input type="text" name="contextlines" id="contextlines" size="2" 90 maxlength="2" value="<?cs var:diff.options.contextlines ?>" /> 91 <label for="contextlines">lines around each change</label> 92 </div> 93 <fieldset id="ignore"> 94 <legend>Ignore:</legend> 95 <div class="field"> 96 <input type="checkbox" id="blanklines" name="ignoreblanklines"<?cs 97 if:diff.options.ignoreblanklines ?> checked="checked"<?cs /if ?> /> 98 <label for="blanklines">Blank lines</label> 99 </div> 100 <div class="field"> 101 <input type="checkbox" id="case" name="ignorecase"<?cs 102 if:diff.options.ignorecase ?> checked="checked"<?cs /if ?> /> 103 <label for="case">Case changes</label> 104 </div> 105 <div class="field"> 106 <input type="checkbox" id="whitespace" name="ignorewhitespace"<?cs 107 if:diff.options.ignorewhitespace ?> checked="checked"<?cs /if ?> /> 108 <label for="whitespace">White space changes</label> 109 </div> 110 </fieldset> 111 <div class="buttons"> 112 <input type="submit" name="update" value="Update" /> 113 </div> 114 </div> 115 </form><?cs /if ?> 116 117 <?cs def:node_change(item,cl,kind) ?><?cs 118 set:ndiffs = len(item.diff) ?><?cs 119 set:nprops = len(item.props) ?> 120 <div class="<?cs var:cl ?>"></div><?cs 121 if:cl == "rem" ?> 122 <a title="Show what was removed (rev. <?cs var:item.rev.old ?>)" href="<?cs 123 var:item.browser_href.old ?>"><?cs var:item.path.old ?></a><?cs 124 else ?> 125 <a title="Show entry in browser" href="<?cs 126 var:item.browser_href.new ?>"><?cs var:item.path.new ?></a><?cs 127 /if ?> 128 <span class="comment">(<?cs var:kind ?>)</span><?cs 129 if:item.path.old && item.change == 'copy' || item.change == 'move' ?> 130 <small><em>(<?cs var:kind ?> from <a href="<?cs 131 var:item.browser_href.old ?>" title="Show original file (rev. <?cs 132 var:item.rev.old ?>)"><?cs var:item.path.old ?></a>)</em></small><?cs 133 /if ?><?cs 134 if:$ndiffs + $nprops > #0 ?> 135 (<a href="#file<?cs var:name(item) ?>" title="Show differences"><?cs 136 if:$ndiffs > #0 ?><?cs var:ndiffs ?> diff<?cs if:$ndiffs > #1 ?>s<?cs /if ?><?cs 137 /if ?><?cs 138 if:$ndiffs && $nprops ?>, <?cs /if ?><?cs 139 if:$nprops > #0 ?><?cs var:nprops ?> prop<?cs if:$nprops > #1 ?>s<?cs /if ?><?cs 140 /if ?></a>)<?cs 141 elif:cl == "mod" ?> 142 (<a href="<?cs var:item.browser_href.old ?>" 143 title="Show previous version in browser">previous</a>)<?cs 144 /if ?> 145 <?cs /def ?> 146 147 <dl id="overview"><?cs 148 if:diff.chgset ?> 149 <dt class="time">Timestamp:</dt> 150 <dd class="time"><?cs var:changeset.time ?></dd> 151 <dt class="author">Author:</dt> 152 <dd class="author"><?cs var:changeset.author ?></dd> 153 <dt class="message">Message:</dt> 154 <dd class="message" id="searchable"><?cs var:changeset.message ?></dd><?cs 155 /if ?> 156 <dt class="files"><?cs 157 if:len(diff.changes) > #0 ?> 158 Files:<?cs 159 else ?> 160 (None)<?cs 161 /if ?> 162 </dt> 163 <dd class="files"> 164 <ul><?cs each:item = diff.changes ?> 165 <li><?cs 166 if:item.change == 'add' ?><?cs 167 call:node_change(item, 'add', 'added') ?><?cs 168 elif:item.change == 'delete' ?><?cs 169 call:node_change(item, 'rem', 'deleted') ?><?cs 170 elif:item.change == 'copy' ?><?cs 171 call:node_change(item, 'cp', 'copied') ?><?cs 172 elif:item.change == 'move' ?><?cs 173 call:node_change(item, 'mv', 'moved') ?><?cs 174 elif:item.change == 'edit' ?><?cs 175 call:node_change(item, 'mod', 'modified') ?><?cs 176 /if ?> 177 </li> 178 <?cs /each ?></ul> 179 </dd> 180 </dl> 181 182 <div class="diff"> 183 <div id="legend"> 184 <h3>Legend:</h3> 185 <dl> 186 <dt class="unmod"></dt><dd>Unmodified</dd> 187 <dt class="add"></dt><dd>Added</dd> 188 <dt class="rem"></dt><dd>Removed</dd> 189 <dt class="mod"></dt><dd>Modified</dd> 190 <dt class="cp"></dt><dd>Copied</dd> 191 <dt class="mv"></dt><dd>Moved</dd> 192 </dl> 193 </div> 194 <ul class="entries"><?cs 195 each:item = diff.changes ?><?cs 196 if:len(item.diff) || len(item.props) ?><li class="entry" id="file<?cs 197 var:name(item) ?>"><h2><a href="<?cs 198 var:item.browser_href.new ?>" title="Show new revision <?cs 199 var:item.rev.new ?> of this file in browser"><?cs 200 var:item.path.new ?></a></h2><?cs 201 if:len(item.props) ?><ul class="props"><?cs 202 each:prop = item.props ?><li>Property <strong><?cs 203 var:name(prop) ?></strong> <?cs 204 if:prop.old && prop.new ?>changed from <?cs 205 elif:!prop.old ?>set<?cs 206 else ?>deleted<?cs 207 /if ?><?cs 208 if:prop.old && prop.new ?><em><tt><?cs var:prop.old ?></tt></em><?cs /if ?><?cs 209 if:prop.new ?> to <em><tt><?cs var:prop.new ?></tt></em><?cs /if ?></li><?cs 210 /each ?></ul><?cs 211 /if ?><?cs 212 if:len(item.diff) ?><table class="<?cs 213 var:diff.style ?>" summary="Differences" cellspacing="0"><?cs 214 if:diff.style == 'sidebyside' ?> 215 <colgroup class="l"><col class="lineno" /><col class="content" /></colgroup> 216 <colgroup class="r"><col class="lineno" /><col class="content" /></colgroup> 217 <thead><tr> 218 <th colspan="2"><a href="<?cs 219 var:item.browser_href.old ?>" title="Show old rev. <?cs 220 var:item.rev.old ?> of <?cs var:item.path.old ?>">Revision <?cs 221 var:item.rev.old ?></a></th> 222 <th colspan="2"><a href="<?cs 223 var:item.browser_href.new ?>" title="Show new rev. <?cs 224 var:item.rev.new ?> of <?cs var:item.path.new ?>">Revision <?cs 225 var:item.rev.new ?></a></th> 226 </tr> 227 </thead><?cs 228 each:change = item.diff ?><tbody><?cs 229 call:diff_display(change, diff.style) ?></tbody><?cs 230 if:name(change) < len(item.diff) - 1 ?><tbody class="skipped"><tr> 231 <th>…</th><td> </td><th>…</th><td> </td> 232 </tr></tbody><?cs /if ?><?cs 233 /each ?><?cs 234 else ?> 235 <colgroup><col class="lineno" /><col class="lineno" /><col class="content" /></colgroup> 236 <thead><tr> 237 <th title="Revision <?cs var:item.rev.old ?>"><a href="<?cs 238 var:item.browser_href.old ?>" title="Show old version of <?cs 239 var:item.path.old ?>">r<?cs var:item.rev.old ?></a></th> 240 <th title="Revision <?cs var:item.rev.new ?>"><a href="<?cs 241 var:item.browser_href.new ?>" title="Show new version of <?cs 242 var:item.path.new ?>">r<?cs var:item.rev.new ?></a></th> 243 <th> </th></tr> 244 </thead><?cs 245 each:change = item.diff ?><?cs 246 call:diff_display(change, diff.style) ?><?cs 247 if:name(change) < len(item.diff) - 1 ?><tbody class="skipped"><tr> 248 <th>…</th><th>…</th><td> </td> 249 </tr></tbody><?cs /if ?><?cs 250 /each ?><?cs 251 /if ?></table><?cs 252 /if ?></li><?cs 253 /if ?><?cs 254 /each ?></ul> 255 </div> 256 257 </div> 258 <?cs include "footer.cs"?> -
templates/log.cs
diff -ruN -x .svn trac-0.9b2/templates/log.cs anydiff-branch/templates/log.cs
old new 3 3 4 4 <div id="ctxtnav" class="nav"> 5 5 <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 8 9 if:len(chrome.links.prev) ?> 9 10 <li class="first<?cs if:!len(chrome.links.next) ?> last<?cs /if ?>"> 10 11 ← <a href="<?cs var:chrome.links.prev.0.href ?>" title="<?cs … … 61 62 title="Warning: by updating, you will clear the page history" /> 62 63 </div> 63 64 </form> 65 64 66 <div class="diff"> 65 67 <div id="legend"> 66 68 <h3>Legend:</h3> … … 74 76 </dl> 75 77 </div> 76 78 </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> 77 84 <table id="chglist" class="listing"> 78 85 <thead> 79 86 <tr> 87 <th>Old</th> 88 <th>New</th> 80 89 <th class="change"></th> 81 90 <th class="data">Date</th> 82 91 <th class="rev">Rev</th> … … 87 96 </thead> 88 97 <tbody><?cs 89 98 set:indent = #1 ?><?cs 99 set:idx = #0 ?><?cs 90 100 each:item = log.items ?><?cs 91 101 if:item.copyfrom_path ?> 92 102 <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"> 94 104 copied from <a href="<?cs var:item.browser_href ?>"?><?cs var:item.copyfrom_path ?></a>: 95 105 </td> 96 106 </tr><?cs … … 99 109 set:indent = #1 ?><?cs 100 110 /if ?> 101 111 <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> 102 118 <td class="change" style="padding-left:<?cs var:indent ?>em"> 103 119 <a title="View log starting at this revision" href="<?cs var:item.log_href ?>"> 104 120 <span class="<?cs var:item.change ?>"></span> … … 117 133 <td class="author"><?cs var:log.changes[item.rev].author ?></td> 118 134 <td class="summary"><?cs var:log.changes[item.rev].message ?></td> 119 135 </tr><?cs 136 set:idx = idx + 1 ?><?cs 120 137 /each ?> 121 138 </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 123 144 if:len(links.prev) || len(links.next) ?><div id="paging" class="nav"><ul><?cs 124 145 if:len(links.prev) ?><li class="first<?cs 125 146 if:!len(links.next) ?> last<?cs /if ?>">← <a href="<?cs -
templates/wiki.cs
diff -ruN -x .svn trac-0.9b2/templates/wiki.cs anydiff-branch/templates/wiki.cs
old new 154 154 var:wiki.page_name ?></a></h1> 155 155 <?cs if:len(wiki.history) ?><form method="get" action=""> 156 156 <input type="hidden" name="action" value="diff" /> 157 <div class="buttons"> 158 <input type="submit" value="View changes" /> 159 </div> 157 160 <table id="wikihist" class="listing" summary="Change history"> 158 161 <thead><tr> 159 162 <th class="diff"></th> -
trac/__init__.py
diff -ruN -x .svn trac-0.9b2/trac/__init__.py anydiff-branch/trac/__init__.py
old new 10 10 """ 11 11 __docformat__ = 'epytext en' 12 12 13 __version__ = '0.9b2 '13 __version__ = '0.9b2-anydiff' 14 14 __url__ = 'http://trac.edgewall.com/' 15 15 __copyright__ = '(C) 2003-2005 Edgewall Software' 16 16 __license__ = 'BSD' -
trac/versioncontrol/api.py
diff -ruN -x .svn trac-0.9b2/trac/versioncontrol/api.py anydiff-branch/trac/versioncontrol/api.py
old new 39 39 """ 40 40 raise NotImplementedError 41 41 42 def has_node(self, path, rev): 43 """ 44 Tell if there's a node at the specified (path,rev) combination. 45 """ 46 raise NotImplementedError 47 42 48 def get_node(self, path, rev=None): 43 49 """ 44 50 Retrieve a Node (directory or file) from the repository at the … … 111 117 'None' is a valid revision value and represents the youngest revision. 112 118 """ 113 119 return NotImplementedError 114 120 121 def get_deltas(self, old_path, old_rev, new_path, new_rev, ignore_ancestry=1): 122 """ 123 Generator that yields change tuples (old_node, new_node, kind, change) 124 for each node change between the two arbitrary (path,rev) pairs. 125 126 The old_node is assumed to be None when the change is an ADD, 127 the new_node is assumed to be None when the change is a DELETE. 128 """ 129 raise NotImplementedError 130 115 131 116 132 class Node(object): 117 133 """ … … 149 165 node (if the underlying version control system supports that), which 150 166 will be indicated by the first element of the tuple (i.e. the path) 151 167 changing. 168 Starts with an entry for the current revision. 152 169 """ 153 170 raise NotImplementedError 154 171 172 def get_previous(self): 173 """ 174 Return the (path, rev, chg) tuple corresponding to the previous 175 revision for that node. 176 """ 177 skip = True 178 for p in self.get_history(2): 179 if skip: 180 skip = False 181 else: 182 return p 183 155 184 def get_properties(self): 156 185 """ 157 186 Returns a dictionary containing the properties (meta-data) of the node. -
trac/versioncontrol/cache.py
diff -ruN -x .svn trac-0.9b2/trac/versioncontrol/cache.py anydiff-branch/trac/versioncontrol/cache.py
old new 84 84 def get_node(self, path, rev=None): 85 85 return self.repos.get_node(path, rev) 86 86 87 def has_node(self, path, rev): 88 return self.repos.has_node(path, rev) 89 87 90 def get_oldest_rev(self): 88 91 return self.repos.oldest_rev 89 92 … … 108 111 def normalize_rev(self, rev): 109 112 return self.repos.normalize_rev(rev) 110 113 114 def get_deltas(self, old_path, old_rev, new_path, new_rev, ignore_ancestry=1): 115 return self.repos.get_deltas(old_path, old_rev, new_path, new_rev, ignore_ancestry) 116 111 117 112 118 class CachedChangeset(Changeset): 113 119 -
trac/versioncontrol/diff.py
diff -ruN -x .svn trac-0.9b2/trac/versioncontrol/diff.py anydiff-branch/trac/versioncontrol/diff.py
old new 218 218 ignore_space_changes) 219 219 for group in _group_opcodes(opcodes, context): 220 220 i1, i2, j1, j2 = group[0][1], group[-1][2], group[0][3], group[-1][4] 221 if i1 == 0 and i2 == 0: 222 i1, i2 = -1, -1 # support for 'A'dd changes 221 223 yield '@@ -%d,%d +%d,%d @@' % (i1 + 1, i2 - i1, j1 + 1, j2 - j1) 222 224 for tag, i1, i2, j1, j2 in group: 223 225 if tag == 'equal': -
trac/versioncontrol/svn_fs.py
diff -ruN -x .svn trac-0.9b2/trac/versioncontrol/svn_fs.py anydiff-branch/trac/versioncontrol/svn_fs.py
old new 207 207 def __del__(self): 208 208 self.close() 209 209 210 def has_node(self, path, rev): 211 rev_root = fs.revision_root(self.fs_ptr, rev, self.pool()) 212 node_type = fs.check_path(rev_root, path, self.pool()) 213 return node_type in _kindmap 214 210 215 def normalize_path(self, path): 211 216 return (not path or path == '/') and '/' or path.strip('/') 212 217 … … 298 303 subpool = Pool(self.pool) 299 304 while rev: 300 305 subpool.clear() 301 rev_root = fs.revision_root(self.fs_ptr, rev, subpool()) 302 node_type = fs.check_path(rev_root, path, subpool()) 303 if node_type in _kindmap: # then path exists at that rev 306 if self.has_node(path, rev): 304 307 if expect_deletion: 305 308 # it was missing, now it's there again: 306 309 # rev+1 must be a delete … … 330 333 expect_deletion = True 331 334 rev = self.previous_rev(rev) 332 335 336 def get_deltas(self, old_path, old_rev, new_path, new_rev, 337 ignore_ancestry=0): 338 old_node = new_node = None 339 old_rev = self.normalize_rev(old_rev) 340 new_rev = self.normalize_rev(new_rev) 341 if self.has_node(old_path, old_rev): 342 old_node = self.get_node(old_path, old_rev) 343 else: 344 raise TracError, ('The Base for Diff is invalid: path %s' 345 ' doesn\'t exist in revision %s' \ 346 % (old_path, old_rev)) 347 if self.has_node(new_path, new_rev): 348 new_node = self.get_node(new_path, new_rev) 349 else: 350 raise TracError, ('The Target for Diff is invalid: path %s' 351 ' doesn\'t exist in revision %s' \ 352 % (new_path, new_rev)) 353 if new_node.kind != old_node.kind: 354 raise TracError, ('Diff mismatch: Base is a %s (%s in revision %s) ' 355 'and Target is a %s (%s in revision %s).' \ 356 % (old_node.kind, old_path, old_rev, 357 new_node.kind, new_path, new_rev)) 358 subpool = Pool(self.pool) 359 if new_node.isdir: 360 editor = DiffChangeEditor() 361 e_ptr, e_baton = delta.make_editor(editor, subpool()) 362 old_root = fs.revision_root(self.fs_ptr, old_rev, subpool()) 363 new_root = fs.revision_root(self.fs_ptr, new_rev, subpool()) 364 def authz_cb(root, path, pool): return 1 365 text_deltas = 0 # as this is anyway re-done in Diff.py... 366 entry_props = 0 # "... typically used only for working copy updates" 367 repos.svn_repos_dir_delta(old_root, old_path, '', 368 new_root, new_path, 369 e_ptr, e_baton, authz_cb, 370 text_deltas, 371 1, # directory 372 entry_props, 373 ignore_ancestry, 374 subpool()) 375 for path, kind, change in editor.deltas: 376 old_node = new_node = None 377 if change != Changeset.ADD: 378 old_node = self.get_node(posixpath.join(old_path, path), 379 old_rev) 380 if change != Changeset.DELETE: 381 new_node = self.get_node(posixpath.join(new_path, path), 382 new_rev) 383 else: 384 kind = _kindmap[fs.check_path(old_root, old_node.path, 385 subpool())] 386 yield (old_node, new_node, kind, change) 387 else: 388 old_root = fs.revision_root(self.fs_ptr, old_rev, subpool()) 389 new_root = fs.revision_root(self.fs_ptr, new_rev, subpool()) 390 if fs.contents_changed(old_root, old_path, new_root, new_path, 391 subpool()): 392 yield (old_node, new_node, Node.FILE, Changeset.EDIT) 393 333 394 334 395 class SubversionNode(Node): 335 396 … … 352 413 self.pool()) 353 414 self.created_path = fs.node_created_path(self.root, self.scoped_path, 354 415 self.pool()) 355 # 'created_path' differs from 'path' if the last operation is a copy, 356 # and furthermore, 'path' might not exist at 'create_rev' 416 # Note: 'created_path' differs from 'path' if the last change was a copy, 417 # and furthermore, 'path' might not exist at 'create_rev'. 418 # The only guarantees are: 419 # * this node exists at (path,rev) 420 # * the node existed at (created_path,created_rev) 421 # TODO: check node id 357 422 self.rev = self.created_rev 358 423 359 424 Node.__init__(self, path, self.rev, _kindmap[node_type]) … … 393 458 if newer: 394 459 yield newer 395 460 461 # def get_previous(self): 462 # # FIXME: redo it with fs.node_history 463 396 464 def get_properties(self): 397 465 props = fs.node_proplist(self.root, self.scoped_path, self.pool()) 398 466 for name,value in props.items(): … … 487 555 488 556 def _get_prop(self, name): 489 557 return fs.revision_prop(self.fs_ptr, self.rev, name, self.pool()) 558 559 560 # 561 # Delta editor for diffs between arbitrary nodes 562 # 563 # Note 1: the 'copyfrom_path' and 'copyfrom_rev' information is not used 564 # because 'repos.svn_repos_dir_delta' *doesn't* provide it. 565 # 566 # Note 2: the 'dir_baton' is the path of the parent directory 567 # 568 569 class DiffChangeEditor(delta.Editor): 570 571 def __init__(self): 572 self.deltas = [] 573 574 # -- svn.delta.Editor callbacks 575 576 def open_root(self, base_revision, dir_pool): 577 return ('/', Changeset.EDIT) 578 579 def add_directory(self, path, dir_baton, copyfrom_path, copyfrom_rev, 580 dir_pool): 581 self.deltas.append((path, Node.DIRECTORY, Changeset.ADD)) 582 return (path, Changeset.ADD) 583 584 def open_directory(self, path, dir_baton, base_revision, dir_pool): 585 return (path, dir_baton[1]) 586 587 def change_dir_prop(self, dir_baton, name, value, pool): 588 path, change = dir_baton 589 if change != Changeset.ADD: 590 self.deltas.append((path, Node.DIRECTORY, change)) 591 592 def delete_entry(self, path, revision, dir_baton, pool): 593 self.deltas.append((path, None, Changeset.DELETE)) 594 595 def add_file(self, path, dir_baton, copyfrom_path, copyfrom_revision, 596 dir_pool): 597 self.deltas.append((path, Node.FILE, Changeset.ADD)) 598 599 def open_file(self, path, dir_baton, dummy_rev, file_pool): 600 self.deltas.append((path, Node.FILE, Changeset.EDIT)) 601 -
trac/versioncontrol/tests/svn_fs.py
diff -ruN -x .svn trac-0.9b2/trac/versioncontrol/tests/svn_fs.py anydiff-branch/trac/versioncontrol/tests/svn_fs.py
old new 215 215 self.assertEqual(('tags/v1', 7, 'unknown'), history.next()) 216 216 self.assertRaises(StopIteration, history.next) 217 217 218 # Diffs 219 220 def _cmp_diff(self, expected, got): 221 if expected[0]: 222 old = self.repos.get_node(*expected[0]) 223 self.assertEqual((old.path, old.rev), (got[0].path, got[0].rev)) 224 if expected[1]: 225 new = self.repos.get_node(*expected[1]) 226 self.assertEqual((new.path, new.rev), (got[1].path, got[1].rev)) 227 self.assertEqual(expected[2], (got[2], got[3])) 228 229 def test_diff_file_different_revs(self): 230 diffs = self.repos.get_deltas('trunk/README.txt', 2, 'trunk/README.txt', 3) 231 self._cmp_diff((('trunk/README.txt', 2), 232 ('trunk/README.txt', 3), 233 (Node.FILE, Changeset.EDIT)), diffs.next()) 234 self.assertRaises(StopIteration, diffs.next) 235 236 def test_diff_file_different_files(self): 237 diffs = self.repos.get_deltas('branches/v1x/README.txt', 12, 238 'branches/v1x/README2.txt', 12) 239 self._cmp_diff((('branches/v1x/README.txt', 12), 240 ('branches/v1x/README2.txt', 12), 241 (Node.FILE, Changeset.EDIT)), diffs.next()) 242 self.assertRaises(StopIteration, diffs.next) 243 244 def test_diff_file_no_change(self): 245 diffs = self.repos.get_deltas('trunk/README.txt', 7, 246 'tags/v1/README.txt', 7) 247 self.assertRaises(StopIteration, diffs.next) 248 249 def test_diff_dir_different_revs(self): 250 diffs = self.repos.get_deltas('trunk', 4, 'trunk', 8) 251 self._cmp_diff((None, ('trunk/dir1/dir2', 8), 252 (Node.DIRECTORY, Changeset.ADD)), diffs.next()) 253 self._cmp_diff((None, ('trunk/dir1/dir3', 8), 254 (Node.DIRECTORY, Changeset.ADD)), diffs.next()) 255 self._cmp_diff((None, ('trunk/README2.txt', 6), 256 (Node.FILE, Changeset.ADD)), diffs.next()) 257 self._cmp_diff((('trunk/dir2', 4), None, 258 (Node.DIRECTORY, Changeset.DELETE)), diffs.next()) 259 self._cmp_diff((('trunk/dir3', 4), None, 260 (Node.DIRECTORY, Changeset.DELETE)), diffs.next()) 261 self.assertRaises(StopIteration, diffs.next) 262 263 def test_diff_dir_different_dirs(self): 264 diffs = self.repos.get_deltas('trunk', 1, 'branches/v1x', 12) 265 self._cmp_diff((None, ('branches/v1x/dir1', 12), 266 (Node.DIRECTORY, Changeset.ADD)), diffs.next()) 267 self._cmp_diff((None, ('branches/v1x/dir1/dir2', 12), 268 (Node.DIRECTORY, Changeset.ADD)), diffs.next()) 269 self._cmp_diff((None, ('branches/v1x/dir1/dir3', 12), 270 (Node.DIRECTORY, Changeset.ADD)), diffs.next()) 271 self._cmp_diff((None, ('branches/v1x/README.txt', 12), 272 (Node.FILE, Changeset.ADD)), diffs.next()) 273 self._cmp_diff((None, ('branches/v1x/README2.txt', 12), 274 (Node.FILE, Changeset.ADD)), diffs.next()) 275 self.assertRaises(StopIteration, diffs.next) 276 277 def test_diff_dir_no_change(self): 278 diffs = self.repos.get_deltas('trunk', 7, 279 'tags/v1', 7) 280 self.assertRaises(StopIteration, diffs.next) 281 282 # Changesets 283 218 284 def test_changeset_repos_creation(self): 219 285 chgset = self.repos.get_changeset(0) 220 286 self.assertEqual(0, chgset.rev) -
trac/versioncontrol/web_ui/browser.py
diff -ruN -x .svn trac-0.9b2/trac/versioncontrol/web_ui/browser.py anydiff-branch/trac/versioncontrol/web_ui/browser.py
old new 88 88 89 89 repos = self.env.get_repository(req.authname) 90 90 node = repos.get_node(path, rev) 91 rev = repos.normalize_rev(rev) 91 92 92 93 hidden_properties = [p.strip() for p 93 94 in self.config.get('browser', 'hide_properties', 94 95 'svk:merge').split(',')] 96 95 97 req.hdf['title'] = path 96 req.hdf['browser']= {98 browser_hdf = { 97 99 'path': path, 98 'revision': rev or repos.youngest_rev,100 'revision': rev, 99 101 'props': dict([(util.escape(name), util.escape(value)) 100 102 for name, value in node.get_properties().items() 101 if not name in hidden_properties]), 102 'href': util.escape(self.env.href.browser(path, rev=rev or 103 repos.youngest_rev)), 104 'log_href': util.escape(self.env.href.log(path)) 105 } 103 if not name in hidden_properties]) 104 } 105 browser_hrefs = { 106 'href': self.env.href.browser(path,rev=rev), 107 'restr_changeset_href': self.env.href.changeset(node.rev, path), 108 'anydiff_href': self.env.href.anydiff(), 109 'log_href': self.env.href.log(path) 110 } 111 browser_hdf.update(dict([(key, util.escape(href)) for key, href in 112 browser_hrefs.items()])) 113 req.hdf['browser'] = browser_hdf 106 114 107 115 path_links = get_path_links(self.env.href, path, rev) 108 116 if len(path_links) > 1: … … 162 170 163 171 req.hdf['browser.items'] = info 164 172 req.hdf['browser.changes'] = changes 165 173 if node.path != '': 174 zip_href = self.env.href.diff(node.path, new=rev, old=rev, 175 old_path='/', # special case (#238) 176 format='zip') 177 add_link(req, 'alternate', zip_href, 'Zip Archive', 178 'application/zip', 'zip') 179 180 166 181 def _render_file(self, req, repos, node, rev=None): 167 182 req.perm.assert_permission('FILE_VIEW') 168 183 -
trac/versioncontrol/web_ui/changeset.py
diff -ruN -x .svn trac-0.9b2/trac/versioncontrol/web_ui/changeset.py anydiff-branch/trac/versioncontrol/web_ui/changeset.py
old new 22 22 23 23 from trac import mimeview, util 24 24 from trac.core import * 25 from trac.perm import IPermissionRequestor26 25 from trac.Search import ISearchSource, query_to_sql, shorten_result 27 26 from trac.Timeline import ITimelineEventProvider 28 27 from trac.versioncontrol import Changeset, Node 29 28 from trac.versioncontrol.svn_authz import SubversionAuthorizer 30 from trac.versioncontrol.diff import get_diff_options, hdf_diff, unified_diff31 29 from trac.web import IRequestHandler 32 from trac.web.chrome import add_link, add_stylesheet,INavigationContributor30 from trac.web.chrome import INavigationContributor 33 31 from trac.wiki import wiki_to_html, wiki_to_oneliner, IWikiSyntaxProvider 32 from trac.versioncontrol.web_ui.diff import AbstractDiffModule 34 33 34 class ChangesetModule(AbstractDiffModule): 35 35 36 class ChangesetModule(Component): 37 38 implements(INavigationContributor, IPermissionRequestor, IRequestHandler, 36 implements(INavigationContributor, 39 37 ITimelineEventProvider, IWikiSyntaxProvider, ISearchSource) 40 38 41 39 # INavigationContributor methods … … 46 44 def get_navigation_items(self, req): 47 45 return [] 48 46 49 # IPermissionRequestor methods 50 51 def get_permission_actions(self): 52 return ['CHANGESET_VIEW'] 53 54 # IRequestHandler methods 47 # (reimplemented) IRequestHandler methods 55 48 56 49 def match_request(self, req): 57 match = re.match(r'/changeset/([0-9]+) $', req.path_info)50 match = re.match(r'/changeset/([0-9]+)(/.*)?$', req.path_info) 58 51 if match: 59 52 req.args['rev'] = match.group(1) 53 path = match.group(2) 54 if path: 55 req.args['path'] = path 60 56 return 1 61 57 62 def process_request(self, req):63 req.perm.assert_permission('CHANGESET_VIEW')64 65 rev = req.args.get('rev')66 repos = self.env.get_repository(req.authname)67 authzperm = SubversionAuthorizer(self.env, req.authname)68 authzperm.assert_permission_for_changeset(rev)69 70 diff_options = get_diff_options(req)71 if req.args.has_key('update'):72 req.redirect(self.env.href.changeset(rev))73 74 chgset = repos.get_changeset(rev)75 req.check_modified(chgset.date,76 diff_options[0] + ''.join(diff_options[1]))77 78 format = req.args.get('format')79 if format == 'diff':80 self._render_diff(req, repos, chgset, diff_options)81 return82 elif format == 'zip':83 self._render_zip(req, repos, chgset)84 return85 86 self._render_html(req, repos, chgset, diff_options)87 add_link(req, 'alternate', '?format=diff', 'Unified Diff',88 'text/plain', 'diff')89 add_link(req, 'alternate', '?format=zip', 'Zip Archive',90 'application/zip', 'zip')91 add_stylesheet(req, 'common/css/changeset.css')92 add_stylesheet(req, 'common/css/diff.css')93 add_stylesheet(req, 'common/css/code.css')94 return 'changeset.cs', None95 96 58 # ITimelineEventProvider methods 97 59 98 60 def get_timeline_filters(self, req): … … 143 105 message 144 106 rev = repos.previous_rev(rev) 145 107 146 # Internal methods147 148 def _render_html(self, req, repos, chgset, diff_options):149 """HTML version"""150 req.hdf['title'] = '[%s]' % chgset.rev151 req.hdf['changeset'] = {152 'revision': chgset.rev,153 'time': util.format_datetime(chgset.date),154 'author': util.escape(chgset.author or 'anonymous'),155 'message': wiki_to_html(chgset.message or '--', self.env, req,156 escape_newlines=True)157 }158 159 oldest_rev = repos.oldest_rev160 if chgset.rev != oldest_rev:161 add_link(req, 'first', self.env.href.changeset(oldest_rev),162 'Changeset %s' % oldest_rev)163 previous_rev = repos.previous_rev(chgset.rev)164 add_link(req, 'prev', self.env.href.changeset(previous_rev),165 'Changeset %s' % previous_rev)166 youngest_rev = repos.youngest_rev167 if str(chgset.rev) != str(youngest_rev):168 next_rev = repos.next_rev(chgset.rev)169 add_link(req, 'next', self.env.href.changeset(next_rev),170 'Changeset %s' % next_rev)171 add_link(req, 'last', self.env.href.changeset(youngest_rev),172 'Changeset %s' % youngest_rev)173 174 edits = []175 idx = 0176 for path, kind, change, base_path, base_rev in chgset.get_changes():177 info = {'change': change}178 if base_path:179 info['path.old'] = base_path180 info['rev.old'] = base_rev181 info['browser_href.old'] = self.env.href.browser(base_path,182 rev=base_rev)183 if path:184 info['path.new'] = path185 info['rev.new'] = chgset.rev186 info['browser_href.new'] = self.env.href.browser(path,187 rev=chgset.rev)188 if change in (Changeset.COPY, Changeset.EDIT, Changeset.MOVE):189 edits.append((idx, path, kind, base_path, base_rev))190 req.hdf['changeset.changes.%d' % idx] = info191 idx += 1192 193 hidden_properties = [p.strip() for p194 in self.config.get('browser', 'hide_properties',195 'svk:merge').split(',')]196 197 for idx, path, kind, base_path, base_rev in edits:198 old_node = repos.get_node(base_path or path, base_rev)199 new_node = repos.get_node(path, chgset.rev)200 201 # Property changes202 old_props = old_node.get_properties()203 new_props = new_node.get_properties()204 changed_props = {}205 if old_props != new_props:206 for k,v in old_props.items():207 if not k in new_props:208 changed_props[k] = {'old': v}209 elif v != new_props[k]:210 changed_props[k] = {'old': v, 'new': new_props[k]}211 for k,v in new_props.items():212 if not k in old_props:213 changed_props[k] = {'new': v}214 for k in hidden_properties:215 if k in changed_props:216 del changed_props[k]217 req.hdf['changeset.changes.%d.props' % idx] = changed_props218 219 if kind == Node.DIRECTORY:220 continue221 222 # Content changes223 default_charset = self.config.get('trac', 'default_charset')224 old_content = old_node.get_content().read()225 if mimeview.is_binary(old_content):226 continue227 charset = mimeview.get_charset(old_node.content_type) or \228 default_charset229 old_content = util.to_utf8(old_content, charset)230 231 new_content = new_node.get_content().read()232 if mimeview.is_binary(new_content):233 continue234 charset = mimeview.get_charset(new_node.content_type) or \235 default_charset236 new_content = util.to_utf8(new_content, charset)237 238 if old_content != new_content:239 context = 3240 for option in diff_options[1]:241 if option.startswith('-U'):242 context = int(option[2:])243 break244 tabwidth = int(self.config.get('diff', 'tab_width',245 self.config.get('mimeviewer',246 'tab_width')))247 changes = hdf_diff(old_content.splitlines(),248 new_content.splitlines(),249 context, tabwidth,250 ignore_blank_lines='-B' in diff_options[1],251 ignore_case='-i' in diff_options[1],252 ignore_space_changes='-b' in diff_options[1])253 req.hdf['changeset.changes.%d.diff' % idx] = changes254 255 def _render_diff(self, req, repos, chgset, diff_options):256 """Raw Unified Diff version"""257 req.send_response(200)258 req.send_header('Content-Type', 'text/plain;charset=utf-8')259 req.send_header('Content-Disposition',260 'filename=Changeset%s.diff' % req.args.get('rev'))261 req.end_headers()262 263 for path, kind, change, base_path, base_rev in chgset.get_changes():264 if change == Changeset.ADD:265 old_node = None266 else:267 old_node = repos.get_node(base_path or path, base_rev)268 if change == Changeset.DELETE:269 new_node = None270 else:271 new_node = repos.get_node(path, chgset.rev)272 273 # TODO: Property changes274 275 # Content changes276 if kind == 'dir':277 continue278 279 default_charset = self.config.get('trac', 'default_charset')280 new_content = old_content = ''281 new_node_info = old_node_info = ('','')282 283 if old_node:284 charset = mimeview.get_charset(old_node.content_type) or \285 default_charset286 old_content = util.to_utf8(old_node.get_content().read(),287 charset)288 old_node_info = (old_node.path, old_node.rev)289 if mimeview.is_binary(old_content):290 continue291 292 if new_node:293 charset = mimeview.get_charset(new_node.content_type) or \294 default_charset295 new_content = util.to_utf8(new_node.get_content().read(),296 charset)297 new_node_info = (new_node.path, new_node.rev)298 if mimeview.is_binary(new_content):299 continue300 301 if old_content != new_content:302 context = 3303 for option in diff_options[1]:304 if option.startswith('-U'):305 context = int(option[2:])306 break307 req.write('Index: ' + path + util.CRLF)308 req.write('=' * 67 + util.CRLF)309 req.write('--- %s (revision %s)' % old_node_info +310 util.CRLF)311 req.write('+++ %s (revision %s)' % new_node_info +312 util.CRLF)313 for line in unified_diff(old_content.splitlines(),314 new_content.splitlines(), context,315 ignore_blank_lines='-B' in diff_options[1],316 ignore_case='-i' in diff_options[1],317 ignore_space_changes='-b' in diff_options[1]):318 req.write(line + util.CRLF)319 320 def _render_zip(self, req, repos, chgset):321 """ZIP archive with all the added and/or modified files."""322 req.send_response(200)323 req.send_header('Content-Type', 'application/zip')324 req.send_header('Content-Disposition',325 'filename=Changeset%s.zip' % chgset.rev)326 req.end_headers()327 328 try:329 from cStringIO import StringIO330 except ImportError:331 from StringIO import StringIO332 from zipfile import ZipFile, ZipInfo, ZIP_DEFLATED333 334 buf = StringIO()335 zipfile = ZipFile(buf, 'w', ZIP_DEFLATED)336 for path, kind, change, base_path, base_rev in chgset.get_changes():337 if kind == Node.FILE and change != Changeset.DELETE:338 node = repos.get_node(path, chgset.rev)339 zipinfo = ZipInfo()340 zipinfo.filename = node.path341 zipinfo.date_time = time.gmtime(node.last_modified)[:6]342 zipinfo.compress_type = ZIP_DEFLATED343 zipfile.writestr(zipinfo, node.get_content().read())344 zipfile.close()345 req.write(buf.getvalue())346 108 347 109 # IWikiSyntaxProvider methods 348 110 349 111 def get_wiki_syntax(self): 350 yield (r"!?\[\d+ \]|(?:\b|!)r\d+\b(?!:\d)",112 yield (r"!?\[\d+(?:/[^\]]*)?\]|(?:\b|!)r\d+\b(?!:\d)", 351 113 lambda x, y, z: self._format_link(x, 'changeset', 352 114 y[0] == 'r' and y[1:] 353 or y[1:-1], y ))115 or y[1:-1], y, z)) 354 116 355 117 def get_link_resolvers(self): 356 118 yield ('changeset', self._format_link) 357 119 358 def _format_link(self, formatter, ns, rev, label): 120 def _format_link(self, formatter, ns, chgset, label, fullmatch=None): 121 sep = chgset.find('/') 122 if sep > 0: 123 rev, path = chgset[:sep], chgset[sep:] 124 else: 125 rev, path = chgset, None 359 126 cursor = formatter.db.cursor() 360 127 cursor.execute('SELECT message FROM revision WHERE rev=%s', (rev,)) 361 128 row = cursor.fetchone() 362 129 if row: 363 130 return '<a class="changeset" title="%s" href="%s">%s</a>' \ 364 131 % (util.escape(util.shorten_line(row[0])), 365 formatter.href.changeset(rev ), label)132 formatter.href.changeset(rev, path), label) 366 133 else: 367 return '<a class="missing changeset" href="%s" rel="nofollow">%s</a>' \ 368 % (formatter.href.changeset(rev), label) 134 return '<a class="missing changeset" href="%s"' \ 135 ' rel="nofollow">%s</a>' \ 136 % (formatter.href.changeset(rev, path), label) 369 137 370 138 # ISearchProvider methods 371 139 -
trac/versioncontrol/web_ui/diff.py
diff -ruN -x .svn trac-0.9b2/trac/versioncontrol/web_ui/diff.py anydiff-branch/trac/versioncontrol/web_ui/diff.py
old new 1 # -*- coding: iso8859-1 -*- 2 # 3 # Copyright (C) 2003-2005 Edgewall Software 4 # Copyright (C) 2003-2005 Jonas Borgström <jonas@edgewall.com> 5 # Copyright (C) 2004-2005 Christopher Lenz <cmlenz@gmx.de> 6 # All rights reserved. 7 # 8 # This software is licensed as described in the file COPYING, which 9 # you should have received as part of this distribution. The terms 10 # are also available at http://trac.edgewall.com/license.html. 11 # 12 # This software consists of voluntary contributions made by many 13 # individuals. For the exact contribution history, see the revision 14 # history and logs, available at http://projects.edgewall.com/trac/. 15 # 16 # Author: Jonas Borgström <jonas@edgewall.com> 17 # Christopher Lenz <cmlenz@gmx.de> 18 # Christian Boos <cboos@neuf.fr> 19 20 from __future__ import generators 21 import time 22 import re 23 import posixpath 24 from urllib import urlencode 25 26 from trac import mimeview, util 27 from trac.core import * 28 from trac.perm import IPermissionRequestor 29 from trac.versioncontrol import Changeset, Node 30 from trac.versioncontrol.diff import get_diff_options, hdf_diff, unified_diff 31 from trac.versioncontrol.svn_authz import SubversionAuthorizer 32 from trac.web import IRequestHandler 33 from trac.web.chrome import add_link, add_stylesheet 34 from trac.wiki import wiki_to_html, IWikiSyntaxProvider 35 36 class DiffArgs(dict): 37 def __getattr__(self,str): 38 return self[str] 39 40 41 class ChangesPermission(Component): 42 """Simple permission provider for changes related modules.""" 43 44 implements(IPermissionRequestor) 45 46 def get_permission_actions(self): 47 return ['CHANGESET_VIEW'] 48 49 50 class AbstractDiffModule(Component): 51 """Provide flexible functionality for showing sets of differences. 52 53 If the differences shown are coming from a specific changeset, 54 then that changeset informations can be shown too. 55 56 In addition, it is possible to show only a subset of the changeset: 57 Only the changes affecting a given path will be shown. 58 This is called the ''restricted'' changeset. 59 60 But the differences can also be computed in a more general way, 61 between two arbitrary paths and/or between two arbitrary revisions. 62 In that case, there's no changeset information displayed. 63 """ 64 65 abstract = True 66 67 implements(IRequestHandler) 68 69 # IRequestHandler methods 70 71 def match_request(self, req): 72 raise NotImplementedError 73 74 def process_request(self, req): 75 """The appropriate mode of operation is inferred from 76 the request parameters: 77 * If `old` and `new` parameters are given, it will be an 78 arbitrary set of differences: `chgset` is False. 79 * If `old_path` is given and is different from `path`, 80 it's a generalized diff, from `old_path@old` 81 to `path@new`: `restricted` is False. 82 * Otherwise those are differences between two arbitrary revisions 83 of a given path: `restricted` is True. 84 * Otherwise, we are dealing with a changeset only: `chgset` is True. 85 * If the `path` is not empty or not the root, then only 86 the changes affecting that path (i.e. itself, children or 87 ancestors) will be considered: `restricted` is True. 88 * Otherwise, it's the full changeset: `restricted` is False. 89 90 In any case, the given path@rev pair must exist. 91 """ 92 req.perm.assert_permission('CHANGESET_VIEW') 93 94 # -- retrieve arguments 95 path = req.args.get('path') 96 rev = req.args.get('rev') 97 old = req.args.get('old') 98 new = req.args.get('new') 99 old_path = req.args.get('old_path') 100 101 # -- normalize and check for special case 102 repos = self.env.get_repository(req.authname) 103 path = repos.normalize_path(path) 104 rev = repos.normalize_rev(rev) 105 if old_path: # Note: normalize_path now returns '/' if given 'None' 106 old_path = repos.normalize_path(old_path) 107 108 authzperm = SubversionAuthorizer(self.env, req.authname) 109 authzperm.assert_permission_for_changeset(rev) 110 111 if old_path == path and old and old == new: # revert to Changeset 112 rev = old 113 old_path = old = new = None 114 115 diff_options = get_diff_options(req) 116 117 # -- setup the `chgset` and `restricted` flags, see docstring above. 118 chgset = not old and not new and not old_path 119 if chgset: 120 restricted = path != '' and path != '/' # (subset or not) 121 else: 122 restricted = old_path == path # (same path or not) 123 124 # -- redirect if changing the diff options 125 if req.args.has_key('update'): 126 if chgset: 127 if restricted: 128 req.redirect(self.env.href.diff(path, rev=rev)) 129 else: 130 req.redirect(self.env.href.changeset(rev)) 131 else: 132 req.redirect(self.env.href.diff(path, new=new, 133 old_path=old_path, old=old)) 134 135 # -- preparing the diff arguments 136 if chgset: 137 prev = repos.get_node(path, rev).get_previous() 138 if prev: 139 prev_path, prev_rev = prev[:2] 140 else: 141 prev_path, prev_rev = path, repos.previous_rev(rev) 142 diff_args = DiffArgs(old_path=prev_path, old_rev=prev_rev, 143 new_path=path, new_rev=rev) 144 else: 145 if not new: 146 new = repos.youngest_rev 147 elif not old: 148 old = repos.youngest_rev 149 if not old_path: 150 old_path = path 151 diff_args = DiffArgs(old_path=old_path, old_rev=old, 152 new_path=path, new_rev=new) 153 if chgset: 154 chgset = repos.get_changeset(rev) 155 req.check_modified(chgset.date, 156 diff_options[0] + ''.join(diff_options[1])) 157 else: 158 pass # FIXME: what date should we choose for a diff? 159 160 req.hdf['diff'] = diff_args 161 162 format = req.args.get('format') 163 164 if format in ['diff', 'zip']: 165 # choosing an appropriate filename 166 rpath = path.replace('/','_') 167 if chgset: 168 if restricted: 169 filename = 'changeset_%s_r%s' % (rpath, rev) 170 else: 171 filename = 'changeset_r%s' % rev 172 else: 173 if restricted: 174 filename = 'diff-%s-from-r%s-to-r%s' \ 175 % (rpath, old, new) 176 elif old_path == '/': # special case for download (#238) 177 filename = '%s-r%s' % (rpath, old) 178 else: 179 filename = 'diff-from-%s-r%s-to-%s-r%s' \ 180 % (old_path.replace('/','_'), old, rpath, new) 181 if format == 'diff': 182 self._render_diff(req, filename, repos, diff_args, 183 diff_options) 184 return 185 elif format == 'zip': 186 self._render_zip(req, filename, repos, diff_args) 187 return 188 189 # -- HTML format 190 self._render_html(req, repos, chgset, restricted, 191 diff_args, diff_options) 192 if chgset: 193 diff_params = 'rev=%s' % rev 194 else: 195 diff_params = urlencode({'path': path, 196 'new': new, 197 'old_path': old_path, 198 'old': old}) 199 add_link(req, 'alternate', '?format=diff&'+diff_params, 'Unified Diff', 200 'text/plain', 'diff') 201 add_link(req, 'alternate', '?format=zip&'+diff_params, 'Zip Archive', 202 'application/zip', 'zip') 203 add_stylesheet(req, 'common/css/changeset.css') 204 add_stylesheet(req, 'common/css/diff.css') 205 add_stylesheet(req, 'common/css/code.css') 206 return 'diff.cs', None 207 208 209 # Internal methods 210 211 def _render_html(self, req, repos, chgset, restricted, diff, diff_options): 212 """ 213 HTML version 214 """ 215 req.hdf['diff'] = { 216 'chgset': chgset and True, 217 'restricted': restricted, 218 'href': { 'new_rev': self.env.href.changeset(diff.new_rev), 219 'old_rev': self.env.href.changeset(diff.old_rev), 220 'new_path': self.env.href.browser(diff.new_path, 221 rev=diff.new_rev), 222 'old_path': self.env.href.browser(diff.old_path, 223 rev=diff.old_rev) 224 } 225 } 226 227 if chgset: # Changeset Mode (possibly restricted on a path) 228 path, rev = diff.new_path, diff.new_rev 229 230 # -- getting the deltas from the Changeset.get_changes method 231 def get_deltas(): 232 old_node = new_node = None 233 for npath, kind, change, opath, orev in chgset.get_changes(): 234 if restricted and \ 235 not (npath.startswith(path) # npath is below 236 or path.startswith(npath)): # npath is above 237 continue 238 if change != Changeset.ADD: 239 old_node = repos.get_node(opath, orev) 240 if change != Changeset.DELETE: 241 new_node = repos.get_node(npath, rev) 242 yield old_node, new_node, kind, change 243 244 def _changeset_title(rev): 245 if restricted: 246 return 'Changeset %s for %s' % (rev, path) 247 else: 248 return 'Changeset %s' % rev 249 250 title = _changeset_title(rev) 251 req.hdf['changeset'] = { 252 'revision': chgset.rev, 253 'time': util.format_datetime(chgset.date), 254 'author': util.escape(chgset.author or 'anonymous'), 255 'message': wiki_to_html(chgset.message or '--', self.env, req, 256 escape_newlines=True) 257 } 258 oldest_rev = repos.oldest_rev 259 if chgset.rev != oldest_rev: 260 if restricted: 261 prev = repos.get_node(path, rev).get_previous() 262 if prev: 263 prev_path, prev_rev = prev[:2] 264 prev_href = self.env.href.changeset(prev_rev, prev_path) 265 else: 266 prev_path = prev_rev = None 267 else: 268 prev_path = diff.old_path 269 prev_rev = repos.previous_rev(chgset.rev) 270 add_link(req, 'first', self.env.href.changeset(oldest_rev), 271 'Changeset %s' % oldest_rev) 272 prev_href = self.env.href.changeset(prev_rev) 273 if prev_rev: 274 add_link(req, 'prev', prev_href, _changeset_title(prev_rev)) 275 youngest_rev = repos.youngest_rev 276 if str(chgset.rev) != str(youngest_rev): 277 if restricted: 278 next_rev = next_href = None 279 # FIXME: find an effective way to find the next rev 280 else: 281 next_rev = repos.next_rev(chgset.rev) 282 next_href = self.env.href.changeset(next_rev) 283 add_link(req, 'last', 284 self.env.href.diff(path, rev=youngest_rev), 285 'Changeset %s' % youngest_rev) 286 if next_rev: 287 add_link(req, 'next', next_href, _changeset_title(next_rev)) 288 289 else: # Diff Mode 290 # -- getting the deltas from the Repository.get_deltas method 291 def get_deltas(): 292 for d in repos.get_deltas(**diff): 293 yield d 294 295 reverse_href = self.env.href.diff(diff.old_path, 296 new=diff.old_rev, 297 old_path=diff.new_path, 298 old=diff.new_rev) 299 req.hdf['diff.reverse_href'] = reverse_href 300 title = self.title_for_diff(diff) 301 req.hdf['title'] = title 302 303 def _change_info(old_node, new_node, change): 304 info = {'change': change} 305 if old_node: 306 info['path.old'] = old_node.path 307 info['rev.old'] = old_node.rev # this is the created rev. 308 old_href = self.env.href.browser(old_node.path, 309 rev=diff.old_rev) 310 # Reminder: old_node.path may not exist at old_node.rev 311 info['browser_href.old'] = old_href 312 if new_node: 313 info['path.new'] = new_node.path 314 info['rev.new'] = new_node.rev # created rev. 315 new_href = self.env.href.browser(new_node.path, 316 rev=diff.new_rev) 317 # (same remark as above) 318 info['browser_href.new'] = new_href 319 return info 320 321 hidden_properties = [p.strip() for p 322 in self.config.get('browser', 'hide_properties', 323 'svk:merge').split(',')] 324 325 def _prop_changes(old_node, new_node): 326 old_props = old_node.get_properties() 327 new_props = new_node.get_properties() 328 changed_props = {} 329 if old_props != new_props: 330 for k,v in old_props.items(): 331 if not k in new_props: 332 changed_props[k] = {'old': v} 333 elif v != new_props[k]: 334 changed_props[k] = {'old': v, 'new': new_props[k]} 335 for k,v in new_props.items(): 336 if not k in old_props: 337 changed_props[k] = {'new': v} 338 for k in hidden_properties: 339 if k in changed_props: 340 del changed_props[k] 341 return changed_props 342 343 def _content_changes(old_node, new_node): 344 """ 345 Returns the list of differences. 346 The list is empty when no differences between comparable files 347 are detected, but the return value is None for non-comparable files. 348 """ 349 default_charset = self.config.get('trac', 'default_charset') 350 old_content = old_node.get_content().read() 351 if mimeview.is_binary(old_content): 352 return None 353 charset = mimeview.get_charset(old_node.content_type) or \ 354 default_charset 355 old_content = util.to_utf8(old_content, charset) 356 357 new_content = new_node.get_content().read() 358 if mimeview.is_binary(new_content): 359 return None 360 charset = mimeview.get_charset(new_node.content_type) or \ 361 default_charset 362 new_content = util.to_utf8(new_content, charset) 363 364 if old_content != new_content: 365 context = 3 366 options = diff_options[1] 367 for option in options: 368 if option.startswith('-U'): 369 context = int(option[2:]) 370 break 371 tabwidth = int(self.config.get('diff', 'tab_width', 372 self.config.get('mimeviewer', 373 'tab_width'))) 374 return hdf_diff(old_content.splitlines(), 375 new_content.splitlines(), 376 context, tabwidth, 377 ignore_blank_lines='-B' in options, 378 ignore_case='-i' in options, 379 ignore_space_changes='-b' in options) 380 else: 381 return [] 382 383 idx = 0 384 for old_node, new_node, kind, change in get_deltas(): 385 if change != Changeset.EDIT: 386 show_entry = True 387 else: 388 show_entry = False 389 assert old_node and new_node 390 props = _prop_changes(old_node, new_node) 391 if props: 392 req.hdf['diff.changes.%d.props' % idx] = props 393 show_entry = True 394 if kind == Node.FILE: 395 diffs = _content_changes(old_node, new_node) 396 if diffs != []: 397 if diffs: 398 req.hdf['diff.changes.%d.diff' % idx] = diffs 399 # elif None (means: manually compare to (previous)) 400 show_entry = True 401 if show_entry: 402 info = _change_info(old_node, new_node, change) 403 req.hdf['diff.changes.%d' % idx] = info 404 idx += 1 # the sequence should be immutable 405 406 def _render_diff(self, req, filename, repos, diff, diff_options): 407 """Raw Unified Diff version""" 408 req.send_response(200) 409 req.send_header('Content-Type', 'text/plain;charset=utf-8') 410 req.send_header('Content-Disposition', 411 'filename=%s.diff' % filename) 412 req.end_headers() 413 414 for old_node, new_node, kind, change in repos.get_deltas(**diff): 415 # TODO: Property changes 416 417 # Content changes 418 if kind == Node.DIRECTORY: 419 continue 420 421 default_charset = self.config.get('trac', 'default_charset') 422 new_content = old_content = '' 423 new_node_info = old_node_info = ('','') 424 425 if old_node: 426 charset = mimeview.get_charset(old_node.content_type) or \ 427 default_charset 428 old_content = util.to_utf8(old_node.get_content().read(), 429 charset) 430 old_node_info = (old_node.path, old_node.rev) 431 if mimeview.is_binary(old_content): 432 continue 433 434 if new_node: 435 charset = mimeview.get_charset(new_node.content_type) or \ 436 default_charset 437 new_content = util.to_utf8(new_node.get_content().read(), 438 charset) 439 new_node_info = (new_node.path, new_node.rev) 440 if mimeview.is_binary(new_content): 441 continue 442 new_path = new_node.path 443 else: 444 old_node_path = repos.normalize_path(old_node.path) 445 diff_old_path = repos.normalize_path(diff.old_path) 446 new_path = posixpath.join(diff.new_path, 447 old_node_path[len(diff_old_path)+1:]) 448 449 if old_content != new_content: 450 context = 3 451 options = diff_options[1] 452 for option in options: 453 if option.startswith('-U'): 454 context = int(option[2:]) 455 break 456 if not old_node_info[0]: 457 old_node_info = new_node_info # support for 'A'dd changes 458 req.write('Index: ' + new_path + util.CRLF) 459 req.write('=' * 67 + util.CRLF) 460 req.write('--- %s (revision %s)' % old_node_info + 461 util.CRLF) 462 req.write('+++ %s (revision %s)' % new_node_info + 463 util.CRLF) 464 for line in unified_diff(old_content.splitlines(), 465 new_content.splitlines(), context, 466 ignore_blank_lines='-B' in options, 467 ignore_case='-i' in options, 468 ignore_space_changes='-b' in options): 469 req.write(line + util.CRLF) 470 471 def _render_zip(self, req, filename, repos, diff): 472 """ZIP archive with all the added and/or modified files.""" 473 new_rev = diff.new_rev 474 req.send_response(200) 475 req.send_header('Content-Type', 'application/zip') 476 req.send_header('Content-Disposition', 477 'filename=%s.zip' % filename) 478 479 try: 480 from cStringIO import StringIO 481 except ImportError: 482 from StringIO import StringIO 483 from zipfile import ZipFile, ZipInfo, ZIP_DEFLATED 484 485 buf = StringIO() 486 zipfile = ZipFile(buf, 'w', ZIP_DEFLATED) 487 for old_node, new_node, kind, change in repos.get_deltas(**diff): 488 if kind == Node.FILE and change != Changeset.DELETE: 489 assert new_node 490 zipinfo = ZipInfo() 491 zipinfo.filename = new_node.path 492 zipinfo.date_time = time.gmtime(new_node.last_modified)[:6] 493 zipinfo.compress_type = ZIP_DEFLATED 494 zipfile.writestr(zipinfo, new_node.get_content().read()) 495 zipfile.close() 496 497 buf.seek(0, 2) # be sure to be at the end 498 req.send_header("Content-Length", buf.tell()) 499 req.end_headers() 500 501 req.write(buf.getvalue()) 502 503 def title_for_diff(self, diff): 504 if diff.new_path == diff.old_path: # ''diff between 2 revisions'' mode 505 return 'Diff r%s:%s for %s' \ 506 % (diff.old_rev or 'latest', diff.new_rev or 'latest', 507 diff.new_path or '/') 508 else: # ''arbitrary diff'' mode 509 return 'Diff from %s@%s to %s@%s' \ 510 % (diff.old_path or '/', diff.old_rev or 'latest', 511 diff.new_path or '/', diff.new_rev or 'latest') 512 513 514 515 class DiffModule(AbstractDiffModule): 516 517 implements(IWikiSyntaxProvider) 518 519 # (reimplemented) IRequestHandler methods 520 521 def match_request(self, req): 522 match = re.match(r'/diff(?:(/.*)|$)', req.path_info) 523 if match: 524 if match.group(1): 525 req.args['path'] = match.group(1) 526 return 1 527 528 # IWikiSyntaxProvider methods 529 530 def get_wiki_syntax(self): 531 return [] 532 533 def get_link_resolvers(self): 534 yield ('diff', self._format_link) 535 536 def _format_link(self, formatter, ns, params, label): 537 def pathrev(path): 538 if '@' in path: 539 return path.split('@', 1) 540 else: 541 return (path, None) 542 if '//' in params: 543 p1, p2 = params.split('//', 1) 544 old, new = pathrev(p1), pathrev(p2) 545 diff = DiffArgs(old_path=old[0], old_rev=old[1], 546 new_path=new[0], new_rev=new[1]) 547 else: 548 old_path, old_rev = pathrev(params) 549 new_rev = None 550 if old_rev and ':' in old_rev: 551 old_rev, new_rev = old_rev.split(':', 1) 552 diff = DiffArgs(old_path=old_path, old_rev=old_rev, 553 new_path=old_path, new_rev=new_rev) 554 title = self.title_for_diff(diff) 555 href = formatter.href.diff(diff.new_path, new=diff.new_rev, 556 old_path=diff.old_path, old=diff.old_rev) 557 return '<a class="changeset" title="%s" href="%s">%s</a>' \ 558 % (title, href, label) 559 560 561 class AnyDiffModule(Component): 562 563 implements(IRequestHandler) 564 565 # IRequestHandler methods 566 567 def match_request(self, req): 568 return re.match(r'/anydiff$', req.path_info) 569 570 def process_request(self, req): 571 # -- retrieve arguments 572 new_path = req.args.get('new_path') 573 new_rev = req.args.get('new_rev') 574 old_path = req.args.get('old_path') 575 old_rev = req.args.get('old_rev') 576 577 # -- normalize 578 repos = self.env.get_repository(req.authname) 579 new_path = repos.normalize_path(new_path) 580 new_rev = repos.normalize_rev(new_rev) 581 old_path = repos.normalize_path(old_path) 582 old_rev = repos.normalize_rev(old_rev) 583 584 authzperm = SubversionAuthorizer(self.env, req.authname) 585 authzperm.assert_permission_for_changeset(new_rev) 586 authzperm.assert_permission_for_changeset(old_rev) 587 588 # -- prepare rendering 589 req.hdf['anydiff'] = { 590 'new_path': new_path, 591 'new_rev': new_rev, 592 'old_path': old_path, 593 'old_rev': old_rev, 594 'diff_href': self.env.href.diff(), 595 } 596 597 return 'anydiff.cs', None -
trac/versioncontrol/web_ui/__init__.py
diff -ruN -x .svn trac-0.9b2/trac/versioncontrol/web_ui/__init__.py anydiff-branch/trac/versioncontrol/web_ui/__init__.py
old new 1 1 from trac.versioncontrol.web_ui.browser import * 2 2 from trac.versioncontrol.web_ui.changeset import * 3 3 from trac.versioncontrol.web_ui.log import * 4 from trac.versioncontrol.web_ui.diff import * -
trac/versioncontrol/web_ui/log.py
diff -ruN -x .svn trac-0.9b2/trac/versioncontrol/web_ui/log.py anydiff-branch/trac/versioncontrol/web_ui/log.py
old new 67 67 stop_rev = req.args.get('stop_rev') 68 68 verbose = req.args.get('verbose') 69 69 limit = LOG_LIMIT 70 old = req.args.get('old') 71 new = req.args.get('new') 72 73 repos = self.env.get_repository(req.authname) 74 normpath = repos.normalize_path(path) 75 rev = str(repos.normalize_rev(rev)) 76 77 if old and new: 78 osep = util.unescape(old).rindex('#') 79 nsep = util.unescape(new).rindex('#') 80 old_path, old_rev = old[:osep], old[osep+1:] 81 new_path, new_rev = new[:nsep], new[nsep+1:] 82 req.redirect(self.env.href.diff(new_path, new=new_rev, 83 old_path=old_path, old=old_rev)) 70 84 71 85 req.hdf['title'] = path + ' (log)' 72 86 req.hdf['log'] = { … … 83 97 if path_links: 84 98 add_link(req, 'up', path_links[-1]['href'], 'Parent directory') 85 99 86 repos = self.env.get_repository(req.authname)87 normpath = repos.normalize_path(path)88 rev = str(repos.normalize_rev(rev))89 100 90 101 # ''Node'' history uses `get_node()`, 91 102 # ''Path'' history uses `get_path_history()` -
trac/wiki/tests/wiki-tests.txt
diff -ruN -x .svn trac-0.9b2/trac/wiki/tests/wiki-tests.txt anydiff-branch/trac/wiki/tests/wiki-tests.txt
old new 36 36 <a class="ext-link" href="http://www.edgewall.com/"><span class="icon"></span>http://www.edgewall.com/</a> 37 37 </p> 38 38 ============================== 39 #1, [1], r1, {1} 39 #1, {1} 40 [1], r1 41 [1/README.txt] 40 42 ------------------------------ 41 43 <p> 42 <a class="missing ticket" href="/ticket/1" rel="nofollow">#1</a>, <a class="missing changeset" href="/changeset/1" rel="nofollow">[1]</a>, <a class="missing changeset" href="/changeset/1" rel="nofollow">r1</a>, <a class="report" href="/report/1">{1}</a> 44 <a class="missing ticket" href="/ticket/1" rel="nofollow">#1</a>, <a class="report" href="/report/1">{1}</a> 45 <a class="missing changeset" href="/changeset/1" rel="nofollow">[1]</a>, <a class="missing changeset" href="/changeset/1" rel="nofollow">r1</a> 46 <a class="missing changeset" href="/changeset/1/README.txt" rel="nofollow">[1/README.txt]</a> 43 47 </p> 44 48 ============================== 45 49 !#1, ![1], !r1, !{1} … … 60 64 [1:2], r1:2, [12:23], r12:23 61 65 </p> 62 66 ============================== 63 ticket:1, changeset:1, report:1, source:foo/bar 67 ticket:1, report:1, source:foo/bar 68 changeset:1, changeset:1/README.txt 64 69 65 70 Issue [ticket:1], CS[changeset:1], Listing [report:1], File [source:foo/bar] 66 71 ------------------------------ 67 72 <p> 68 <a class="missing ticket" href="/ticket/1" rel="nofollow">ticket:1</a>, <a class="missing changeset" href="/changeset/1" rel="nofollow">changeset:1</a>, <a class="report" href="/report/1">report:1</a>, <a class="source" href="/browser/foo/bar">source:foo/bar</a> 73 <a class="missing ticket" href="/ticket/1" rel="nofollow">ticket:1</a>, <a class="report" href="/report/1">report:1</a>, <a class="source" href="/browser/foo/bar">source:foo/bar</a> 74 <a class="missing changeset" href="/changeset/1" rel="nofollow">changeset:1</a>, <a class="missing changeset" href="/changeset/1/README.txt" rel="nofollow">changeset:1/README.txt</a> 69 75 </p> 70 76 <p> 71 77 Issue <a class="missing ticket" href="/ticket/1" rel="nofollow">1</a>, CS<a class="missing changeset" href="/changeset/1" rel="nofollow">1</a>, Listing <a class="report" href="/report/1">1</a>, File <a class="source" href="/browser/foo/bar">foo/bar</a> … … 79 85 <a class="source" href="/browser/foo/bar">source foo/bar</a>, <a class="ext-link" href="http://www.edgewall.com/"><span class="icon"></span>edgewall</a> 80 86 </p> 81 87 ============================== 88 diff:trunk//branch 89 diff:trunk@12//branch@23 90 diff:trunk@12:23 91 diff:@12:23 92 ------------------------------ 93 <p> 94 <a class="changeset" title="Diff from trunk@latest to branch@latest" href="/diff/branch?old_path=trunk">diff:trunk//branch</a> 95 <a class="changeset" title="Diff from trunk@12 to branch@23" href="/diff/branch?new=23&old=12&old_path=trunk">diff:trunk@12//branch@23</a> 96 <a class="changeset" title="Diff r12:23 for trunk" href="/diff/trunk?new=23&old=12&old_path=trunk">diff:trunk@12:23</a> 97 <a class="changeset" title="Diff r12:23 for /" href="/diff/?new=23&old=12&old_path=">diff:@12:23</a> 98 </p> 99 ============================== 82 100 CamelCase AlabamA ABc AlaBamA FooBar 83 101 ------------------------------ 84 102 <p> -
wiki-default/WikiStart
diff -ruN -x .svn trac-0.9b2/wiki-default/WikiStart anydiff-branch/wiki-default/WikiStart
old new 1 = Welcome to Trac 0.9b2 =2 3 Trac is a '''minimalistic''' approach to '''web-based''' management of 4 '''software projects'''. Its goal is to simplify effective tracking and handling of software issues, enhancements and overall progress. 5 6 All aspects of Trac have been designed with the single goal to 7 '''help developers write great software''' while '''staying out of the way''' 8 and imposing as little as possible on a team's established process and 9 culture. 10 11 As all Wiki pages, this page is editable, this means that you can 12 modify the contents of this page simply by using your 13 web-browser. Simply click on the "Edit this page" link at the bottom 14 of the page. WikiFormatting will give you a detailed description of 15 available Wiki formatting commands. 16 17 "[wiki:TracAdmin trac-admin] ''yourenvdir'' initenv" created 18 a new Trac environment, containing a default set of wiki pages and some sample 19 data. This newly created environment also contains 20 [wiki:TracGuide documentation] to help you get started with your project. 21 22 You can use [wiki:TracAdmin trac-admin] to configure 23 [http://trac.edgewall.com/ Trac] to better fit your project, especially in 24 regard to ''components'', ''versions'' and ''milestones''. 25 26 27 TracGuide is a good place to start. 28 29 Enjoy! [[BR]] 30 ''The Trac Team'' 31 32 == Starting Points == 33 34 * TracGuide -- Built-in Documentation 35 * [http://projects.edgewall.com/trac/ The Trac project] -- Trac Open Source Project 36 * [http://projects.edgewall.com/trac/wiki/TracFaq Trac FAQ] -- Frequently Asked Questions 37 * TracSupport -- Trac Support 38 39 For a complete list of local wiki pages, see TitleIndex. 40 41 Trac is brought to you by [http://www.edgewall.com/ Edgewall Software], 42 providing professional Linux and software development services to clients 43 worldwide. Visit http://www.edgewall.com/ for more information. 44 1 = Welcome to Trac 0.9b2-anydiff = 2 3 Trac is a '''minimalistic''' approach to '''web-based''' management of 4 '''software projects'''. Its goal is to simplify effective tracking and handling of software issues, enhancements and overall progress. 5 6 All aspects of Trac have been designed with the single goal to 7 '''help developers write great software''' while '''staying out of the way''' 8 and imposing as little as possible on a team's established process and 9 culture. 10 11 As all Wiki pages, this page is editable, this means that you can 12 modify the contents of this page simply by using your 13 web-browser. Simply click on the "Edit this page" link at the bottom 14 of the page. WikiFormatting will give you a detailed description of 15 available Wiki formatting commands. 16 17 "[wiki:TracAdmin trac-admin] ''yourenvdir'' initenv" created 18 a new Trac environment, containing a default set of wiki pages and some sample 19 data. This newly created environment also contains 20 [wiki:TracGuide documentation] to help you get started with your project. 21 22 You can use [wiki:TracAdmin trac-admin] to configure 23 [http://trac.edgewall.com/ Trac] to better fit your project, especially in 24 regard to ''components'', ''versions'' and ''milestones''. 25 26 27 TracGuide is a good place to start. 28 29 Enjoy! [[BR]] 30 ''The Trac Team'' 31 32 == Starting Points == 33 34 * TracGuide -- Built-in Documentation 35 * [http://projects.edgewall.com/trac/ The Trac project] -- Trac Open Source Project 36 * [http://projects.edgewall.com/trac/wiki/TracFaq Trac FAQ] -- Frequently Asked Questions 37 * TracSupport -- Trac Support 38 39 For a complete list of local wiki pages, see TitleIndex. 40 41 Trac is brought to you by [http://www.edgewall.com/ Edgewall Software], 42 providing professional Linux and software development services to clients 43 worldwide. Visit http://www.edgewall.com/ for more information. 44
