/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-04-18 08:39:02 UTC
  • mto: (2425.1.2 integration)
  • mto: This revision was merged to the branch mainline in revision 2427.
  • Revision ID: robertc@robertcollins.net-20070418083902-4o66h9fk7zeisvwa
Command objects can now declare related help topics by having _see_also
set to a list of related topic. Updated the HACKING guide entry on
documentation to be more clear about how the help for commands is
generated and to reference this new feature. (Robert Collins)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2004, 2005 by Canonical Ltd
2
 
 
 
1
# Copyright (C) 2004, 2005, 2006, 2007 Canonical Ltd
 
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
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
7
 
 
 
7
#
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
11
# GNU General Public License for more details.
12
 
 
 
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26
26
# e.g. "3:12 Tue", "13 Oct", "Oct 2005", etc.  
27
27
 
28
28
import sys
29
 
import os
30
29
import time
31
30
 
32
 
import bzrlib.weave
 
31
from bzrlib import (
 
32
    errors,
 
33
    patiencediff,
 
34
    tsort,
 
35
    )
33
36
from bzrlib.config import extract_email_address
34
 
from bzrlib.errors import BzrError
35
37
 
36
38
 
37
39
def annotate_file(branch, rev_id, file_id, verbose=False, full=False,
38
 
        to_file=None):
 
40
                  to_file=None, show_ids=False):
39
41
    if to_file is None:
40
42
        to_file = sys.stdout
41
43
 
42
44
    prevanno=''
 
45
    last_rev_id = None
 
46
    if show_ids:
 
47
        w = branch.repository.weave_store.get_weave(file_id,
 
48
            branch.repository.get_transaction())
 
49
        annotations = list(w.annotate_iter(rev_id))
 
50
        max_origin_len = max(len(origin) for origin, text in annotations)
 
51
        for origin, text in annotations:
 
52
            if full or last_rev_id != origin:
 
53
                this = origin
 
54
            else:
 
55
                this = ''
 
56
            to_file.write('%*s | %s' % (max_origin_len, this, text))
 
57
            last_rev_id = origin
 
58
        return
 
59
 
43
60
    annotation = list(_annotate_file(branch, rev_id, file_id))
44
 
    max_origin_len = max(len(origin) for origin in set(x[1] for x in annotation))
45
 
    for (revno_str, author, date_str, line_rev_id, text ) in annotation:
 
61
    if len(annotation) == 0:
 
62
        max_origin_len = max_revno_len = max_revid_len = 0
 
63
    else:
 
64
        max_origin_len = max(len(x[1]) for x in annotation)
 
65
        max_revno_len = max(len(x[0]) for x in annotation)
 
66
        max_revid_len = max(len(x[3]) for x in annotation)
 
67
 
 
68
    if not verbose:
 
69
        max_revno_len = min(max_revno_len, 12)
 
70
    max_revno_len = max(max_revno_len, 3)
 
71
 
 
72
    for (revno_str, author, date_str, line_rev_id, text) in annotation:
46
73
        if verbose:
47
 
            anno = '%5s %-*s %8s ' % (revno_str, max_origin_len, author, date_str)
 
74
            anno = '%-*s %-*s %8s ' % (max_revno_len, revno_str,
 
75
                                       max_origin_len, author, date_str)
48
76
        else:
49
 
            anno = "%5s %-7s " % ( revno_str, author[:7] )
 
77
            if len(revno_str) > max_revno_len:
 
78
                revno_str = revno_str[:max_revno_len-1] + '>'
 
79
            anno = "%-*s %-7s " % (max_revno_len, revno_str, author[:7])
50
80
 
51
81
        if anno.lstrip() == "" and full: anno = prevanno
52
82
        print >>to_file, '%s| %s' % (anno, text)
53
83
        prevanno=anno
54
84
 
55
 
def _annotate_file(branch, rev_id, file_id ):
56
 
 
57
 
    rh = branch.revision_history()
58
 
    w = branch.repository.weave_store.get_weave(file_id, 
 
85
 
 
86
def _annotate_file(branch, rev_id, file_id):
 
87
    """Yield the origins for each line of a file.
 
88
 
 
89
    This includes detailed information, such as the committer name, and
 
90
    date string for the commit, rather than just the revision id.
 
91
    """
 
92
    branch_last_revision = branch.last_revision()
 
93
    revision_graph = branch.repository.get_revision_graph(branch_last_revision)
 
94
    merge_sorted_revisions = tsort.merge_sort(
 
95
        revision_graph,
 
96
        branch_last_revision,
 
97
        None,
 
98
        generate_revno=True)
 
99
    revision_id_to_revno = dict((rev_id, revno)
 
100
                                for seq_num, rev_id, depth, revno, end_of_merge
 
101
                                 in merge_sorted_revisions)
 
102
    w = branch.repository.weave_store.get_weave(file_id,
59
103
        branch.repository.get_transaction())
60
104
    last_origin = None
61
 
    for origin, text in w.annotate_iter(rev_id):
 
105
    annotations = list(w.annotate_iter(rev_id))
 
106
    revision_ids = set(o for o, t in annotations)
 
107
    revision_ids = [o for o in revision_ids if 
 
108
                    branch.repository.has_revision(o)]
 
109
    revisions = dict((r.revision_id, r) for r in 
 
110
                     branch.repository.get_revisions(revision_ids))
 
111
    for origin, text in annotations:
62
112
        text = text.rstrip('\r\n')
63
113
        if origin == last_origin:
64
114
            (revno_str, author, date_str) = ('','','')
65
115
        else:
66
116
            last_origin = origin
67
 
            if not branch.repository.has_revision(origin):
 
117
            if origin not in revisions:
68
118
                (revno_str, author, date_str) = ('?','?','?')
69
119
            else:
70
 
                if origin in rh:
71
 
                    revno_str = str(rh.index(origin) + 1)
72
 
                else:
73
 
                    revno_str = 'merge'
74
 
            rev = branch.repository.get_revision(origin)
 
120
                revno_str = '.'.join(str(i) for i in
 
121
                                            revision_id_to_revno[origin])
 
122
            rev = revisions[origin]
75
123
            tz = rev.timezone or 0
76
 
            date_str = time.strftime('%Y%m%d', 
 
124
            date_str = time.strftime('%Y%m%d',
77
125
                                     time.gmtime(rev.timestamp + tz))
78
126
            # a lazy way to get something like the email address
79
127
            # TODO: Get real email address
80
128
            author = rev.committer
81
129
            try:
82
130
                author = extract_email_address(author)
83
 
            except BzrError:
 
131
            except errors.NoEmailInUsername:
84
132
                pass        # use the whole name
85
133
        yield (revno_str, author, date_str, origin, text)
 
134
 
 
135
 
 
136
def reannotate(parents_lines, new_lines, new_revision_id):
 
137
    """Create a new annotated version from new lines and parent annotations.
 
138
    
 
139
    :param parents_lines: List of annotated lines for all parents
 
140
    :param new_lines: The un-annotated new lines
 
141
    :param new_revision_id: The revision-id to associate with new lines
 
142
        (will often be CURRENT_REVISION)
 
143
    """
 
144
    if len(parents_lines) == 1:
 
145
        for data in _reannotate(parents_lines[0], new_lines, new_revision_id):
 
146
            yield data
 
147
    else:
 
148
        reannotations = [list(_reannotate(p, new_lines, new_revision_id)) for
 
149
                         p in parents_lines]
 
150
        for annos in zip(*reannotations):
 
151
            origins = set(a for a, l in annos)
 
152
            line = annos[0][1]
 
153
            if len(origins) == 1:
 
154
                yield iter(origins).next(), line
 
155
            elif len(origins) == 2 and new_revision_id in origins:
 
156
                yield (x for x in origins if x != new_revision_id).next(), line
 
157
            else:
 
158
                yield new_revision_id, line
 
159
 
 
160
 
 
161
def _reannotate(parent_lines, new_lines, new_revision_id):
 
162
    plain_parent_lines = [l for r, l in parent_lines]
 
163
    matcher = patiencediff.PatienceSequenceMatcher(None, plain_parent_lines,
 
164
                                                   new_lines)
 
165
    new_cur = 0
 
166
    for i, j, n in matcher.get_matching_blocks():
 
167
        for line in new_lines[new_cur:j]:
 
168
            yield new_revision_id, line
 
169
        for data in parent_lines[i:i+n]:
 
170
            yield data
 
171
        new_cur = j + n