~loggerhead-team/loggerhead/trunk-rich

« back to all changes in this revision

Viewing changes to loggerhead/controllers/revision_ui.py

  • Committer: John Arbash Meinel
  • Date: 2011-03-16 12:21:36 UTC
  • mfrom: (411.3.3 simple_mainline)
  • Revision ID: john@arbash-meinel.com-20110316122136-930ttoriqgbw2cy3
Include simple_mainline.

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
18
#
19
19
 
20
 
from StringIO import StringIO
21
 
 
22
 
import bzrlib.diff
23
 
from bzrlib import errors
 
20
import simplejson
 
21
import urllib
24
22
 
25
23
from paste.httpexceptions import HTTPServerError
26
24
 
27
25
from loggerhead import util
28
26
from loggerhead.controllers import TemplatedBranchView
29
 
from loggerhead.history import rich_filename
 
27
from loggerhead.controllers.filediff_ui import diff_chunks_for_file
30
28
 
31
29
 
32
30
DEFAULT_LINE_COUNT_LIMIT = 3000
33
31
 
 
32
def dq(p):
 
33
    return urllib.quote(urllib.quote(p, safe=''))
 
34
 
34
35
 
35
36
class RevisionUI(TemplatedBranchView):
36
37
 
37
38
    template_path = 'loggerhead.templates.revision'
38
39
 
39
 
    def _process_diff(self, diff):
40
 
        # doesn't really need to be a method; could be static.
41
 
        chunks = []
42
 
        chunk = None
43
 
        for line in diff.splitlines():
44
 
            if len(line) == 0:
45
 
                continue
46
 
            if line.startswith('+++ ') or line.startswith('--- '):
47
 
                continue
48
 
            if line.startswith('@@ '):
49
 
                # new chunk
50
 
                if chunk is not None:
51
 
                    chunks.append(chunk)
52
 
                chunk = util.Container()
53
 
                chunk.diff = []
54
 
                split_lines = line.split(' ')[1:3]
55
 
                lines = [int(x.split(',')[0][1:]) for x in split_lines]
56
 
                old_lineno = lines[0]
57
 
                new_lineno = lines[1]
58
 
            elif line.startswith(' '):
59
 
                chunk.diff.append(util.Container(old_lineno=old_lineno,
60
 
                                                 new_lineno=new_lineno,
61
 
                                                 type='context',
62
 
                                                 line=line[1:]))
63
 
                old_lineno += 1
64
 
                new_lineno += 1
65
 
            elif line.startswith('+'):
66
 
                chunk.diff.append(util.Container(old_lineno=None,
67
 
                                                 new_lineno=new_lineno,
68
 
                                                 type='insert', line=line[1:]))
69
 
                new_lineno += 1
70
 
            elif line.startswith('-'):
71
 
                chunk.diff.append(util.Container(old_lineno=old_lineno,
72
 
                                                 new_lineno=None,
73
 
                                                 type='delete', line=line[1:]))
74
 
                old_lineno += 1
75
 
            else:
76
 
                chunk.diff.append(util.Container(old_lineno=None,
77
 
                                                 new_lineno=None,
78
 
                                                 type='unknown',
79
 
                                                 line=repr(line)))
80
 
        if chunk is not None:
81
 
            chunks.append(chunk)
82
 
        return chunks
83
 
 
84
 
    def _parse_diffs(self, old_tree, new_tree, delta, specific_path):
85
 
        """
86
 
        Return a list of processed diffs, in the format::
87
 
 
88
 
            list(
89
 
                filename: str,
90
 
                file_id: str,
91
 
                chunks: list(
92
 
                    diff: list(
93
 
                        old_lineno: int,
94
 
                        new_lineno: int,
95
 
                        type: str('context', 'delete', or 'insert'),
96
 
                        line: str,
97
 
                    ),
98
 
                ),
99
 
            )
100
 
        """
101
 
        process = []
102
 
        out = []
103
 
 
104
 
        def include_specific_path(path):
105
 
            return specific_path == path
106
 
        def include_all_paths(path):
107
 
            return True
108
 
        if specific_path:
109
 
            include_path = include_specific_path
110
 
        else:
111
 
            include_path = include_all_paths
112
 
 
113
 
        for old_path, new_path, fid, \
114
 
            kind, text_modified, meta_modified in delta.renamed:
115
 
            if text_modified and include_path(new_path):
116
 
                process.append((old_path, new_path, fid, kind))
117
 
        for path, fid, kind, text_modified, meta_modified in delta.modified:
118
 
            if include_path(path):
119
 
                process.append((path, path, fid, kind))
120
 
        for path, fid, kind in delta.added:
121
 
            if file == 'kind' and include_path(path):
122
 
                process.append((path, path, fid, kind))
123
 
        for path, fid, kind in delta.removed:
124
 
            if file == 'kind' and include_path(path):
125
 
                process.append((path, path, fid, kind))
126
 
 
127
 
        process.sort(key=lambda x:x[1])
128
 
 
129
 
        for old_path, new_path, fid, kind in process:
130
 
            try:
131
 
                old_lines = old_tree.get_file_lines(fid)
132
 
            except errors.NoSuchId:
133
 
                old_lines = []
134
 
            try:
135
 
                new_lines = new_tree.get_file_lines(fid)
136
 
            except errors.NoSuchId:
137
 
                new_lines = []
138
 
            buffer = StringIO()
139
 
            if old_lines != new_lines:
140
 
                try:
141
 
                    bzrlib.diff.internal_diff(old_path, old_lines,
142
 
                                              new_path, new_lines, buffer)
143
 
                except bzrlib.errors.BinaryFile:
144
 
                    diff = ''
145
 
                else:
146
 
                    diff = buffer.getvalue()
147
 
            else:
148
 
                diff = ''
149
 
            out.append(util.Container(
150
 
                filename=rich_filename(new_path, kind), file_id=fid,
151
 
                chunks=self._process_diff(diff)))
152
 
 
153
 
        return out
154
 
 
155
 
    def get_changes_with_diff(self, change, compare_revid, specific_path):
156
 
        h = self._history
157
 
        if compare_revid is None:
158
 
            if change.parents:
159
 
                compare_revid = change.parents[0].revid
160
 
            else:
161
 
                compare_revid = 'null:'
162
 
 
163
 
        rev_tree1 = h._branch.repository.revision_tree(compare_revid)
164
 
        rev_tree2 = h._branch.repository.revision_tree(change.revid)
165
 
        delta = rev_tree2.changes_from(rev_tree1)
166
 
 
167
 
        changes = h.parse_delta(delta)
168
 
 
169
 
        return changes, self._parse_diffs(
170
 
            rev_tree1, rev_tree2, delta, specific_path)
171
 
 
172
40
    def get_values(self, path, kwargs, headers):
173
41
        h = self._history
174
42
        revid = self.get_revid()
197
65
        util.fill_in_navigation(navigation)
198
66
 
199
67
        change = h.get_changes([revid])[0]
 
68
 
 
69
        if compare_revid is None:
 
70
            file_changes = h.get_file_changes(change)
 
71
        else:
 
72
            file_changes = h.file_changes_for_revision_ids(
 
73
                compare_revid, change.revid)
 
74
 
200
75
        if path in ('', '/'):
201
76
            path = None
202
 
        change.changes, diffs = self.get_changes_with_diff(change, compare_revid, path)
203
 
        # add parent & merge-point branch-nick info, in case it's useful
204
 
        h.get_branch_nicks([change])
 
77
 
 
78
        link_data = {}
 
79
        path_to_id = {}
 
80
        if path:
 
81
            item = [x for x in file_changes.text_changes if x.filename == path][0]
 
82
            diff_chunks = diff_chunks_for_file(
 
83
                self._history._branch.repository, item.file_id,
 
84
                item.old_revision, item.new_revision)
 
85
        else:
 
86
            diff_chunks = None
 
87
            for i, item in enumerate(file_changes.text_changes):
 
88
                item.index = i
 
89
                link_data['diff-' + str(i)] = '%s/%s/%s' % (
 
90
                    dq(item.new_revision), dq(item.old_revision), dq(item.file_id))
 
91
                path_to_id[item.filename] = 'diff-' + str(i)
 
92
 
 
93
        h.add_branch_nicks(change)
 
94
 
 
95
        if '.' in change.revno:
 
96
            # Walk "up" though the merge-sorted graph until we find a
 
97
            # revision with merge depth 0: this is the revision that merged
 
98
            # this one to mainline.
 
99
            ri = self._history._rev_info
 
100
            i = self._history._rev_indices[change.revid]
 
101
            while ri[i][0][2] > 0:
 
102
                i -= 1
 
103
            merged_in = ri[i][0][3]
 
104
        else:
 
105
            merged_in = None
205
106
 
206
107
        # Directory Breadcrumbs
207
108
        directory_breadcrumbs = (
214
115
            'branch': self._branch,
215
116
            'revid': revid,
216
117
            'change': change,
217
 
            'diffs': diffs,
 
118
            'file_changes': file_changes,
 
119
            'diff_chunks': diff_chunks,
 
120
            'link_data': simplejson.dumps(link_data),
218
121
            'specific_path': path,
 
122
            'json_specific_path': simplejson.dumps(path),
 
123
            'path_to_id': simplejson.dumps(path_to_id),
219
124
            'start_revid': start_revid,
220
125
            'filter_file_id': filter_file_id,
221
126
            'util': util,
222
127
            'history': h,
 
128
            'merged_in': merged_in,
223
129
            'navigation': navigation,
224
130
            'query': query,
225
131
            'remember': remember,