/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: John Arbash Meinel
  • Date: 2007-05-31 20:29:04 UTC
  • mto: This revision was merged to the branch mainline in revision 2499.
  • Revision ID: john@arbash-meinel.com-20070531202904-34h7ygudo7qq9ha1
Update the code so that symlinks aren't cached at incorrect times
and fix the tests so that they don't assume files and symlinks
get cached even when the timestamp doesn't declare them 'safe'.

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
    patiencediff,
 
34
    tsort,
 
35
    )
 
36
from bzrlib.config import extract_email_address
 
37
 
 
38
 
 
39
def annotate_file(branch, rev_id, file_id, verbose=False, full=False,
 
40
                  to_file=None, show_ids=False):
 
41
    if to_file is None:
 
42
        to_file = sys.stdout
 
43
 
 
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
 
 
60
    annotation = list(_annotate_file(branch, rev_id, file_id))
 
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:
 
73
        if verbose:
 
74
            anno = '%-*s %-*s %8s ' % (max_revno_len, revno_str,
 
75
                                       max_origin_len, author, date_str)
 
76
        else:
 
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])
 
80
 
 
81
        if anno.lstrip() == "" and full: anno = prevanno
 
82
        print >>to_file, '%s| %s' % (anno, text)
 
83
        prevanno=anno
 
84
 
 
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
    revision_id_to_revno = branch.get_revision_id_to_revno_map()
 
93
    w = branch.repository.weave_store.get_weave(file_id,
 
94
        branch.repository.get_transaction())
 
95
    last_origin = None
 
96
    annotations = list(w.annotate_iter(rev_id))
 
97
    revision_ids = set(o for o, t in annotations)
 
98
    revision_ids = [o for o in revision_ids if 
 
99
                    branch.repository.has_revision(o)]
 
100
    revisions = dict((r.revision_id, r) for r in 
 
101
                     branch.repository.get_revisions(revision_ids))
 
102
    for origin, text in annotations:
 
103
        text = text.rstrip('\r\n')
 
104
        if origin == last_origin:
 
105
            (revno_str, author, date_str) = ('','','')
 
106
        else:
 
107
            last_origin = origin
 
108
            if origin not in revisions:
 
109
                (revno_str, author, date_str) = ('?','?','?')
 
110
            else:
 
111
                revno_str = '.'.join(str(i) for i in
 
112
                                            revision_id_to_revno[origin])
 
113
            rev = revisions[origin]
 
114
            tz = rev.timezone or 0
 
115
            date_str = time.strftime('%Y%m%d',
 
116
                                     time.gmtime(rev.timestamp + tz))
 
117
            # a lazy way to get something like the email address
 
118
            # TODO: Get real email address
 
119
            author = rev.committer
 
120
            try:
 
121
                author = extract_email_address(author)
 
122
            except errors.NoEmailInUsername:
 
123
                pass        # use the whole name
 
124
        yield (revno_str, author, date_str, origin, text)
 
125
 
 
126
 
 
127
def reannotate(parents_lines, new_lines, new_revision_id):
 
128
    """Create a new annotated version from new lines and parent annotations.
 
129
    
 
130
    :param parents_lines: List of annotated lines for all parents
 
131
    :param new_lines: The un-annotated new lines
 
132
    :param new_revision_id: The revision-id to associate with new lines
 
133
        (will often be CURRENT_REVISION)
 
134
    """
 
135
    if len(parents_lines) == 1:
 
136
        for data in _reannotate(parents_lines[0], new_lines, new_revision_id):
 
137
            yield data
 
138
    else:
 
139
        reannotations = [list(_reannotate(p, new_lines, new_revision_id)) for
 
140
                         p in parents_lines]
 
141
        for annos in zip(*reannotations):
 
142
            origins = set(a for a, l in annos)
 
143
            line = annos[0][1]
 
144
            if len(origins) == 1:
 
145
                yield iter(origins).next(), line
 
146
            elif len(origins) == 2 and new_revision_id in origins:
 
147
                yield (x for x in origins if x != new_revision_id).next(), line
 
148
            else:
 
149
                yield new_revision_id, line
 
150
 
 
151
 
 
152
def _reannotate(parent_lines, new_lines, new_revision_id):
 
153
    plain_parent_lines = [l for r, l in parent_lines]
 
154
    matcher = patiencediff.PatienceSequenceMatcher(None, plain_parent_lines,
 
155
                                                   new_lines)
 
156
    new_cur = 0
 
157
    for i, j, n in matcher.get_matching_blocks():
 
158
        for line in new_lines[new_cur:j]:
 
159
            yield new_revision_id, line
 
160
        for data in parent_lines[i:i+n]:
 
161
            yield data
 
162
        new_cur = j + n