Files @ 02a26ed03537
Branch filter:

Location: kallithea/rhodecode/controllers/changeset.py - annotation

Marcin Kuzminski
fixed instructions for git
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
30ad41c76fae
30ad41c76fae
30ad41c76fae
30ad41c76fae
30ad41c76fae
6832ef664673
28524453bb76
6832ef664673
30ad41c76fae
30ad41c76fae
6832ef664673
30ad41c76fae
30ad41c76fae
a671db5bdd58
a671db5bdd58
a671db5bdd58
a671db5bdd58
6832ef664673
1e757ac98988
1e757ac98988
1e757ac98988
1e757ac98988
6832ef664673
1e757ac98988
a671db5bdd58
30ad41c76fae
30ad41c76fae
22333ddd1a40
22333ddd1a40
30ad41c76fae
1e757ac98988
1e757ac98988
1e757ac98988
d2de0c2f02cd
30ad41c76fae
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
30ad41c76fae
cac5109ac3b6
3fc9183e05dd
3765f0f479a2
87ec80c280bb
1d1ccb873d00
d2de0c2f02cd
d2de0c2f02cd
54687aa00724
1e757ac98988
1e757ac98988
1e757ac98988
50e41777675d
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
22333ddd1a40
3fc9183e05dd
ffd07396d315
1e757ac98988
1e757ac98988
ffd07396d315
1e757ac98988
1e757ac98988
1cecc7db3106
ffd07396d315
1e757ac98988
ffd07396d315
22333ddd1a40
22333ddd1a40
22333ddd1a40
ffd07396d315
28524453bb76
28524453bb76
d2de0c2f02cd
1e757ac98988
28524453bb76
28524453bb76
28524453bb76
7bb902022d86
7bb902022d86
28524453bb76
899c19b0865c
57a2b792a3a1
57a2b792a3a1
833f9dec0a06
833f9dec0a06
57a2b792a3a1
6d2239363c81
1e757ac98988
c8bd0e6cc3da
ffd07396d315
28524453bb76
28524453bb76
28524453bb76
28524453bb76
0a2a10a1912f
0a2a10a1912f
215a4801bb1e
ffd07396d315
d2de0c2f02cd
7276b170ce8b
7276b170ce8b
7a0004efde12
28524453bb76
7c487d2678c7
7c487d2678c7
7c487d2678c7
7276b170ce8b
7276b170ce8b
7276b170ce8b
7276b170ce8b
28524453bb76
28524453bb76
28524453bb76
28524453bb76
28524453bb76
28524453bb76
28524453bb76
1e757ac98988
28524453bb76
28524453bb76
7a0004efde12
7fcc539e20b1
1e757ac98988
1e757ac98988
4209cd4b7823
1e757ac98988
7a0004efde12
7a0004efde12
7a0004efde12
1e757ac98988
22333ddd1a40
22333ddd1a40
22333ddd1a40
30ad41c76fae
1d1ccb873d00
22333ddd1a40
22333ddd1a40
1d1ccb873d00
7a0004efde12
7a0004efde12
0a2a10a1912f
7a0004efde12
1e757ac98988
50e41777675d
50e41777675d
50e41777675d
1cecc7db3106
1cecc7db3106
ffd07396d315
1e757ac98988
ffd07396d315
0a2a10a1912f
0a2a10a1912f
0a2a10a1912f
0a2a10a1912f
ffd07396d315
28524453bb76
1e757ac98988
28524453bb76
1cecc7db3106
1cecc7db3106
1cecc7db3106
1cecc7db3106
1cecc7db3106
7a0004efde12
50e41777675d
50e41777675d
ffd07396d315
1cecc7db3106
1cecc7db3106
4209cd4b7823
1cecc7db3106
ffd07396d315
1cecc7db3106
22333ddd1a40
22333ddd1a40
22333ddd1a40
1d1ccb873d00
22333ddd1a40
22333ddd1a40
1d1ccb873d00
0a2a10a1912f
4209cd4b7823
7a0004efde12
7a0004efde12
7a0004efde12
7a0004efde12
7a0004efde12
7a0004efde12
7a0004efde12
1cecc7db3106
1cecc7db3106
1cecc7db3106
50e41777675d
50e41777675d
50e41777675d
1cecc7db3106
1cecc7db3106
ffd07396d315
1cecc7db3106
1cecc7db3106
0a2a10a1912f
0a2a10a1912f
0a2a10a1912f
0a2a10a1912f
ffd07396d315
28524453bb76
6832ef664673
28524453bb76
1cecc7db3106
1cecc7db3106
50e41777675d
4209cd4b7823
ffd07396d315
7276b170ce8b
7276b170ce8b
7276b170ce8b
7276b170ce8b
7276b170ce8b
28524453bb76
28524453bb76
28524453bb76
28524453bb76
28524453bb76
28524453bb76
28524453bb76
1e757ac98988
1e757ac98988
ffd07396d315
1e757ac98988
f28dc032adf0
5610fd9b6803
1e757ac98988
3fc9183e05dd
3fc9183e05dd
1e757ac98988
1e757ac98988
ffd07396d315
1e757ac98988
1e757ac98988
28524453bb76
1e757ac98988
28524453bb76
1e757ac98988
ffd07396d315
1e757ac98988
1e757ac98988
1e757ac98988
30ad41c76fae
ffd07396d315
1d1ccb873d00
5610fd9b6803
5610fd9b6803
1d1ccb873d00
50e41777675d
1e757ac98988
1e757ac98988
ffd07396d315
1e757ac98988
ffd07396d315
1e757ac98988
28524453bb76
1e757ac98988
1e757ac98988
ffd07396d315
1d1ccb873d00
5610fd9b6803
5610fd9b6803
1d1ccb873d00
50e41777675d
1e757ac98988
ffd07396d315
ffd07396d315
ffd07396d315
ffd07396d315
1e757ac98988
30ad41c76fae
1e757ac98988
50e41777675d
50e41777675d
30ad41c76fae
edfbf3ee9965
edfbf3ee9965
ffd07396d315
1e757ac98988
1e757ac98988
1e757ac98988
30ad41c76fae
1e757ac98988
d2de0c2f02cd
d2de0c2f02cd
cac5109ac3b6
cac5109ac3b6
cac5109ac3b6
cac5109ac3b6
cac5109ac3b6
cac5109ac3b6
8ecc6b8229a5
d2de0c2f02cd
d2de0c2f02cd
d2de0c2f02cd
d2de0c2f02cd
7d1fc253549e
6f0143e5efe5
cac5109ac3b6
cac5109ac3b6
54687aa00724
8ecc6b8229a5
6f0143e5efe5
6f0143e5efe5
6f0143e5efe5
6f0143e5efe5
# -*- coding: utf-8 -*-
"""
    rhodecode.controllers.changeset
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    changeset controller for pylons showoing changes beetween
    revisions

    :created_on: Apr 25, 2010
    :author: marcink
    :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
    :license: GPLv3, see COPYING for more details.
"""
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
import logging
import traceback
from collections import defaultdict
from webob.exc import HTTPForbidden

from pylons import tmpl_context as c, url, request, response
from pylons.i18n.translation import _
from pylons.controllers.util import redirect
from pylons.decorators import jsonify

from vcs.exceptions import RepositoryError, ChangesetError, \
    ChangesetDoesNotExistError
from vcs.nodes import FileNode

import rhodecode.lib.helpers as h
from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
from rhodecode.lib.base import BaseRepoController, render
from rhodecode.lib.utils import EmptyChangeset
from rhodecode.lib.compat import OrderedDict
from rhodecode.lib import diffs
from rhodecode.model.db import ChangesetComment
from rhodecode.model.comment import ChangesetCommentsModel
from rhodecode.model.meta import Session

log = logging.getLogger(__name__)


def anchor_url(revision,path):
    fid = h.FID(revision, path)
    return h.url.current(anchor=fid,**request.GET)

def get_ignore_ws(fid, GET):
    ig_ws_global = request.GET.get('ignorews')
    ig_ws = filter(lambda k:k.startswith('WS'),GET.getall(fid))
    if ig_ws:
        try:
            return int(ig_ws[0].split(':')[-1])
        except:
            pass
    return ig_ws_global

def _ignorews_url(fileid=None):

    params = defaultdict(list)
    lbl = _('show white space')
    ig_ws = get_ignore_ws(fileid, request.GET)
    ln_ctx = get_line_ctx(fileid, request.GET)
    # global option
    if fileid is None:
        if ig_ws is None:
            params['ignorews'] += [1]
            lbl = _('ignore white space')
        ctx_key = 'context'
        ctx_val = ln_ctx
    # per file options
    else:
        if ig_ws is None:
            params[fileid] += ['WS:1']
            lbl = _('ignore white space')

        ctx_key = fileid
        ctx_val = 'C:%s' % ln_ctx
    # if we have passed in ln_ctx pass it along to our params
    if ln_ctx:
        params[ctx_key] += [ctx_val]
    
    params['anchor'] = fileid
    return h.link_to(lbl, h.url.current(**params))

def get_line_ctx(fid, GET):
    ln_ctx_global = request.GET.get('context')
    ln_ctx = filter(lambda k:k.startswith('C'),GET.getall(fid))
    
    if ln_ctx:
        retval = ln_ctx[0].split(':')[-1]
    else:
        retval = ln_ctx_global

    try:
        return int(retval)
    except:
        return

def _context_url(fileid=None):
    """
    Generates url for context lines
    
    :param fileid:
    """
    ig_ws = get_ignore_ws(fileid, request.GET)
    ln_ctx = (get_line_ctx(fileid, request.GET) or 3) * 2

    params = defaultdict(list)

    # global option
    if fileid is None:
        if ln_ctx > 0:
            params['context'] += [ln_ctx]

        if ig_ws:
            ig_ws_key = 'ignorews'
            ig_ws_val = 1

    # per file option
    else:
        params[fileid] += ['C:%s' % ln_ctx]
        ig_ws_key = fileid
        ig_ws_val = 'WS:%s' % 1
        
    if ig_ws:
        params[ig_ws_key] += [ig_ws_val]

    lbl = _('%s line context') % ln_ctx

    params['anchor'] = fileid
    return h.link_to(lbl, h.url.current(**params))

def wrap_to_table(str_):
    return '''<table class="code-difftable">
                <tr class="line">
                <td class="lineno new"></td>
                <td class="code"><pre>%s</pre></td>
                </tr>
              </table>''' % str_

class ChangesetController(BaseRepoController):

    @LoginRequired()
    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
                                   'repository.admin')
    def __before__(self):
        super(ChangesetController, self).__before__()
        c.affected_files_cut_off = 60

    def index(self, revision):

        c.anchor_url = anchor_url
        c.ignorews_url = _ignorews_url
        c.context_url = _context_url

        #get ranges of revisions if preset
        rev_range = revision.split('...')[:2]

        try:
            if len(rev_range) == 2:
                rev_start = rev_range[0]
                rev_end = rev_range[1]
                rev_ranges = c.rhodecode_repo.get_changesets(start=rev_start,
                                                            end=rev_end)
            else:
                rev_ranges = [c.rhodecode_repo.get_changeset(revision)]

            c.cs_ranges = list(rev_ranges)
            if not c.cs_ranges:
                raise RepositoryError('Changeset range returned empty result')

        except (RepositoryError, ChangesetDoesNotExistError, Exception), e:
            log.error(traceback.format_exc())
            h.flash(str(e), category='warning')
            return redirect(url('home'))

        c.changes = OrderedDict()
        c.sum_added = 0
        c.sum_removed = 0
        c.lines_added = 0
        c.lines_deleted = 0
        c.cut_off = False  # defines if cut off limit is reached

        c.comments = []
        c.inline_comments = []
        c.inline_cnt = 0
        # Iterate over ranges (default changeset view is always one changeset)
        for changeset in c.cs_ranges:
            c.comments.extend(ChangesetCommentsModel()\
                              .get_comments(c.rhodecode_db_repo.repo_id,
                                            changeset.raw_id))
            inlines = ChangesetCommentsModel()\
                        .get_inline_comments(c.rhodecode_db_repo.repo_id,
                                             changeset.raw_id)
            c.inline_comments.extend(inlines)
            c.changes[changeset.raw_id] = []
            try:
                changeset_parent = changeset.parents[0]
            except IndexError:
                changeset_parent = None

            #==================================================================
            # ADDED FILES
            #==================================================================
            for node in changeset.added:

                filenode_old = FileNode(node.path, '', EmptyChangeset())
                if filenode_old.is_binary or node.is_binary:
                    diff = wrap_to_table(_('binary file'))
                    st = (0, 0)
                else:
                    # in this case node.size is good parameter since those are
                    # added nodes and their size defines how many changes were
                    # made
                    c.sum_added += node.size
                    fid = h.FID(revision, node.path)
                    line_context_lcl = get_line_ctx(fid, request.GET)
                    ignore_whitespace_lcl = get_ignore_ws(fid, request.GET)
                    if c.sum_added < self.cut_off_limit:
                        f_gitdiff = diffs.get_gitdiff(filenode_old, node,
                                        ignore_whitespace=ignore_whitespace_lcl,
                                        context=line_context_lcl)
                        d = diffs.DiffProcessor(f_gitdiff, format='gitdiff')

                        st = d.stat()
                        diff = d.as_html()

                    else:
                        diff = wrap_to_table(_('Changeset is to big and '
                                               'was cut off, see raw '
                                               'changeset instead'))
                        c.cut_off = True
                        break

                cs1 = None
                cs2 = node.last_changeset.raw_id
                c.lines_added += st[0]
                c.lines_deleted += st[1]
                c.changes[changeset.raw_id].append(('added', node, diff,
                                                    cs1, cs2, st))

            #==================================================================
            # CHANGED FILES
            #==================================================================
            if not c.cut_off:
                for node in changeset.changed:
                    try:
                        filenode_old = changeset_parent.get_node(node.path)
                    except ChangesetError:
                        log.warning('Unable to fetch parent node for diff')
                        filenode_old = FileNode(node.path, '',
                                                EmptyChangeset())

                    if filenode_old.is_binary or node.is_binary:
                        diff = wrap_to_table(_('binary file'))
                        st = (0, 0)
                    else:

                        if c.sum_removed < self.cut_off_limit:
                            fid = h.FID(revision, node.path)
                            line_context_lcl = get_line_ctx(fid, request.GET)
                            ignore_whitespace_lcl = get_ignore_ws(fid, request.GET,)
                            f_gitdiff = diffs.get_gitdiff(filenode_old, node,
                                    ignore_whitespace=ignore_whitespace_lcl,
                                    context=line_context_lcl)
                            d = diffs.DiffProcessor(f_gitdiff,
                                                     format='gitdiff')
                            st = d.stat()
                            if (st[0] + st[1]) * 256 > self.cut_off_limit:
                                diff = wrap_to_table(_('Diff is to big '
                                                       'and was cut off, see '
                                                       'raw diff instead'))
                            else:
                                diff = d.as_html()

                            if diff:
                                c.sum_removed += len(diff)
                        else:
                            diff = wrap_to_table(_('Changeset is to big and '
                                                   'was cut off, see raw '
                                                   'changeset instead'))
                            c.cut_off = True
                            break

                    cs1 = filenode_old.last_changeset.raw_id
                    cs2 = node.last_changeset.raw_id
                    c.lines_added += st[0]
                    c.lines_deleted += st[1]
                    c.changes[changeset.raw_id].append(('changed', node, diff,
                                                        cs1, cs2, st))

            #==================================================================
            # REMOVED FILES
            #==================================================================
            if not c.cut_off:
                for node in changeset.removed:
                    c.changes[changeset.raw_id].append(('removed', node, None,
                                                        None, None, (0, 0)))

        # count inline comments
        for path, lines in c.inline_comments:
            for comments in lines.values():
                c.inline_cnt += len(comments)

        if len(c.cs_ranges) == 1:
            c.changeset = c.cs_ranges[0]
            c.changes = c.changes[c.changeset.raw_id]

            return render('changeset/changeset.html')
        else:
            return render('changeset/changeset_range.html')

    def raw_changeset(self, revision):

        method = request.GET.get('diff', 'show')
        ignore_whitespace = request.GET.get('ignorews') == '1'
        line_context = request.GET.get('context', 3)
        try:
            c.scm_type = c.rhodecode_repo.alias
            c.changeset = c.rhodecode_repo.get_changeset(revision)
        except RepositoryError:
            log.error(traceback.format_exc())
            return redirect(url('home'))
        else:
            try:
                c.changeset_parent = c.changeset.parents[0]
            except IndexError:
                c.changeset_parent = None
            c.changes = []

            for node in c.changeset.added:
                filenode_old = FileNode(node.path, '')
                if filenode_old.is_binary or node.is_binary:
                    diff = _('binary file') + '\n'
                else:
                    f_gitdiff = diffs.get_gitdiff(filenode_old, node,
                                           ignore_whitespace=ignore_whitespace,
                                           context=line_context)
                    diff = diffs.DiffProcessor(f_gitdiff,
                                                format='gitdiff').raw_diff()

                cs1 = None
                cs2 = node.last_changeset.raw_id
                c.changes.append(('added', node, diff, cs1, cs2))

            for node in c.changeset.changed:
                filenode_old = c.changeset_parent.get_node(node.path)
                if filenode_old.is_binary or node.is_binary:
                    diff = _('binary file')
                else:
                    f_gitdiff = diffs.get_gitdiff(filenode_old, node,
                                           ignore_whitespace=ignore_whitespace,
                                           context=line_context)
                    diff = diffs.DiffProcessor(f_gitdiff,
                                                format='gitdiff').raw_diff()

                cs1 = filenode_old.last_changeset.raw_id
                cs2 = node.last_changeset.raw_id
                c.changes.append(('changed', node, diff, cs1, cs2))

        response.content_type = 'text/plain'

        if method == 'download':
            response.content_disposition = 'attachment; filename=%s.patch' \
                                            % revision

        c.parent_tmpl = ''.join(['# Parent  %s\n' % x.raw_id for x in
                                                 c.changeset.parents])

        c.diffs = ''
        for x in c.changes:
            c.diffs += x[2]

        return render('changeset/raw_changeset.html')

    def comment(self, repo_name, revision):
        ChangesetCommentsModel().create(text=request.POST.get('text'),
                                        repo_id=c.rhodecode_db_repo.repo_id,
                                        user_id=c.rhodecode_user.user_id,
                                        revision=revision,
                                        f_path=request.POST.get('f_path'),
                                        line_no=request.POST.get('line'))
        Session.commit()
        return redirect(h.url('changeset_home', repo_name=repo_name,
                              revision=revision))

    @jsonify
    def delete_comment(self, repo_name, comment_id):
        co = ChangesetComment.get(comment_id)
        owner = lambda : co.author.user_id == c.rhodecode_user.user_id
        if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
            ChangesetCommentsModel().delete(comment=co)
            Session.commit()
            return True
        else:
            raise HTTPForbidden()