/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/annotate.py

  • Committer: Andrew Bennetts
  • Date: 2009-10-21 11:13:40 UTC
  • mto: This revision was merged to the branch mainline in revision 4762.
  • Revision ID: andrew.bennetts@canonical.com-20091021111340-w7x4d5yf83qwjncc
Add test that WSGI glue allows request handlers to access paths above that request's. backing transport, so long as it is within the WSGI app's backing transport.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
16
16
 
17
17
"""File annotate based on weave storage"""
18
18
 
19
 
from __future__ import absolute_import
20
 
 
21
19
# TODO: Choice of more or less verbose formats:
22
20
#
23
21
# interposed: show more details between blocks of modified lines
30
28
import sys
31
29
import time
32
30
 
33
 
from .lazy_import import lazy_import
34
 
lazy_import(globals(), """
35
 
from breezy import (
 
31
from bzrlib import (
 
32
    errors,
 
33
    osutils,
36
34
    patiencediff,
37
35
    tsort,
38
36
    )
39
 
""")
40
 
from . import (
41
 
    errors,
42
 
    osutils,
43
 
    )
44
 
from .config import (
45
 
    NoEmailInUsername,
46
 
    NoWhoami,
47
 
    extract_email_address,
48
 
    )
49
 
from .repository import _strip_NULL_ghosts
50
 
from .revision import (
51
 
    CURRENT_REVISION,
52
 
    Revision,
53
 
    )
54
 
 
55
 
 
56
 
def annotate_file_tree(tree, path, to_file, verbose=False, full=False,
57
 
    show_ids=False, branch=None, file_id=None):
 
37
from bzrlib.config import extract_email_address
 
38
from bzrlib.repository import _strip_NULL_ghosts
 
39
from bzrlib.revision import CURRENT_REVISION, Revision
 
40
 
 
41
 
 
42
def annotate_file(branch, rev_id, file_id, verbose=False, full=False,
 
43
                  to_file=None, show_ids=False):
 
44
    """Annotate file_id at revision rev_id in branch.
 
45
 
 
46
    The branch should already be read_locked() when annotate_file is called.
 
47
 
 
48
    :param branch: The branch to look for revision numbers and history from.
 
49
    :param rev_id: The revision id to annotate.
 
50
    :param file_id: The file_id to annotate.
 
51
    :param verbose: Show all details rather than truncating to ensure
 
52
        reasonable text width.
 
53
    :param full: XXXX Not sure what this does.
 
54
    :param to_file: The file to output the annotation to; if None stdout is
 
55
        used.
 
56
    :param show_ids: Show revision ids in the annotation output.
 
57
    """
 
58
    if to_file is None:
 
59
        to_file = sys.stdout
 
60
 
 
61
    # Handle the show_ids case
 
62
    annotations = _annotations(branch.repository, file_id, rev_id)
 
63
    if show_ids:
 
64
        return _show_id_annotations(annotations, to_file, full)
 
65
 
 
66
    # Calculate the lengths of the various columns
 
67
    annotation = list(_expand_annotations(annotations, branch))
 
68
    _print_annotations(annotation, verbose, to_file, full)
 
69
 
 
70
 
 
71
def annotate_file_tree(tree, file_id, to_file, verbose=False, full=False,
 
72
    show_ids=False):
58
73
    """Annotate file_id in a tree.
59
74
 
60
75
    The tree should already be read_locked() when annotate_file_tree is called.
61
76
 
62
77
    :param tree: The tree to look for revision numbers and history from.
63
 
    :param path: The path to annotate
 
78
    :param file_id: The file_id to annotate.
64
79
    :param to_file: The file to output the annotation to.
65
80
    :param verbose: Show all details rather than truncating to ensure
66
81
        reasonable text width.
67
82
    :param full: XXXX Not sure what this does.
68
83
    :param show_ids: Show revision ids in the annotation output.
69
 
    :param file_id: The file_id to annotate (must match file path)
70
 
    :param branch: Branch to use for revision revno lookups
71
84
    """
72
 
    if branch is None:
73
 
        branch = tree.branch
74
 
    if to_file is None:
75
 
        to_file = sys.stdout
 
85
    rev_id = tree.last_revision()
 
86
    branch = tree.branch
76
87
 
77
88
    # Handle the show_ids case
78
 
    annotations = list(tree.annotate_iter(path, file_id))
 
89
    annotations = list(tree.annotate_iter(file_id))
79
90
    if show_ids:
80
91
        return _show_id_annotations(annotations, to_file, full)
81
92
 
82
 
    if not getattr(tree, "get_revision_id", False):
83
 
        # Create a virtual revision to represent the current tree state.
84
 
        # Should get some more pending commit attributes, like pending tags,
85
 
        # bugfixes etc.
86
 
        current_rev = Revision(CURRENT_REVISION)
87
 
        current_rev.parent_ids = tree.get_parent_ids()
88
 
        try:
89
 
            current_rev.committer = branch.get_config_stack().get('email')
90
 
        except NoWhoami:
91
 
            current_rev.committer = 'local user'
92
 
        current_rev.message = "?"
93
 
        current_rev.timestamp = round(time.time(), 3)
94
 
        current_rev.timezone = osutils.local_time_offset()
95
 
    else:
96
 
        current_rev = None
97
 
    annotation = list(_expand_annotations(annotations, branch,
 
93
    # Create a virtual revision to represent the current tree state.
 
94
    # Should get some more pending commit attributes, like pending tags,
 
95
    # bugfixes etc.
 
96
    current_rev = Revision(CURRENT_REVISION)
 
97
    current_rev.parent_ids = tree.get_parent_ids()
 
98
    current_rev.committer = tree.branch.get_config().username()
 
99
    current_rev.message = "?"
 
100
    current_rev.timestamp = round(time.time(), 3)
 
101
    current_rev.timezone = osutils.local_time_offset()
 
102
    annotation = list(_expand_annotations(annotations, tree.branch,
98
103
        current_rev))
99
104
    _print_annotations(annotation, verbose, to_file, full)
100
105
 
119
124
 
120
125
    # Output the annotations
121
126
    prevanno = ''
 
127
    encoding = getattr(to_file, 'encoding', None) or \
 
128
            osutils.get_terminal_encoding()
122
129
    for (revno_str, author, date_str, line_rev_id, text) in annotation:
123
130
        if verbose:
124
131
            anno = '%-*s %-*s %8s ' % (max_revno_len, revno_str,
129
136
            anno = "%-*s %-7s " % (max_revno_len, revno_str, author[:7])
130
137
        if anno.lstrip() == "" and full:
131
138
            anno = prevanno
132
 
        # GZ 2017-05-21: Writing both unicode annotation and bytes from file
133
 
        # which the given to_file must cope with.
134
 
        to_file.write(anno)
 
139
        try:
 
140
            to_file.write(anno)
 
141
        except UnicodeEncodeError:
 
142
            # cmd_annotate should be passing in an 'exact' object, which means
 
143
            # we have a direct handle to sys.stdout or equivalent. It may not
 
144
            # be able to handle the exact Unicode characters, but 'annotate' is
 
145
            # a user function (non-scripting), so shouldn't die because of
 
146
            # unrepresentable annotation characters. So encode using 'replace',
 
147
            # and write them again.
 
148
            to_file.write(anno.encode(encoding, 'replace'))
135
149
        to_file.write('| %s\n' % (text,))
136
150
        prevanno = anno
137
151
 
151
165
    return
152
166
 
153
167
 
 
168
def _annotations(repo, file_id, rev_id):
 
169
    """Return the list of (origin_revision_id, line_text) for a revision of a file in a repository."""
 
170
    annotations = repo.texts.annotate((file_id, rev_id))
 
171
    #
 
172
    return [(key[-1], line) for (key, line) in annotations]
 
173
 
 
174
 
154
175
def _expand_annotations(annotations, branch, current_rev=None):
155
176
    """Expand a file's annotations into command line UI ready tuples.
156
177
 
162
183
    :param branch: A locked branch to query for revision details.
163
184
    """
164
185
    repository = branch.repository
165
 
    revision_ids = set(o for o, t in annotations)
166
186
    if current_rev is not None:
167
 
        # This can probably become a function on MutableTree, get_revno_map
168
 
        # there, or something.
 
187
        # This can probably become a function on MutableTree, get_revno_map there,
 
188
        # or something.
169
189
        last_revision = current_rev.revision_id
170
190
        # XXX: Partially Cloned from branch, uses the old_get_graph, eep.
171
191
        # XXX: The main difficulty is that we need to inject a single new node
186
206
            for seq_num, rev_id, depth, revno, end_of_merge in
187
207
                merge_sorted_revisions)
188
208
    else:
189
 
        # TODO(jelmer): Only look up the revision ids that we need (i.e. those
190
 
        # in revision_ids). Possibly add a HPSS call that can look those up
191
 
        # in bulk over HPSS.
192
209
        revision_id_to_revno = branch.get_revision_id_to_revno_map()
193
210
    last_origin = None
 
211
    revision_ids = set(o for o, t in annotations)
194
212
    revisions = {}
195
213
    if CURRENT_REVISION in revision_ids:
196
214
        revision_id_to_revno[CURRENT_REVISION] = (
197
215
            "%d?" % (branch.revno() + 1),)
198
216
        revisions[CURRENT_REVISION] = current_rev
199
 
    revisions.update(
200
 
            entry for entry in
201
 
            repository.iter_revisions(revision_ids)
202
 
            if entry[1] is not None)
 
217
    revision_ids = [o for o in revision_ids if
 
218
                    repository.has_revision(o)]
 
219
    revisions.update((r.revision_id, r) for r in
 
220
                     repository.get_revisions(revision_ids))
203
221
    for origin, text in annotations:
204
 
        text = text.rstrip(b'\r\n')
 
222
        text = text.rstrip('\r\n')
205
223
        if origin == last_origin:
206
 
            (revno_str, author, date_str) = ('', '', '')
 
224
            (revno_str, author, date_str) = ('','','')
207
225
        else:
208
226
            last_origin = origin
209
227
            if origin not in revisions:
210
 
                (revno_str, author, date_str) = ('?', '?', '?')
 
228
                (revno_str, author, date_str) = ('?','?','?')
211
229
            else:
212
230
                revno_str = '.'.join(str(i) for i in
213
231
                                            revision_id_to_revno[origin])
220
238
            author = rev.get_apparent_authors()[0]
221
239
            try:
222
240
                author = extract_email_address(author)
223
 
            except NoEmailInUsername:
 
241
            except errors.NoEmailInUsername:
224
242
                pass        # use the whole name
225
243
        yield (revno_str, author, date_str, origin, text)
226
244
 
294
312
 
295
313
 
296
314
def _get_matching_blocks(old, new):
297
 
    matcher = patiencediff.PatienceSequenceMatcher(None, old, new)
 
315
    matcher = patiencediff.PatienceSequenceMatcher(None,
 
316
        old, new)
298
317
    return matcher.get_matching_blocks()
299
318
 
300
319
 
347
366
    output_extend = output_lines.extend
348
367
    output_append = output_lines.append
349
368
    # We need to see if any of the unannotated lines match
350
 
    plain_right_subset = [l for a, l in right_lines[start_right:end_right]]
 
369
    plain_right_subset = [l for a,l in right_lines[start_right:end_right]]
351
370
    plain_child_subset = plain_child_lines[start_child:end_child]
352
371
    match_blocks = _get_matching_blocks(plain_right_subset, plain_child_subset)
353
372
 
358
377
        if child_idx > last_child_idx:
359
378
            output_extend(child_lines[start_child + last_child_idx
360
379
                                      :start_child + child_idx])
361
 
        for offset in range(match_len):
 
380
        for offset in xrange(match_len):
362
381
            left = child_lines[start_child+child_idx+offset]
363
382
            right = right_lines[start_right+right_idx+offset]
364
383
            if left[0] == right[0]:
375
394
                else:
376
395
                    heads = heads_provider.heads((left[0], right[0]))
377
396
                    if len(heads) == 1:
378
 
                        output_append((next(iter(heads)), left[1]))
 
397
                        output_append((iter(heads).next(), left[1]))
379
398
                    else:
380
399
                        # Both claim different origins, get a stable result.
381
400
                        # If the result is not stable, there is a risk a
438
457
 
439
458
 
440
459
try:
441
 
    from breezy._annotator_pyx import Annotator
442
 
except ImportError as e:
 
460
    from bzrlib._annotator_pyx import Annotator
 
461
except ImportError, e:
443
462
    osutils.failed_to_load_extension(e)
444
 
    from breezy._annotator_py import Annotator
 
463
    from bzrlib._annotator_py import Annotator