/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: James Westby
  • Date: 2007-09-18 18:55:00 UTC
  • mto: (2866.1.1 james.westby)
  • mto: This revision was merged to the branch mainline in revision 2867.
  • Revision ID: jw+debian@jameswestby.net-20070918185500-91alkjx8zolds1v8
Fix log against smart server branches that don't support tags. (#140615)

Add get_reverse_tag_dict to DisabledTags for branches that falsely
claim that they support tags (namely smart server branches). When the
remote branch was an old format without tags this caused log to fail.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2004, 2005, 2006, 2007 Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
"""File annotate based on weave storage"""
 
18
 
 
19
# TODO: Choice of more or less verbose formats:
 
20
 
21
# interposed: show more details between blocks of modified lines
 
22
 
 
23
# TODO: Show which revision caused a line to merge into the parent
 
24
 
 
25
# TODO: perhaps abbreviate timescales depending on how recent they are
 
26
# e.g. "3:12 Tue", "13 Oct", "Oct 2005", etc.  
 
27
 
 
28
import sys
 
29
import time
 
30
 
 
31
from bzrlib import (
 
32
    errors,
 
33
    osutils,
 
34
    patiencediff,
 
35
    tsort,
 
36
    )
 
37
from bzrlib.config import extract_email_address
 
38
 
 
39
 
 
40
def annotate_file(branch, rev_id, file_id, verbose=False, full=False,
 
41
                  to_file=None, show_ids=False):
 
42
    if to_file is None:
 
43
        to_file = sys.stdout
 
44
 
 
45
    prevanno=''
 
46
    last_rev_id = None
 
47
    if show_ids:
 
48
        w = branch.repository.weave_store.get_weave(file_id,
 
49
            branch.repository.get_transaction())
 
50
        annotations = list(w.annotate_iter(rev_id))
 
51
        max_origin_len = max(len(origin) for origin, text in annotations)
 
52
        for origin, text in annotations:
 
53
            if full or last_rev_id != origin:
 
54
                this = origin
 
55
            else:
 
56
                this = ''
 
57
            to_file.write('%*s | %s' % (max_origin_len, this, text))
 
58
            last_rev_id = origin
 
59
        return
 
60
 
 
61
    annotation = list(_annotate_file(branch, rev_id, file_id))
 
62
    if len(annotation) == 0:
 
63
        max_origin_len = max_revno_len = max_revid_len = 0
 
64
    else:
 
65
        max_origin_len = max(len(x[1]) for x in annotation)
 
66
        max_revno_len = max(len(x[0]) for x in annotation)
 
67
        max_revid_len = max(len(x[3]) for x in annotation)
 
68
 
 
69
    if not verbose:
 
70
        max_revno_len = min(max_revno_len, 12)
 
71
    max_revno_len = max(max_revno_len, 3)
 
72
 
 
73
    for (revno_str, author, date_str, line_rev_id, text) in annotation:
 
74
        if verbose:
 
75
            anno = '%-*s %-*s %8s ' % (max_revno_len, revno_str,
 
76
                                       max_origin_len, author, date_str)
 
77
        else:
 
78
            if len(revno_str) > max_revno_len:
 
79
                revno_str = revno_str[:max_revno_len-1] + '>'
 
80
            anno = "%-*s %-7s " % (max_revno_len, revno_str, author[:7])
 
81
 
 
82
        if anno.lstrip() == "" and full: anno = prevanno
 
83
        try:
 
84
            to_file.write(anno)
 
85
        except UnicodeEncodeError:
 
86
            # cmd_annotate should be passing in an 'exact' object, which means
 
87
            # we have a direct handle to sys.stdout or equivalent. It may not
 
88
            # be able to handle the exact Unicode characters, but 'annotate' is
 
89
            # a user function (non-scripting), so shouldn't die because of
 
90
            # unrepresentable annotation characters. So encode using 'replace',
 
91
            # and write them again.
 
92
            encoding = getattr(to_file, 'encoding', None) or \
 
93
                    osutils.get_terminal_encoding()
 
94
            to_file.write(anno.encode(encoding, 'replace'))
 
95
        print >>to_file, '| %s' % (text,)
 
96
        prevanno=anno
 
97
 
 
98
 
 
99
def _annotate_file(branch, rev_id, file_id):
 
100
    """Yield the origins for each line of a file.
 
101
 
 
102
    This includes detailed information, such as the author name, and
 
103
    date string for the commit, rather than just the revision id.
 
104
    """
 
105
    revision_id_to_revno = branch.get_revision_id_to_revno_map()
 
106
    w = branch.repository.weave_store.get_weave(file_id,
 
107
        branch.repository.get_transaction())
 
108
    last_origin = None
 
109
    annotations = list(w.annotate_iter(rev_id))
 
110
    revision_ids = set(o for o, t in annotations)
 
111
    revision_ids = [o for o in revision_ids if 
 
112
                    branch.repository.has_revision(o)]
 
113
    revisions = dict((r.revision_id, r) for r in 
 
114
                     branch.repository.get_revisions(revision_ids))
 
115
    for origin, text in annotations:
 
116
        text = text.rstrip('\r\n')
 
117
        if origin == last_origin:
 
118
            (revno_str, author, date_str) = ('','','')
 
119
        else:
 
120
            last_origin = origin
 
121
            if origin not in revisions:
 
122
                (revno_str, author, date_str) = ('?','?','?')
 
123
            else:
 
124
                revno_str = '.'.join(str(i) for i in
 
125
                                            revision_id_to_revno[origin])
 
126
            rev = revisions[origin]
 
127
            tz = rev.timezone or 0
 
128
            date_str = time.strftime('%Y%m%d',
 
129
                                     time.gmtime(rev.timestamp + tz))
 
130
            # a lazy way to get something like the email address
 
131
            # TODO: Get real email address
 
132
            author = rev.get_apparent_author()
 
133
            try:
 
134
                author = extract_email_address(author)
 
135
            except errors.NoEmailInUsername:
 
136
                pass        # use the whole name
 
137
        yield (revno_str, author, date_str, origin, text)
 
138
 
 
139
 
 
140
def reannotate(parents_lines, new_lines, new_revision_id,
 
141
               _left_matching_blocks=None):
 
142
    """Create a new annotated version from new lines and parent annotations.
 
143
    
 
144
    :param parents_lines: List of annotated lines for all parents
 
145
    :param new_lines: The un-annotated new lines
 
146
    :param new_revision_id: The revision-id to associate with new lines
 
147
        (will often be CURRENT_REVISION)
 
148
    :param left_matching_blocks: a hint about which areas are common
 
149
        between the text and its left-hand-parent.  The format is
 
150
        the SequenceMatcher.get_matching_blocks format.
 
151
    """
 
152
    if len(parents_lines) == 0:
 
153
        for line in new_lines:
 
154
            yield new_revision_id, line
 
155
    elif len(parents_lines) == 1:
 
156
        for data in _reannotate(parents_lines[0], new_lines, new_revision_id,
 
157
                                _left_matching_blocks):
 
158
            yield data
 
159
    else:
 
160
        block_list = [_left_matching_blocks] + [None] * len(parents_lines)
 
161
        reannotations = [list(_reannotate(p, new_lines, new_revision_id, b))
 
162
                         for p, b in zip(parents_lines, block_list)]
 
163
        for annos in zip(*reannotations):
 
164
            origins = set(a for a, l in annos)
 
165
            line = annos[0][1]
 
166
            if len(origins) == 1:
 
167
                yield iter(origins).next(), line
 
168
            elif len(origins) == 2 and new_revision_id in origins:
 
169
                yield (x for x in origins if x != new_revision_id).next(), line
 
170
            else:
 
171
                yield new_revision_id, line
 
172
 
 
173
 
 
174
def _reannotate(parent_lines, new_lines, new_revision_id,
 
175
                matching_blocks=None):
 
176
    plain_parent_lines = [l for r, l in parent_lines]
 
177
    matcher = patiencediff.PatienceSequenceMatcher(None, plain_parent_lines,
 
178
                                                   new_lines)
 
179
    new_cur = 0
 
180
    if matching_blocks is None:
 
181
        matching_blocks = matcher.get_matching_blocks()
 
182
    for i, j, n in matching_blocks:
 
183
        for line in new_lines[new_cur:j]:
 
184
            yield new_revision_id, line
 
185
        for data in parent_lines[i:i+n]:
 
186
            yield data
 
187
        new_cur = j + n