Ticket #2028: trac-0.9b1.tar.gz-anydiff.patch
| File trac-0.9b1.tar.gz-anydiff.patch, 83.6 KB (added by cboos, 6 years ago) |
|---|
-
htdocs/css/browser.css
diff -urN -x .svn trac-0.9b1/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 -urN -x .svn trac-0.9b1/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 -urN -x .svn trac-0.9b1/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 -urN -x .svn trac-0.9b1/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.restricted_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 -urN -x .svn trac-0.9b1/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.old ?> 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 -urN -x .svn trac-0.9b1/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.old ?> 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 -urN -x .svn trac-0.9b1/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> … … 115 131 <td class="author"><?cs var:log.changes[item.rev].author ?></td> 116 132 <td class="summary"><?cs var:log.changes[item.rev].message ?></td> 117 133 </tr><?cs 134 set:idx = idx + 1 ?><?cs 118 135 /each ?> 119 136 </tbody> 120 </table><?cs 137 </table> 138 <div class="buttons"><input type="submit" value="View changes" 139 title="Diff from Old Revision to New Revision (select them above)" /> 140 </div> 141 </form><?cs 121 142 if:len(links.prev) || len(links.next) ?><div id="paging" class="nav"><ul><?cs 122 143 if:len(links.prev) ?><li class="first<?cs 123 144 if:!len(links.next) ?> last<?cs /if ?>">← <a href="<?cs -
templates/wiki.cs
diff -urN -x .svn trac-0.9b1/templates/wiki.cs anydiff-branch/templates/wiki.cs
old new 151 151 var:wiki.page_name ?></a></h1> 152 152 <?cs if:len(wiki.history) ?><form method="get" action=""> 153 153 <input type="hidden" name="action" value="diff" /> 154 <div class="buttons"> 155 <input type="submit" value="View changes" /> 156 </div> 154 157 <table id="wikihist" class="listing" summary="Change history"> 155 158 <thead><tr> 156 159 <th class="diff"></th> -
trac/__init__.py
diff -urN -x .svn trac-0.9b1/trac/__init__.py anydiff-branch/trac/__init__.py
old new 10 10 """ 11 11 __docformat__ = 'epytext en' 12 12 13 __version__ = '0.9b1 '13 __version__ = '0.9b1-anydiff' 14 14 __url__ = 'http://trac.edgewall.com/' 15 15 __copyright__ = '(C) 2003-2005 Edgewall Software' 16 16 __license__ = 'BSD' -
trac/versioncontrol/api.py
Binary files trac-0.9b1/trac/__init__.pyc and anydiff-branch/trac/__init__.pyc differ diff -urN -x .svn trac-0.9b1/trac/versioncontrol/api.py anydiff-branch/trac/versioncontrol/api.py
old new 38 38 """ 39 39 raise NotImplementedError 40 40 41 def has_node(self, path, rev): 42 """ 43 Tell if there's a node at the specified (path,rev) combination. 44 """ 45 raise NotImplementedError 46 41 47 def get_node(self, path, rev=None): 42 48 """ 43 49 Retrieve a Node (directory or file) from the repository at the … … 110 116 'None' is a valid revision value and represents the youngest revision. 111 117 """ 112 118 return NotImplementedError 113 119 120 def get_deltas(self, old_path, old_rev, new_path, new_rev, ignore_ancestry=1): 121 """ 122 Generator that yields change tuples (old_node, new_node, kind, change) 123 for each node change between the two arbitrary (path,rev) pairs. 124 125 The old_node is assumed to be None when the change is an ADD, 126 the new_node is assumed to be None when the change is a DELETE. 127 """ 128 raise NotImplementedError 129 114 130 115 131 class Node(object): 116 132 """ … … 148 164 node (if the underlying version control system supports that), which 149 165 will be indicated by the first element of the tuple (i.e. the path) 150 166 changing. 167 Starts with an entry for the current revision. 151 168 """ 152 169 raise NotImplementedError 153 170 171 def get_previous(self): 172 """ 173 Return the (path, rev, chg) tuple corresponding to the previous 174 revision for that node. 175 """ 176 skip = True 177 for p in self.get_history(2): 178 if skip: 179 skip = False 180 else: 181 return p 182 154 183 def get_properties(self): 155 184 """ 156 185 Returns a dictionary containing the properties (meta-data) of the node. -
trac/versioncontrol/cache.py
diff -urN -x .svn trac-0.9b1/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 -urN -x .svn trac-0.9b1/trac/versioncontrol/diff.py anydiff-branch/trac/versioncontrol/diff.py
old new 215 215 ignore_space_changes) 216 216 for group in _group_opcodes(opcodes, context): 217 217 i1, i2, j1, j2 = group[0][1], group[-1][2], group[0][3], group[-1][4] 218 if i1 == 0 and i2 == 0: 219 i1, i2 = -1, -1 # support for 'A'dd changes 218 220 yield '@@ -%d,%d +%d,%d @@' % (i1 + 1, i2 - i1, j1 + 1, j2 - j1) 219 221 for tag, i1, i2, j1, j2 in group: 220 222 if tag == 'equal': -
trac/versioncontrol/svn_fs.py
diff -urN -x .svn trac-0.9b1/trac/versioncontrol/svn_fs.py anydiff-branch/trac/versioncontrol/svn_fs.py
old new 22 22 import os.path 23 23 import time 24 24 import weakref 25 import posixpath 25 26 26 27 from svn import fs, repos, core, delta 27 28 … … 203 204 def __del__(self): 204 205 self.close() 205 206 207 def has_node(self, path, rev): 208 rev_root = fs.revision_root(self.fs_ptr, rev, self.pool()) 209 node_type = fs.check_path(rev_root, path, self.pool()) 210 return node_type in _kindmap 211 206 212 def normalize_path(self, path): 207 return path == '/' and path or path .strip('/')213 return path == '/' and path or path and path.strip('/') or '' 208 214 209 215 def normalize_rev(self, rev): 210 216 try: … … 293 299 subpool = Pool(self.pool) 294 300 while rev: 295 301 subpool.clear() 296 rev_root = fs.revision_root(self.fs_ptr, rev, subpool()) 297 node_type = fs.check_path(rev_root, path, subpool()) 298 if node_type in _kindmap: # then path exists at that rev 302 if self.has_node(path, rev): 299 303 if expect_deletion: 300 304 # it was missing, now it's there again: rev+1 must be a delete 301 305 yield path, rev+1, Changeset.DELETE … … 320 324 expect_deletion = True 321 325 rev = self.previous_rev(rev) 322 326 327 def get_deltas(self, old_path, old_rev, new_path, new_rev, 328 ignore_ancestry=0): 329 old_node = new_node = None 330 old_rev = self.normalize_rev(old_rev) 331 new_rev = self.normalize_rev(new_rev) 332 if self.has_node(old_path, old_rev): 333 old_node = self.get_node(old_path, old_rev) 334 else: 335 raise TracError, ('The Base for Diff is invalid: path %s' 336 ' doesn\'t exist in revision %s' \ 337 % (old_path, old_rev)) 338 if self.has_node(new_path, new_rev): 339 new_node = self.get_node(new_path, new_rev) 340 else: 341 raise TracError, ('The Target for Diff is invalid: path %s' 342 ' doesn\'t exist in revision %s' \ 343 % (new_path, new_rev)) 344 if new_node.kind != old_node.kind: 345 raise TracError, ('Diff mismatch: Base is a %s (%s in revision %s) ' 346 'and Target is a %s (%s in revision %s).' \ 347 % (old_node.kind, old_path, old_rev, 348 new_node.kind, new_path, new_rev)) 349 subpool = Pool(self.pool) 350 if new_node.isdir: 351 editor = DiffChangeEditor() 352 e_ptr, e_baton = delta.make_editor(editor, subpool()) 353 old_root = fs.revision_root(self.fs_ptr, old_rev, subpool()) 354 new_root = fs.revision_root(self.fs_ptr, new_rev, subpool()) 355 def authz_cb(root, path, pool): return 1 356 text_deltas = 0 # as this is anyway re-done in Diff.py... 357 entry_props = 0 # "... typically used only for working copy updates" 358 repos.svn_repos_dir_delta(old_root, old_path, '', 359 new_root, new_path, 360 e_ptr, e_baton, authz_cb, 361 text_deltas, 362 1, # directory 363 entry_props, 364 ignore_ancestry, 365 subpool()) 366 for path, kind, change in editor.deltas: 367 old_node = new_node = None 368 if change != Changeset.ADD: 369 old_node = self.get_node(posixpath.join(old_path, path), 370 old_rev) 371 if change != Changeset.DELETE: 372 new_node = self.get_node(posixpath.join(new_path, path), 373 new_rev) 374 else: 375 kind = _kindmap[fs.check_path(old_root, old_node.path, 376 subpool())] 377 yield (old_node, new_node, kind, change) 378 else: 379 old_root = fs.revision_root(self.fs_ptr, old_rev, subpool()) 380 new_root = fs.revision_root(self.fs_ptr, new_rev, subpool()) 381 if fs.contents_changed(old_root, old_path, new_root, new_path, 382 subpool()): 383 yield (old_node, new_node, Node.FILE, Changeset.EDIT) 384 323 385 324 386 class SubversionNode(Node): 325 387 … … 340 402 raise TracError, "No node at %s in revision %s" % (path, rev) 341 403 self.created_rev = fs.node_created_rev(self.root, self.scoped_path, self.pool()) 342 404 self.created_path = fs.node_created_path(self.root, self.scoped_path, self.pool()) 343 # 'created_path' differs from 'path' if the last operation is a copy, 344 # and furthermore, 'path' might not exist at 'create_rev' 405 # Note: 'created_path' differs from 'path' if the last change was a copy, 406 # and furthermore, 'path' might not exist at 'create_rev'. 407 # The only guarantees are: 408 # * this node exists at (path,rev) 409 # * the node existed at (created_path,created_rev) 410 # TODO: check node id 345 411 self.rev = self.created_rev 346 412 347 413 Node.__init__(self, path, self.rev, _kindmap[node_type]) … … 380 446 if newer: 381 447 yield newer 382 448 449 # def get_previous(self): 450 # # FIXME: redo it with fs.node_history 451 383 452 def get_properties(self): 384 453 props = fs.node_proplist(self.root, self.scoped_path, self.pool()) 385 454 for name,value in props.items(): … … 474 543 475 544 def _get_prop(self, name): 476 545 return fs.revision_prop(self.fs_ptr, self.rev, name, self.pool()) 546 547 548 # 549 # Delta editor for diffs between arbitrary nodes 550 # 551 # Note 1: the 'copyfrom_path' and 'copyfrom_rev' information is not used 552 # because 'repos.svn_repos_dir_delta' *doesn't* provide it. 553 # 554 # Note 2: the 'dir_baton' is the path of the parent directory 555 # 556 557 class DiffChangeEditor(delta.Editor): 558 559 def __init__(self): 560 self.deltas = [] 561 562 # -- svn.delta.Editor callbacks 563 564 def open_root(self, base_revision, dir_pool): 565 return ('/', Changeset.EDIT) 566 567 def add_directory(self, path, dir_baton, copyfrom_path, copyfrom_rev, 568 dir_pool): 569 self.deltas.append((path, Node.DIRECTORY, Changeset.ADD)) 570 return (path, Changeset.ADD) 571 572 def open_directory(self, path, dir_baton, base_revision, dir_pool): 573 return (path, dir_baton[1]) 574 575 def change_dir_prop(self, dir_baton, name, value, pool): 576 path, change = dir_baton 577 if change != Changeset.ADD: 578 self.deltas.append((path, Node.DIRECTORY, change)) 579 580 def delete_entry(self, path, revision, dir_baton, pool): 581 self.deltas.append((path, None, Changeset.DELETE)) 582 583 def add_file(self, path, dir_baton, copyfrom_path, copyfrom_revision, 584 dir_pool): 585 self.deltas.append((path, Node.FILE, Changeset.ADD)) 586 587 def open_file(self, path, dir_baton, dummy_rev, file_pool): 588 self.deltas.append((path, Node.FILE, Changeset.EDIT)) 589 -
trac/versioncontrol/tests/svn_fs.py
diff -urN -x .svn trac-0.9b1/trac/versioncontrol/tests/svn_fs.py anydiff-branch/trac/versioncontrol/tests/svn_fs.py
old new 198 198 self.assertEqual(('tags/v1', 7, 'unknown'), history.next()) 199 199 self.assertRaises(StopIteration, history.next) 200 200 201 # Diffs 202 203 def _cmp_diff(self, expected, got): 204 if expected[0]: 205 old = self.repos.get_node(*expected[0]) 206 self.assertEqual((old.path, old.rev), (got[0].path, got[0].rev)) 207 if expected[1]: 208 new = self.repos.get_node(*expected[1]) 209 self.assertEqual((new.path, new.rev), (got[1].path, got[1].rev)) 210 self.assertEqual(expected[2], (got[2], got[3])) 211 212 def test_diff_file_different_revs(self): 213 diffs = self.repos.get_deltas('trunk/README.txt', 2, 'trunk/README.txt', 3) 214 self._cmp_diff((('trunk/README.txt', 2), 215 ('trunk/README.txt', 3), 216 (Node.FILE, Changeset.EDIT)), diffs.next()) 217 self.assertRaises(StopIteration, diffs.next) 218 219 def test_diff_file_different_files(self): 220 diffs = self.repos.get_deltas('branches/v1x/README.txt', 12, 221 'branches/v1x/README2.txt', 12) 222 self._cmp_diff((('branches/v1x/README.txt', 12), 223 ('branches/v1x/README2.txt', 12), 224 (Node.FILE, Changeset.EDIT)), diffs.next()) 225 self.assertRaises(StopIteration, diffs.next) 226 227 def test_diff_file_no_change(self): 228 diffs = self.repos.get_deltas('trunk/README.txt', 7, 229 'tags/v1/README.txt', 7) 230 self.assertRaises(StopIteration, diffs.next) 231 232 def test_diff_dir_different_revs(self): 233 diffs = self.repos.get_deltas('trunk', 4, 'trunk', 8) 234 self._cmp_diff((None, ('trunk/dir1/dir2', 8), 235 (Node.DIRECTORY, Changeset.ADD)), diffs.next()) 236 self._cmp_diff((None, ('trunk/dir1/dir3', 8), 237 (Node.DIRECTORY, Changeset.ADD)), diffs.next()) 238 self._cmp_diff((None, ('trunk/README2.txt', 6), 239 (Node.FILE, Changeset.ADD)), diffs.next()) 240 self._cmp_diff((('trunk/dir2', 4), None, 241 (Node.DIRECTORY, Changeset.DELETE)), diffs.next()) 242 self._cmp_diff((('trunk/dir3', 4), None, 243 (Node.DIRECTORY, Changeset.DELETE)), diffs.next()) 244 self.assertRaises(StopIteration, diffs.next) 245 246 def test_diff_dir_different_dirs(self): 247 diffs = self.repos.get_deltas('trunk', 1, 'branches/v1x', 12) 248 self._cmp_diff((None, ('branches/v1x/dir1', 12), 249 (Node.DIRECTORY, Changeset.ADD)), diffs.next()) 250 self._cmp_diff((None, ('branches/v1x/dir1/dir2', 12), 251 (Node.DIRECTORY, Changeset.ADD)), diffs.next()) 252 self._cmp_diff((None, ('branches/v1x/dir1/dir3', 12), 253 (Node.DIRECTORY, Changeset.ADD)), diffs.next()) 254 self._cmp_diff((None, ('branches/v1x/README.txt', 12), 255 (Node.FILE, Changeset.ADD)), diffs.next()) 256 self._cmp_diff((None, ('branches/v1x/README2.txt', 12), 257 (Node.FILE, Changeset.ADD)), diffs.next()) 258 self.assertRaises(StopIteration, diffs.next) 259 260 def test_diff_dir_no_change(self): 261 diffs = self.repos.get_deltas('trunk', 7, 262 'tags/v1', 7) 263 self.assertRaises(StopIteration, diffs.next) 264 265 # Changesets 266 201 267 def test_changeset_repos_creation(self): 202 268 chgset = self.repos.get_changeset(0) 203 269 self.assertEqual(0, chgset.rev) -
trac/versioncontrol/web_ui/browser.py
diff -urN -x .svn trac-0.9b1/trac/versioncontrol/web_ui/browser.py anydiff-branch/trac/versioncontrol/web_ui/browser.py
old new 89 89 90 90 repos = self.env.get_repository(req.authname) 91 91 node = repos.get_node(path, rev) 92 rev = repos.normalize_rev(rev) 92 93 93 94 hidden_properties = [p.strip() for p 94 95 in self.config.get('browser', 'hide_properties', 95 96 'svk:merge').split(',')] 97 96 98 req.hdf['title'] = path 97 req.hdf['browser']= {99 browser_hdf = { 98 100 'path': path, 99 'revision': rev or repos.youngest_rev,101 'revision': rev, 100 102 'props': dict([(util.escape(name), util.escape(value)) 101 103 for name, value in node.get_properties().items() 102 if not name in hidden_properties]), 103 'href': util.escape(self.env.href.browser(path, rev=rev or 104 repos.youngest_rev)), 105 'log_href': util.escape(self.env.href.log(path)) 106 } 104 if not name in hidden_properties]) 105 } 106 browser_hrefs = { 107 'href': self.env.href.browser(path,rev=rev), 108 'restricted_changeset_href': self.env.href.changeset(node.rev, 109 path=path), 110 'anydiff_href': self.env.href.anydiff(), 111 'log_href': self.env.href.log(path) 112 } 113 browser_hdf.update(dict([(key, util.escape(href)) for key, href in 114 browser_hrefs.items()])) 115 req.hdf['browser'] = browser_hdf 107 116 108 117 path_links = get_path_links(self.env.href, path, rev) 109 118 if len(path_links) > 1: … … 163 172 164 173 req.hdf['browser.items'] = info 165 174 req.hdf['browser.changes'] = changes 166 175 if node.path != '': 176 zip_href = self.env.href.diff(node.path, new=rev, old=rev, 177 old_path='/', # special case (#238) 178 format='zip') 179 add_link(req, 'alternate', zip_href, 'Zip Archive', 180 'application/zip', 'zip') 181 182 167 183 def _render_file(self, req, repos, node, rev=None): 168 184 req.perm.assert_permission('FILE_VIEW') 169 185 -
trac/versioncontrol/web_ui/changeset.py
diff -urN -x .svn trac-0.9b1/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 50 match = re.match(r'/changeset/([0-9]+)$', req.path_info) … … 59 52 req.args['rev'] = match.group(1) 60 53 return 1 61 54 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 68 diff_options = get_diff_options(req)69 if req.args.has_key('update'):70 req.redirect(self.env.href.changeset(rev))71 72 chgset = repos.get_changeset(rev)73 req.check_modified(chgset.date,74 diff_options[0] + ''.join(diff_options[1]))75 76 format = req.args.get('format')77 if format == 'diff':78 self._render_diff(req, repos, chgset, diff_options)79 return80 elif format == 'zip':81 self._render_zip(req, repos, chgset)82 return83 84 self._render_html(req, repos, chgset, diff_options)85 add_link(req, 'alternate', '?format=diff', 'Unified Diff',86 'text/plain', 'diff')87 add_link(req, 'alternate', '?format=zip', 'Zip Archive',88 'application/zip', 'zip')89 add_stylesheet(req, 'common/css/changeset.css')90 add_stylesheet(req, 'common/css/diff.css')91 add_stylesheet(req, 'common/css/code.css')92 return 'changeset.cs', None93 94 55 # ITimelineEventProvider methods 95 56 96 57 def get_timeline_filters(self, req): … … 136 97 message 137 98 rev = repos.previous_rev(rev) 138 99 139 # Internal methods140 141 def _render_html(self, req, repos, chgset, diff_options):142 """HTML version"""143 req.hdf['title'] = '[%s]' % chgset.rev144 req.hdf['changeset'] = {145 'revision': chgset.rev,146 'time': time.strftime('%c', time.localtime(chgset.date)),147 'author': util.escape(chgset.author or 'anonymous'),148 'message': wiki_to_html(chgset.message or '--', self.env, req,149 escape_newlines=True)150 }151 152 oldest_rev = repos.oldest_rev153 if chgset.rev != oldest_rev:154 add_link(req, 'first', self.env.href.changeset(oldest_rev),155 'Changeset %s' % oldest_rev)156 previous_rev = repos.previous_rev(chgset.rev)157 add_link(req, 'prev', self.env.href.changeset(previous_rev),158 'Changeset %s' % previous_rev)159 youngest_rev = repos.youngest_rev160 if str(chgset.rev) != str(youngest_rev):161 next_rev = repos.next_rev(chgset.rev)162 add_link(req, 'next', self.env.href.changeset(next_rev),163 'Changeset %s' % next_rev)164 add_link(req, 'last', self.env.href.changeset(youngest_rev),165 'Changeset %s' % youngest_rev)166 167 edits = []168 idx = 0169 for path, kind, change, base_path, base_rev in chgset.get_changes():170 info = {'change': change}171 if base_path:172 info['path.old'] = base_path173 info['rev.old'] = base_rev174 info['browser_href.old'] = self.env.href.browser(base_path,175 rev=base_rev)176 if path:177 info['path.new'] = path178 info['rev.new'] = chgset.rev179 info['browser_href.new'] = self.env.href.browser(path,180 rev=chgset.rev)181 if change in (Changeset.COPY, Changeset.EDIT, Changeset.MOVE):182 edits.append((idx, path, kind, base_path, base_rev))183 req.hdf['changeset.changes.%d' % idx] = info184 idx += 1185 186 hidden_properties = [p.strip() for p187 in self.config.get('browser', 'hide_properties',188 'svk:merge').split(',')]189 190 for idx, path, kind, base_path, base_rev in edits:191 old_node = repos.get_node(base_path or path, base_rev)192 new_node = repos.get_node(path, chgset.rev)193 194 # Property changes195 old_props = old_node.get_properties()196 new_props = new_node.get_properties()197 changed_props = {}198 if old_props != new_props:199 for k,v in old_props.items():200 if not k in new_props:201 changed_props[k] = {'old': v}202 elif v != new_props[k]:203 changed_props[k] = {'old': v, 'new': new_props[k]}204 for k,v in new_props.items():205 if not k in old_props:206 changed_props[k] = {'new': v}207 for k in hidden_properties:208 if k in changed_props:209 del changed_props[k]210 req.hdf['changeset.changes.%d.props' % idx] = changed_props211 212 if kind == Node.DIRECTORY:213 continue214 215 # Content changes216 default_charset = self.config.get('trac', 'default_charset')217 old_content = old_node.get_content().read()218 if mimeview.is_binary(old_content):219 continue220 charset = mimeview.get_charset(old_node.content_type) or \221 default_charset222 old_content = util.to_utf8(old_content, charset)223 224 new_content = new_node.get_content().read()225 if mimeview.is_binary(new_content):226 continue227 charset = mimeview.get_charset(new_node.content_type) or \228 default_charset229 new_content = util.to_utf8(new_content, charset)230 231 if old_content != new_content:232 context = 3233 for option in diff_options[1]:234 if option.startswith('-U'):235 context = int(option[2:])236 break237 tabwidth = int(self.config.get('diff', 'tab_width',238 self.config.get('mimeviewer',239 'tab_width')))240 changes = hdf_diff(old_content.splitlines(),241 new_content.splitlines(),242 context, tabwidth,243 ignore_blank_lines='-B' in diff_options[1],244 ignore_case='-i' in diff_options[1],245 ignore_space_changes='-b' in diff_options[1])246 req.hdf['changeset.changes.%d.diff' % idx] = changes247 248 def _render_diff(self, req, repos, chgset, diff_options):249 """Raw Unified Diff version"""250 req.send_response(200)251 req.send_header('Content-Type', 'text/plain;charset=utf-8')252 req.send_header('Content-Disposition',253 'filename=Changeset%s.diff' % req.args.get('rev'))254 req.end_headers()255 256 for path, kind, change, base_path, base_rev in chgset.get_changes():257 if change == Changeset.ADD:258 old_node = None259 else:260 old_node = repos.get_node(base_path or path, base_rev)261 if change == Changeset.DELETE:262 new_node = None263 else:264 new_node = repos.get_node(path, chgset.rev)265 266 # TODO: Property changes267 268 # Content changes269 if kind == 'dir':270 continue271 272 default_charset = self.config.get('trac', 'default_charset')273 new_content = old_content = ''274 new_node_info = old_node_info = ('','')275 276 if old_node:277 charset = mimeview.get_charset(old_node.content_type) or \278 default_charset279 old_content = util.to_utf8(old_node.get_content().read(),280 charset)281 old_node_info = (old_node.path, old_node.rev)282 if mimeview.is_binary(old_content):283 continue284 285 if new_node:286 charset = mimeview.get_charset(new_node.content_type) or \287 default_charset288 new_content = util.to_utf8(new_node.get_content().read(),289 charset)290 new_node_info = (new_node.path, new_node.rev)291 if mimeview.is_binary(new_content):292 continue293 294 if old_content != new_content:295 context = 3296 for option in diff_options[1]:297 if option.startswith('-U'):298 context = int(option[2:])299 break300 req.write('Index: ' + path + util.CRLF)301 req.write('=' * 67 + util.CRLF)302 req.write('--- %s (revision %s)' % old_node_info +303 util.CRLF)304 req.write('+++ %s (revision %s)' % new_node_info +305 util.CRLF)306 for line in unified_diff(old_content.splitlines(),307 new_content.splitlines(), context,308 ignore_blank_lines='-B' in diff_options[1],309 ignore_case='-i' in diff_options[1],310 ignore_space_changes='-b' in diff_options[1]):311 req.write(line + util.CRLF)312 313 def _render_zip(self, req, repos, chgset):314 """ZIP archive with all the added and/or modified files."""315 req.send_response(200)316 req.send_header('Content-Type', 'application/zip')317 req.send_header('Content-Disposition',318 'filename=Changeset%s.zip' % chgset.rev)319 req.end_headers()320 321 try:322 from cStringIO import StringIO323 except ImportError:324 from StringIO import StringIO325 from zipfile import ZipFile, ZipInfo, ZIP_DEFLATED326 327 buf = StringIO()328 zipfile = ZipFile(buf, 'w', ZIP_DEFLATED)329 for path, kind, change, base_path, base_rev in chgset.get_changes():330 if kind == Node.FILE and change != Changeset.DELETE:331 node = repos.get_node(path, chgset.rev)332 zipinfo = ZipInfo()333 zipinfo.filename = node.path334 zipinfo.date_time = time.gmtime(node.last_modified)[:6]335 zipinfo.compress_type = ZIP_DEFLATED336 zipfile.writestr(zipinfo, node.get_content().read())337 zipfile.close()338 req.write(buf.getvalue())339 100 340 101 # IWikiSyntaxProvider methods 341 102 -
trac/versioncontrol/web_ui/diff.py
diff -urN -x .svn trac-0.9b1/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.web import IRequestHandler 32 from trac.web.chrome import add_link, add_stylesheet 33 from trac.wiki import wiki_to_html, IWikiSyntaxProvider 34 35 class DiffArgs(dict): 36 def __getattr__(self,str): 37 return self[str] 38 39 40 class ChangesPermission(Component): 41 """Simple permission provider for changes related modules.""" 42 43 implements(IPermissionRequestor) 44 45 def get_permission_actions(self): 46 return ['CHANGESET_VIEW'] 47 48 49 class AbstractDiffModule(Component): 50 """Provide flexible functionality for showing sets of differences. 51 52 If the differences shown are coming from a specific changeset, 53 then that changeset informations can be shown too. 54 55 In addition, it is possible to show only a subset of the changeset: 56 Only the changes affecting a given path will be shown. 57 This is called the ''restricted'' changeset. 58 59 But the differences can also be computed in a more general way, 60 between two arbitrary paths and/or between two arbitrary revisions. 61 In that case, there's no changeset information displayed. 62 """ 63 64 abstract = True 65 66 implements(IRequestHandler) 67 68 # IRequestHandler methods 69 70 def match_request(self, req): 71 raise NotImplementedError 72 73 def process_request(self, req): 74 """The appropriate mode of operation is inferred from 75 the request parameters: 76 * If `old` and `new` parameters are given, it will be an 77 arbitrary set of differences: `chgset` is False. 78 * If `old_path` is given and is different from `path`, 79 it's a generalized diff, from `old_path@old` 80 to `path@new`: `restricted` is False. 81 * Otherwise those are differences between two arbitrary revisions 82 of a given path: `restricted` is True. 83 * Otherwise, we are dealing with a changeset only: `chgset` is True. 84 * If the `path` is not empty or not the root, then only 85 the changes affecting that path (i.e. itself, children or 86 ancestors) will be considered: `restricted` is True. 87 * Otherwise, it's the full changeset: `restricted` is False. 88 89 In any case, the given path@rev pair must exist. 90 """ 91 req.perm.assert_permission('CHANGESET_VIEW') 92 93 # -- retrieve arguments 94 path = req.args.get('path') 95 rev = req.args.get('rev') 96 old = req.args.get('old') 97 new = req.args.get('new') 98 old_path = req.args.get('old_path') 99 100 # -- normalize and check for special case 101 repos = self.env.get_repository(req.authname) 102 path = repos.normalize_path(path) 103 rev = repos.normalize_rev(rev) 104 old_path = repos.normalize_path(old_path) 105 106 if old_path == path and old and old == new: # revert to Changeset 107 rev = old 108 old_path = old = new = None 109 110 diff_options = get_diff_options(req) 111 112 # -- setup the `chgset` and `restricted` flags, see docstring above. 113 chgset = not old and not new and not old_path 114 if chgset: 115 restricted = path != '' and path != '/' # (subset or not) 116 else: 117 restricted = old_path == path # (same path or not) 118 119 # -- redirect if changing the diff options 120 if req.args.has_key('update'): 121 if chgset: 122 if restricted: 123 req.redirect(self.env.href.diff(path, rev=rev)) 124 else: 125 req.redirect(self.env.href.changeset(rev)) 126 else: 127 req.redirect(self.env.href.diff(path, new=new, 128 old_path=old_path, old=old)) 129 130 # -- preparing the diff arguments 131 if chgset: 132 prev = repos.get_node(path, rev).get_previous() 133 if prev: 134 prev_path, prev_rev = prev[:2] 135 else: 136 prev_path, prev_rev = path, repos.previous_rev(rev) 137 diff_args = DiffArgs(old_path=prev_path, old_rev=prev_rev, 138 new_path=path, new_rev=rev) 139 else: 140 if not new: 141 new = repos.youngest_rev 142 elif not old: 143 old = repos.youngest_rev 144 if not old_path: 145 old_path = path 146 diff_args = DiffArgs(old_path=old_path, old_rev=old, 147 new_path=path, new_rev=new) 148 if chgset: 149 chgset = repos.get_changeset(rev) 150 req.check_modified(chgset.date, 151 diff_options[0] + ''.join(diff_options[1])) 152 else: 153 pass # FIXME: what date should we choose for a diff? 154 155 req.hdf['diff'] = diff_args 156 157 format = req.args.get('format') 158 159 if format in ['diff', 'zip']: 160 # choosing an appropriate filename 161 rpath = path.replace('/','_') 162 if chgset: 163 if restricted: 164 filename = 'changeset_%s_r%s' % (rpath, rev) 165 else: 166 filename = 'changeset_r%s' % rev 167 else: 168 if restricted: 169 filename = 'diff-%s-from-r%s-to-r%s' \ 170 % (rpath, old, new) 171 elif old_path == '/': # special case for download (#238) 172 filename = '%s-r%s' % (rpath, old) 173 else: 174 filename = 'diff-from-%s-r%s-to-%s-r%s' \ 175 % (old_path.replace('/','_'), old, rpath, new) 176 if format == 'diff': 177 self._render_diff(req, filename, repos, diff_args, 178 diff_options) 179 return 180 elif format == 'zip': 181 self._render_zip(req, filename, repos, diff_args) 182 return 183 184 # -- HTML format 185 self._render_html(req, repos, chgset, restricted, 186 diff_args, diff_options) 187 if chgset: 188 diff_params = 'rev=%s' % rev 189 else: 190 diff_params = urlencode({'path': path, 191 'new': new, 192 'old_path': old_path, 193 'old': old}) 194 add_link(req, 'alternate', '?format=diff&'+diff_params, 'Unified Diff', 195 'text/plain', 'diff') 196 add_link(req, 'alternate', '?format=zip&'+diff_params, 'Zip Archive', 197 'application/zip', 'zip') 198 add_stylesheet(req, 'common/css/changeset.css') 199 add_stylesheet(req, 'common/css/diff.css') 200 add_stylesheet(req, 'common/css/code.css') 201 return 'diff.cs', None 202 203 204 # Internal methods 205 206 def _render_html(self, req, repos, chgset, restricted, diff, diff_options): 207 """ 208 HTML version 209 """ 210 req.hdf['diff'] = { 211 'chgset': chgset and True, 212 'restricted': restricted, 213 'href': { 'new_rev': self.env.href.changeset(diff.new_rev), 214 'old_rev': self.env.href.changeset(diff.old_rev), 215 'new_path': self.env.href.browser(diff.new_path, 216 rev=diff.new_rev), 217 'old_path': self.env.href.browser(diff.old_path, 218 rev=diff.old_rev) 219 } 220 } 221 222 if chgset: # Changeset Mode (possibly restricted on a path) 223 path, rev = diff.new_path, diff.new_rev 224 225 # -- getting the deltas from the Changeset.get_changes method 226 def get_deltas(): 227 old_node = new_node = None 228 for npath, kind, change, opath, orev in chgset.get_changes(): 229 if restricted and \ 230 not (npath.startswith(path) # npath is below 231 or path.startswith(npath)): # npath is above 232 continue 233 if change != Changeset.ADD: 234 old_node = repos.get_node(opath, orev) 235 if change != Changeset.DELETE: 236 new_node = repos.get_node(npath, rev) 237 yield old_node, new_node, kind, change 238 239 def _changeset_title(rev): 240 if restricted: 241 return 'Changeset %s for %s' % (rev, path) 242 else: 243 return 'Changeset %s' % rev 244 245 title = _changeset_title(rev) 246 req.hdf['changeset'] = { 247 'revision': chgset.rev, 248 'time': time.strftime('%c', time.localtime(chgset.date)), 249 'author': util.escape(chgset.author or 'anonymous'), 250 'message': wiki_to_html(chgset.message or '--', self.env, req, 251 escape_newlines=True) 252 } 253 oldest_rev = repos.oldest_rev 254 if chgset.rev != oldest_rev: 255 if restricted: 256 prev = repos.get_node(path, rev).get_previous() 257 if prev: 258 prev_path, prev_rev = prev[:2] 259 prev_href = self.env.href.changeset(prev_rev, 260 path=prev_path) 261 else: 262 prev_path = prev_rev = None 263 else: 264 prev_path = diff.old_path 265 prev_rev = repos.previous_rev(chgset.rev) 266 add_link(req, 'first', self.env.href.changeset(oldest_rev), 267 'Changeset %s' % oldest_rev) 268 prev_href = self.env.href.changeset(prev_rev) 269 if prev_rev: 270 add_link(req, 'prev', prev_href, _changeset_title(prev_rev)) 271 youngest_rev = repos.youngest_rev 272 if str(chgset.rev) != str(youngest_rev): 273 if restricted: 274 next_rev = next_href = None 275 # FIXME: find an effective way to find the next rev 276 else: 277 next_rev = repos.next_rev(chgset.rev) 278 next_href = self.env.href.changeset(next_rev) 279 add_link(req, 'last', 280 self.env.href.diff(path, rev=youngest_rev), 281 'Changeset %s' % youngest_rev) 282 if next_rev: 283 add_link(req, 'next', next_href, _changeset_title(next_rev)) 284 285 else: # Diff Mode 286 # -- getting the deltas from the Repository.get_deltas method 287 def get_deltas(): 288 for d in repos.get_deltas(**diff): 289 yield d 290 291 reverse_href = self.env.href.diff(diff.old_path, 292 new=diff.old_rev, 293 old_path=diff.new_path, 294 old=diff.new_rev) 295 req.hdf['diff.reverse_href'] = reverse_href 296 if restricted: # 'diff between 2 revisions' mode 297 title = 'Diff r%s:%s for %s' % (diff.old_rev, diff.new_rev, 298 diff.new_path) 299 else: # 'arbitrary diff' mode 300 title = 'Diff from %s @ %s to %s @ %s' % (diff.old_path, 301 diff.old_rev, 302 diff.new_path, 303 diff.new_rev) 304 req.hdf['title'] = title 305 306 def _change_info(old_node, new_node, change): 307 info = {'change': change} 308 if old_node: 309 info['path.old'] = old_node.path 310 info['rev.old'] = old_node.rev # this is the created rev. 311 old_href = self.env.href.browser(old_node.path, 312 rev=diff.old_rev) 313 # Reminder: old_node.path may not exist at old_node.rev 314 info['browser_href.old'] = old_href 315 if new_node: 316 info['path.new'] = new_node.path 317 info['rev.new'] = new_node.rev # created rev. 318 new_href = self.env.href.browser(new_node.path, 319 rev=diff.new_rev) 320 # (same remark as above) 321 info['browser_href.new'] = new_href 322 return info 323 324 hidden_properties = [p.strip() for p 325 in self.config.get('browser', 'hide_properties', 326 'svk:merge').split(',')] 327 328 def _prop_changes(old_node, new_node): 329 old_props = old_node.get_properties() 330 new_props = new_node.get_properties() 331 changed_props = {} 332 if old_props != new_props: 333 for k,v in old_props.items(): 334 if not k in new_props: 335 changed_props[k] = {'old': v} 336 elif v != new_props[k]: 337 changed_props[k] = {'old': v, 'new': new_props[k]} 338 for k,v in new_props.items(): 339 if not k in old_props: 340 changed_props[k] = {'new': v} 341 for k in hidden_properties: 342 if k in changed_props: 343 del changed_props[k] 344 return changed_props 345 346 def _content_changes(old_node, new_node): 347 """ 348 Returns the list of differences. 349 The list is empty when no differences between comparable files 350 are detected, but the return value is None for non-comparable files. 351 """ 352 default_charset = self.config.get('trac', 'default_charset') 353 old_content = old_node.get_content().read() 354 if mimeview.is_binary(old_content): 355 return None 356 charset = mimeview.get_charset(old_node.content_type) or \ 357 default_charset 358 old_content = util.to_utf8(old_content, charset) 359 360 new_content = new_node.get_content().read() 361 if mimeview.is_binary(new_content): 362 return None 363 charset = mimeview.get_charset(new_node.content_type) or \ 364 default_charset 365 new_content = util.to_utf8(new_content, charset) 366 367 if old_content != new_content: 368 context = 3 369 options = diff_options[1] 370 for option in options: 371 if option.startswith('-U'): 372 context = int(option[2:]) 373 break 374 tabwidth = int(self.config.get('diff', 'tab_width', 375 self.config.get('mimeviewer', 376 'tab_width'))) 377 return hdf_diff(old_content.splitlines(), 378 new_content.splitlines(), 379 context, tabwidth, 380 ignore_blank_lines='-B' in options, 381 ignore_case='-i' in options, 382 ignore_space_changes='-b' in options) 383 else: 384 return [] 385 386 idx = 0 387 for old_node, new_node, kind, change in get_deltas(): 388 if change != Changeset.EDIT: 389 show_entry = True 390 else: 391 show_entry = False 392 assert old_node and new_node 393 props = _prop_changes(old_node, new_node) 394 if props: 395 req.hdf['diff.changes.%d.props' % idx] = props 396 show_entry = True 397 if kind == Node.FILE: 398 diffs = _content_changes(old_node, new_node) 399 if diffs != []: 400 if diffs: 401 req.hdf['diff.changes.%d.diff' % idx] = diffs 402 # elif None (means: manually compare to (previous)) 403 show_entry = True 404 if show_entry: 405 info = _change_info(old_node, new_node, change) 406 req.hdf['diff.changes.%d' % idx] = info 407 idx += 1 # the sequence should be immutable 408 409 def _render_diff(self, req, filename, repos, diff, diff_options): 410 """Raw Unified Diff version""" 411 req.send_response(200) 412 req.send_header('Content-Type', 'text/plain;charset=utf-8') 413 req.send_header('Content-Disposition', 414 'filename=%s.diff' % filename) 415 req.end_headers() 416 417 for old_node, new_node, kind, change in repos.get_deltas(**diff): 418 # TODO: Property changes 419 420 # Content changes 421 if kind == Node.DIRECTORY: 422 continue 423 424 default_charset = self.config.get('trac', 'default_charset') 425 new_content = old_content = '' 426 new_node_info = old_node_info = ('','') 427 428 if old_node: 429 charset = mimeview.get_charset(old_node.content_type) or \ 430 default_charset 431 old_content = util.to_utf8(old_node.get_content().read(), 432 charset) 433 old_node_info = (old_node.path, old_node.rev) 434 if mimeview.is_binary(old_content): 435 continue 436 437 if new_node: 438 charset = mimeview.get_charset(new_node.content_type) or \ 439 default_charset 440 new_content = util.to_utf8(new_node.get_content().read(), 441 charset) 442 new_node_info = (new_node.path, new_node.rev) 443 if mimeview.is_binary(new_content): 444 continue 445 new_path = new_node.path 446 else: 447 old_node_path = repos.normalize_path(old_node.path) 448 diff_old_path = repos.normalize_path(diff.old_path) 449 new_path = posixpath.join(diff.new_path, 450 old_node_path[len(diff_old_path)+1:]) 451 452 if old_content != new_content: 453 context = 3 454 options = diff_options[1] 455 for option in options: 456 if option.startswith('-U'): 457 context = int(option[2:]) 458 break 459 if not old_node_info[0]: 460 old_node_info = new_node_info # support for 'A'dd changes 461 req.write('Index: ' + new_path + util.CRLF) 462 req.write('=' * 67 + util.CRLF) 463 req.write('--- %s (revision %s)' % old_node_info + 464 util.CRLF) 465 req.write('+++ %s (revision %s)' % new_node_info + 466 util.CRLF) 467 for line in unified_diff(old_content.splitlines(), 468 new_content.splitlines(), context, 469 ignore_blank_lines='-B' in options, 470 ignore_case='-i' in options, 471 ignore_space_changes='-b' in options): 472 req.write(line + util.CRLF) 473 474 def _render_zip(self, req, filename, repos, diff): 475 """ZIP archive with all the added and/or modified files.""" 476 new_rev = diff.new_rev 477 req.send_response(200) 478 req.send_header('Content-Type', 'application/zip') 479 req.send_header('Content-Disposition', 480 'filename=%s.zip' % filename) 481 req.end_headers() 482 483 try: 484 from cStringIO import StringIO 485 except ImportError: 486 from StringIO import StringIO 487 from zipfile import ZipFile, ZipInfo, ZIP_DEFLATED 488 489 buf = StringIO() 490 zipfile = ZipFile(buf, 'w', ZIP_DEFLATED) 491 for old_node, new_node, kind, change in repos.get_deltas(**diff): 492 if kind == Node.FILE and change != Changeset.DELETE: 493 assert new_node 494 zipinfo = ZipInfo() 495 zipinfo.filename = new_node.path 496 zipinfo.date_time = time.gmtime(new_node.last_modified)[:6] 497 zipinfo.compress_type = ZIP_DEFLATED 498 zipfile.writestr(zipinfo, new_node.get_content().read()) 499 zipfile.close() 500 req.write(buf.getvalue()) 501 502 503 class DiffModule(AbstractDiffModule): 504 505 implements(IWikiSyntaxProvider) 506 507 # (reimplemented) IRequestHandler methods 508 509 def match_request(self, req): 510 match = re.match(r'/diff(?:(/.*)|$)', req.path_info) 511 if match: 512 if match.group(1): 513 req.args['path'] = match.group(1) 514 return 1 515 516 # IWikiSyntaxProvider methods 517 518 def get_wiki_syntax(self): 519 return [] 520 521 def get_link_resolvers(self): 522 yield ('diff', self._format_link) 523 524 def _format_link(self, formatter, ns, params, label): 525 def pathrev(path): 526 irev = path.find('#') 527 if irev > 0: 528 return (path[:irev], path[irev+1:]) 529 else: 530 return (path, None) 531 ianydiff = params.find('//') 532 if ianydiff > 0: 533 old_path, old_rev = pathrev(params[:ianydiff]) 534 new_path, new_rev = pathrev(params[ianydiff+2:]) 535 else: 536 old_path, old_rev = pathrev(params) 537 new_path = old_path 538 new_rev = None 539 if old_rev: 540 isep = old_rev.find(':') 541 if isep > 0: 542 old_rev = old_rev[:isep] 543 new_rev = old_rev[isep+1:] 544 href = formatter.href.diff(new_path, new=new_rev, 545 old_path=old_path, old=old_rev) 546 return '<a class="changeset" title="%s" href="%s">%s</a>' \ 547 % ('Diff', href, label) 548 549 550 class AnyDiffModule(Component): 551 552 implements(IRequestHandler) 553 554 # IRequestHandler methods 555 556 def match_request(self, req): 557 return re.match(r'/anydiff$', req.path_info) 558 559 def process_request(self, req): 560 # -- retrieve arguments 561 new_path = req.args.get('new_path') 562 new_rev = req.args.get('new_rev') 563 old_path = req.args.get('old_path') 564 old_rev = req.args.get('old_rev') 565 566 # -- normalize 567 repos = self.env.get_repository(req.authname) 568 new_path = repos.normalize_path(new_path) 569 new_rev = repos.normalize_rev(new_rev) 570 old_path = repos.normalize_path(old_path) 571 old_rev = repos.normalize_rev(old_rev) 572 573 # -- prepare rendering 574 req.hdf['anydiff'] = { 575 'new_path': new_path, 576 'new_rev': new_rev, 577 'old_path': old_path, 578 'old_rev': old_rev, 579 'diff_href': self.env.href.diff(), 580 } 581 582 return 'anydiff.cs', None -
trac/versioncontrol/web_ui/__init__.py
diff -urN -x .svn trac-0.9b1/trac/versioncontrol/web_ui/__init__.py anydiff-branch/trac/versioncontrol/web_ui/__init__.py
old new 1 from trac.versioncontrol.web_ui.browser import * 2 from trac.versioncontrol.web_ui.changeset import * 3 from trac.versioncontrol.web_ui.log import * 1 from trac.versioncontrol.web_ui.browser import * 2 from trac.versioncontrol.web_ui.changeset import * 3 from trac.versioncontrol.web_ui.log import * 4 from trac.versioncontrol.web_ui.diff import * 5 -
trac/versioncontrol/web_ui/log.py
diff -urN -x .svn trac-0.9b1/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()` -
wiki-default/WikiStart
diff -urN -x .svn trac-0.9b1/wiki-default/WikiStart anydiff-branch/wiki-default/WikiStart
old new 1 = Welcome to Trac 0.9b1 =1 = Welcome to Trac 0.9b1-anydiff = 2 2 3 3 Trac is a '''minimalistic''' approach to '''web-based''' management of 4 4 '''software projects'''. Its goal is to simplify effective tracking and handling of software issues, enhancements and overall progress.
