| 1 | # svntrac
|
|---|
| 2 | #
|
|---|
| 3 | # Copyright (C) 2003 Jonas Borgström <jonas@xyche.com>
|
|---|
| 4 | #
|
|---|
| 5 | # svntrac is free software; you can redistribute it and/or
|
|---|
| 6 | # modify it under the terms of the GNU General Public License as
|
|---|
| 7 | # published by the Free Software Foundation; either version 2 of the
|
|---|
| 8 | # License, or (at your option) any later version.
|
|---|
| 9 | #
|
|---|
| 10 | # svntrac is distributed in the hope that it will be useful,
|
|---|
| 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|---|
| 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|---|
| 13 | # General Public License for more details.
|
|---|
| 14 | #
|
|---|
| 15 | # You should have received a copy of the GNU General Public License
|
|---|
| 16 | # along with this program; if not, write to the Free Software
|
|---|
| 17 | # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|---|
| 18 | #
|
|---|
| 19 | # Author: Jonas Borgström <jonas@xyche.com>
|
|---|
| 20 |
|
|---|
| 21 | from util import *
|
|---|
| 22 | from Module import Module
|
|---|
| 23 | import db
|
|---|
| 24 | import perm
|
|---|
| 25 | from xml.sax.saxutils import escape
|
|---|
| 26 |
|
|---|
| 27 | import re
|
|---|
| 28 | import string
|
|---|
| 29 | import StringIO
|
|---|
| 30 | from svn import fs, util, delta, repos
|
|---|
| 31 |
|
|---|
| 32 | line_re = re.compile('@@ [+-]([0-9]+),([0-9]+) [+-]([0-9]+),([0-9]+) @@')
|
|---|
| 33 | space_re = re.compile(' ')
|
|---|
| 34 |
|
|---|
| 35 | class DiffColorizer:
|
|---|
| 36 | def __init__(self):
|
|---|
| 37 | self.count = 0
|
|---|
| 38 | self.block = []
|
|---|
| 39 | self.type = None
|
|---|
| 40 | self.p_block = []
|
|---|
| 41 | self.p_type = None
|
|---|
| 42 |
|
|---|
| 43 | print '<table class="diff-table" cellspacing="0">'
|
|---|
| 44 |
|
|---|
| 45 | def writeadd (self, text):
|
|---|
| 46 | print ('<tr><td class="diff-add-left"></td>'
|
|---|
| 47 | '<td class="diff-add-right">'
|
|---|
| 48 | '%s</td></tr>' % text)
|
|---|
| 49 |
|
|---|
| 50 | def writeremove (self, text):
|
|---|
| 51 | print ('<tr><td class="diff-remove-left">%s</td>'
|
|---|
| 52 | '<td class="diff-remove-right"></td></tr>' % text)
|
|---|
| 53 |
|
|---|
| 54 | def writeunchanged (self, text):
|
|---|
| 55 | print ('<tr><td class="diff-unchanged">%s</td>'
|
|---|
| 56 | '<td class="diff-unchanged">%s</td></tr>' %
|
|---|
| 57 | (text, text))
|
|---|
| 58 |
|
|---|
| 59 | def writechanged (self, old, new):
|
|---|
| 60 | print ('<tr><td class="diff-changed">%s</td>'
|
|---|
| 61 | '<td class="diff-changed">%s</td></tr>' %
|
|---|
| 62 | (old, new))
|
|---|
| 63 |
|
|---|
| 64 | def print_block (self):
|
|---|
| 65 | if self.p_type == '-' and self.type == '+':
|
|---|
| 66 | self.writechanged(string.join(self.p_block, '<br>'),
|
|---|
| 67 | string.join(self.block, '<br>'))
|
|---|
| 68 | elif self.type == '+':
|
|---|
| 69 | self.writeadd(string.join(self.block, '<br>'))
|
|---|
| 70 | elif self.type == '-':
|
|---|
| 71 | self.writeremove(string.join(self.block, '<br>'))
|
|---|
| 72 | elif self.type == ' ':
|
|---|
| 73 | self.writeunchanged(string.join(self.block, '<br>'))
|
|---|
| 74 | self.block = self.p_block = []
|
|---|
| 75 |
|
|---|
| 76 | def writeline(self, text):
|
|---|
| 77 | self.count = self.count + 1
|
|---|
| 78 | if self.count < 3:
|
|---|
| 79 | return
|
|---|
| 80 | match = line_re.search(text)
|
|---|
| 81 | if match:
|
|---|
| 82 | self.print_block()
|
|---|
| 83 | print ('<tr><td class="diff-line">line %s</td>'
|
|---|
| 84 | '<td class="diff-line">line %s</td></tr>' %
|
|---|
| 85 | (match.group(1), match.group(3)))
|
|---|
| 86 | return
|
|---|
| 87 | type = text[0]
|
|---|
| 88 | text = text[1:]
|
|---|
| 89 | text = space_re.sub (' ', text.expandtabs(8))
|
|---|
| 90 | if type == self.type:
|
|---|
| 91 | self.block.append(text)
|
|---|
| 92 | else:
|
|---|
| 93 | if type == '+' and self.type == '-':
|
|---|
| 94 | self.p_block = self.block
|
|---|
| 95 | self.p_type = self.type
|
|---|
| 96 | else:
|
|---|
| 97 | self.print_block()
|
|---|
| 98 | self.block = [text]
|
|---|
| 99 | self.type = type
|
|---|
| 100 |
|
|---|
| 101 |
|
|---|
| 102 | def close(self):
|
|---|
| 103 | self.print_block()
|
|---|
| 104 | print '</table>'
|
|---|
| 105 |
|
|---|
| 106 |
|
|---|
| 107 | class DiffEditor (delta.Editor):
|
|---|
| 108 | """
|
|---|
| 109 | generates a unified diff of the changes for a given changeset.
|
|---|
| 110 | the output is written to stdout.
|
|---|
| 111 | """
|
|---|
| 112 | def __init__(self, old_root, new_root):
|
|---|
| 113 | self.old_root = old_root
|
|---|
| 114 | self.new_root = new_root
|
|---|
| 115 |
|
|---|
| 116 | def print_diff (self, old_path, new_path, pool):
|
|---|
| 117 | old_root = new_root = None
|
|---|
| 118 | if old_path:
|
|---|
| 119 | old_root = self.old_root
|
|---|
| 120 | name = old_path
|
|---|
| 121 | if new_path:
|
|---|
| 122 | new_root = self.new_root
|
|---|
| 123 | name = new_path
|
|---|
| 124 | differ = fs.FileDiff(old_root, old_path,
|
|---|
| 125 | new_root, new_path, pool, ['-u'])
|
|---|
| 126 | differ.get_files()
|
|---|
| 127 | pobj = differ.get_pipe()
|
|---|
| 128 | print '<h3>%s</h3>' % name
|
|---|
| 129 | filter = DiffColorizer()
|
|---|
| 130 | while 1:
|
|---|
| 131 | line = pobj.readline()
|
|---|
| 132 | if not line:
|
|---|
| 133 | break
|
|---|
| 134 | filter.writeline(escape(line))
|
|---|
| 135 | filter.close()
|
|---|
| 136 |
|
|---|
| 137 |
|
|---|
| 138 | def add_file(self, path, parent_baton,
|
|---|
| 139 | copyfrom_path, copyfrom_revision, file_pool):
|
|---|
| 140 | self.print_diff (None, path, file_pool)
|
|---|
| 141 | return [None, path, file_pool]
|
|---|
| 142 |
|
|---|
| 143 | def open_file(self, path, parent_baton, base_revision, file_pool):
|
|---|
| 144 | return [path, path, file_pool]
|
|---|
| 145 |
|
|---|
| 146 | def apply_textdelta(self, file_baton, base_checksum):
|
|---|
| 147 | self.print_diff (*file_baton)
|
|---|
| 148 |
|
|---|
| 149 | def render_diffs(fs_ptr, rev, pool):
|
|---|
| 150 | """
|
|---|
| 151 | generates a unified diff of the changes for a given changeset.
|
|---|
| 152 | the output is written to stdout.
|
|---|
| 153 | """
|
|---|
| 154 | old_root = fs.revision_root(fs_ptr, rev - 1, pool)
|
|---|
| 155 | new_root = fs.revision_root(fs_ptr, rev, pool)
|
|---|
| 156 |
|
|---|
| 157 | editor = DiffEditor(old_root, new_root)
|
|---|
| 158 | e_ptr, e_baton = delta.make_editor(editor, pool)
|
|---|
| 159 |
|
|---|
| 160 | repos.svn_repos_dir_delta(old_root, '', None,
|
|---|
| 161 | new_root, '', e_ptr, e_baton,
|
|---|
| 162 | 0, 1, 0, 1, pool)
|
|---|
| 163 |
|
|---|
| 164 | class Changeset (Module):
|
|---|
| 165 | template_key = 'changeset_template'
|
|---|
| 166 |
|
|---|
| 167 | def get_changeset_info (self, rev):
|
|---|
| 168 | cnx = db.get_connection()
|
|---|
| 169 | cursor = cnx.cursor ()
|
|---|
| 170 |
|
|---|
| 171 | cursor.execute ('SELECT time, author, message FROM revision ' +
|
|---|
| 172 | 'WHERE rev=%d' % rev)
|
|---|
| 173 | return cursor.fetchone()
|
|---|
| 174 |
|
|---|
| 175 | def get_change_info (self, rev):
|
|---|
| 176 | cnx = db.get_connection()
|
|---|
| 177 | cursor = cnx.cursor ()
|
|---|
| 178 |
|
|---|
| 179 | cursor.execute ('SELECT name, change FROM node_change ' +
|
|---|
| 180 | 'WHERE rev=%d' % rev)
|
|---|
| 181 | return cursor.fetchall()
|
|---|
| 182 |
|
|---|
| 183 | def print_item (self, out, item):
|
|---|
| 184 | action = {
|
|---|
| 185 | 'A': 'added',
|
|---|
| 186 | 'D': 'deleted',
|
|---|
| 187 | 'M': 'modified'
|
|---|
| 188 | }
|
|---|
| 189 | out.write ('<tr>\n')
|
|---|
| 190 | if item['change'] in ['A', 'M']:
|
|---|
| 191 | out.write ('<td><a href="%s">%s</a></td><td>%s</td>'
|
|---|
| 192 | % (log_href (item['name']),
|
|---|
| 193 | item['name'],
|
|---|
| 194 | action[item['change']]))
|
|---|
| 195 | else:
|
|---|
| 196 | out.write ('<td>%s</td><td>%s</td>' % (item['name'],
|
|---|
| 197 | action[item['change']]))
|
|---|
| 198 | out.write ('</tr>')
|
|---|
| 199 |
|
|---|
| 200 | def render (self):
|
|---|
| 201 | perm.assert_permission (perm.CHANGESET_VIEW)
|
|---|
| 202 |
|
|---|
| 203 | if self.args.has_key('rev'):
|
|---|
| 204 | self.rev = int(self.args['rev'])
|
|---|
| 205 | else:
|
|---|
| 206 | self.rev = fs.youngest_rev(self.fs_ptr, self.pool)
|
|---|
| 207 |
|
|---|
| 208 | out = StringIO.StringIO()
|
|---|
| 209 | change_info = self.get_change_info (self.rev)
|
|---|
| 210 |
|
|---|
| 211 | for item in change_info:
|
|---|
| 212 | self.print_item (out, item)
|
|---|
| 213 |
|
|---|
| 214 | changeset_info = self.get_changeset_info (self.rev)
|
|---|
| 215 |
|
|---|
| 216 | self.namespace['time'] = time_to_string (int(changeset_info['time']))
|
|---|
| 217 | self.namespace['author'] = changeset_info['author']
|
|---|
| 218 | self.namespace['message'] = changeset_info['message']
|
|---|
| 219 | self.namespace['revision'] = self.rev
|
|---|
| 220 | self.namespace['changeset_info'] = out.getvalue()
|
|---|
| 221 |
|
|---|
| 222 | def apply_template (self):
|
|---|
| 223 | Module.apply_template(self)
|
|---|
| 224 | print '<h3>diffs</h3>'
|
|---|
| 225 | render_diffs(self.fs_ptr, int(self.rev), self.pool)
|
|---|
| 226 | print self.namespace['footer']
|
|---|