/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: Robert Collins
  • Date: 2007-07-15 15:40:37 UTC
  • mto: (2592.3.33 repository)
  • mto: This revision was merged to the branch mainline in revision 2624.
  • Revision ID: robertc@robertcollins.net-20070715154037-3ar8g89decddc9su
Make GraphIndex accept nodes as key, value, references, so that the method
signature is closer to what a simple key->value index delivers. Also
change the behaviour when the reference list count is zero to accept
key, value as nodes, and emit key, value to make it identical in that case
to a simple key->value index. This may not be a good idea, but for now it
seems ok.

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 committer 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.committer
 
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
    """Create a new annotated version from new lines and parent annotations.
 
142
    
 
143
    :param parents_lines: List of annotated lines for all parents
 
144
    :param new_lines: The un-annotated new lines
 
145
    :param new_revision_id: The revision-id to associate with new lines
 
146
        (will often be CURRENT_REVISION)
 
147
    """
 
148
    if len(parents_lines) == 1:
 
149
        for data in _reannotate(parents_lines[0], new_lines, new_revision_id):
 
150
            yield data
 
151
    else:
 
152
        reannotations = [list(_reannotate(p, new_lines, new_revision_id)) for
 
153
                         p in parents_lines]
 
154
        for annos in zip(*reannotations):
 
155
            origins = set(a for a, l in annos)
 
156
            line = annos[0][1]
 
157
            if len(origins) == 1:
 
158
                yield iter(origins).next(), line
 
159
            elif len(origins) == 2 and new_revision_id in origins:
 
160
                yield (x for x in origins if x != new_revision_id).next(), line
 
161
            else:
 
162
                yield new_revision_id, line
 
163
 
 
164
 
 
165
def _reannotate(parent_lines, new_lines, new_revision_id):
 
166
    plain_parent_lines = [l for r, l in parent_lines]
 
167
    matcher = patiencediff.PatienceSequenceMatcher(None, plain_parent_lines,
 
168
                                                   new_lines)
 
169
    new_cur = 0
 
170
    for i, j, n in matcher.get_matching_blocks():
 
171
        for line in new_lines[new_cur:j]:
 
172
            yield new_revision_id, line
 
173
        for data in parent_lines[i:i+n]:
 
174
            yield data
 
175
        new_cur = j + n