~bzr-pqm/bzr/bzr.dev

2245.3.2 by John Arbash Meinel
Cleanup according to Wouter's suggestions.
1
# Copyright (C) 2004, 2005, 2006, 2007 Canonical Ltd
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
2
#
1385 by Martin Pool
- simple weave-based annotate code (not complete)
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.
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
7
#
1385 by Martin Pool
- simple weave-based annotate code (not complete)
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.
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
12
#
1385 by Martin Pool
- simple weave-based annotate code (not complete)
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
1185.16.8 by Martin Pool
doc
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
1185.16.57 by Martin Pool
[merge] from aaron
25
# TODO: perhaps abbreviate timescales depending on how recent they are
26
# e.g. "3:12 Tue", "13 Oct", "Oct 2005", etc.  
27
1385 by Martin Pool
- simple weave-based annotate code (not complete)
28
import sys
1185.16.1 by Martin Pool
- update annotate for new branch api
29
import time
1385 by Martin Pool
- simple weave-based annotate code (not complete)
30
2182.3.1 by John Arbash Meinel
Annotate now shows dotted revnos instead of plain revnos.
31
from bzrlib import (
32
    errors,
1551.9.19 by Aaron Bentley
Merge from bzr.dev
33
    patiencediff,
2182.3.1 by John Arbash Meinel
Annotate now shows dotted revnos instead of plain revnos.
34
    tsort,
35
    )
1185.16.53 by Martin Pool
- annotate improvements from Goffreddo, with extra bug fixes and tests
36
from bzrlib.config import extract_email_address
37
38
39
def annotate_file(branch, rev_id, file_id, verbose=False, full=False,
2182.3.1 by John Arbash Meinel
Annotate now shows dotted revnos instead of plain revnos.
40
                  to_file=None, show_ids=False):
1385 by Martin Pool
- simple weave-based annotate code (not complete)
41
    if to_file is None:
42
        to_file = sys.stdout
1185.16.53 by Martin Pool
- annotate improvements from Goffreddo, with extra bug fixes and tests
43
44
    prevanno=''
2182.3.9 by John Arbash Meinel
Faster annotate --show-ids. No need to pull the history or the revision info
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
1185.33.39 by Martin Pool
[patch] annotate --long (robey pointer)
60
    annotation = list(_annotate_file(branch, rev_id, file_id))
2027.3.1 by John Arbash Meinel
'bzr annotate' shouldn't fail on an empty file: fix bug #56814
61
    if len(annotation) == 0:
2182.3.4 by John Arbash Meinel
add show-ids and test that nearby areas are collapsed without full
62
        max_origin_len = max_revno_len = max_revid_len = 0
2027.3.1 by John Arbash Meinel
'bzr annotate' shouldn't fail on an empty file: fix bug #56814
63
    else:
2182.3.8 by John Arbash Meinel
Some cleanup to make annotate.py < 79 chars wide.
64
        max_origin_len = max(len(x[1]) for x in annotation)
2182.3.1 by John Arbash Meinel
Annotate now shows dotted revnos instead of plain revnos.
65
        max_revno_len = max(len(x[0]) for x in annotation)
2182.3.4 by John Arbash Meinel
add show-ids and test that nearby areas are collapsed without full
66
        max_revid_len = max(len(x[3]) for x in annotation)
2182.3.1 by John Arbash Meinel
Annotate now shows dotted revnos instead of plain revnos.
67
2182.3.2 by John Arbash Meinel
Use shortened revnos unless --long is supplied
68
    if not verbose:
2182.3.7 by John Arbash Meinel
Cleanup and add blackbox tests for annotate.
69
        max_revno_len = min(max_revno_len, 12)
70
    max_revno_len = max(max_revno_len, 3)
2182.3.2 by John Arbash Meinel
Use shortened revnos unless --long is supplied
71
2182.3.4 by John Arbash Meinel
add show-ids and test that nearby areas are collapsed without full
72
    for (revno_str, author, date_str, line_rev_id, text) in annotation:
2182.3.9 by John Arbash Meinel
Faster annotate --show-ids. No need to pull the history or the revision info
73
        if verbose:
74
            anno = '%-*s %-*s %8s ' % (max_revno_len, revno_str,
75
                                       max_origin_len, author, date_str)
1185.16.53 by Martin Pool
- annotate improvements from Goffreddo, with extra bug fixes and tests
76
        else:
2182.3.9 by John Arbash Meinel
Faster annotate --show-ids. No need to pull the history or the revision info
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])
1185.16.53 by Martin Pool
- annotate improvements from Goffreddo, with extra bug fixes and tests
80
81
        if anno.lstrip() == "" and full: anno = prevanno
82
        print >>to_file, '%s| %s' % (anno, text)
83
        prevanno=anno
84
2182.3.10 by John Arbash Meinel
minor cleanup.
85
2245.3.1 by John Arbash Meinel
bzr annotate should use Branch's dotted revnos.
86
def _annotate_file(branch, rev_id, file_id):
2182.3.10 by John Arbash Meinel
minor cleanup.
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
    """
2245.3.1 by John Arbash Meinel
bzr annotate should use Branch's dotted revnos.
92
    branch_last_revision = branch.last_revision()
93
    revision_graph = branch.repository.get_revision_graph(branch_last_revision)
2182.3.1 by John Arbash Meinel
Annotate now shows dotted revnos instead of plain revnos.
94
    merge_sorted_revisions = tsort.merge_sort(
95
        revision_graph,
2245.3.1 by John Arbash Meinel
bzr annotate should use Branch's dotted revnos.
96
        branch_last_revision,
2182.3.1 by John Arbash Meinel
Annotate now shows dotted revnos instead of plain revnos.
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,
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
103
        branch.repository.get_transaction())
1385 by Martin Pool
- simple weave-based annotate code (not complete)
104
    last_origin = None
1551.9.6 by Aaron Bentley
Optimize annotate by retrieving all revisions at once
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:
1385 by Martin Pool
- simple weave-based annotate code (not complete)
112
        text = text.rstrip('\r\n')
113
        if origin == last_origin:
1185.16.53 by Martin Pool
- annotate improvements from Goffreddo, with extra bug fixes and tests
114
            (revno_str, author, date_str) = ('','','')
1385 by Martin Pool
- simple weave-based annotate code (not complete)
115
        else:
116
            last_origin = origin
1551.9.6 by Aaron Bentley
Optimize annotate by retrieving all revisions at once
117
            if origin not in revisions:
1185.16.53 by Martin Pool
- annotate improvements from Goffreddo, with extra bug fixes and tests
118
                (revno_str, author, date_str) = ('?','?','?')
1185.16.1 by Martin Pool
- update annotate for new branch api
119
            else:
2182.3.1 by John Arbash Meinel
Annotate now shows dotted revnos instead of plain revnos.
120
                revno_str = '.'.join(str(i) for i in
121
                                            revision_id_to_revno[origin])
1551.9.6 by Aaron Bentley
Optimize annotate by retrieving all revisions at once
122
            rev = revisions[origin]
1185.16.32 by Martin Pool
- add a basic annotate built-in command
123
            tz = rev.timezone or 0
2182.3.8 by John Arbash Meinel
Some cleanup to make annotate.py < 79 chars wide.
124
            date_str = time.strftime('%Y%m%d',
1185.16.32 by Martin Pool
- add a basic annotate built-in command
125
                                     time.gmtime(rev.timestamp + tz))
126
            # a lazy way to get something like the email address
127
            # TODO: Get real email address
1185.16.53 by Martin Pool
- annotate improvements from Goffreddo, with extra bug fixes and tests
128
            author = rev.committer
129
            try:
130
                author = extract_email_address(author)
2182.3.1 by John Arbash Meinel
Annotate now shows dotted revnos instead of plain revnos.
131
            except errors.NoEmailInUsername:
1185.16.53 by Martin Pool
- annotate improvements from Goffreddo, with extra bug fixes and tests
132
                pass        # use the whole name
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
133
        yield (revno_str, author, date_str, origin, text)
1551.9.16 by Aaron Bentley
Implement Tree.annotate_iter for RevisionTree and WorkingTree
134
135
1551.9.17 by Aaron Bentley
Annotate for working trees across all parents
136
def reannotate(parents_lines, new_lines, new_revision_id):
1551.9.16 by Aaron Bentley
Implement Tree.annotate_iter for RevisionTree and WorkingTree
137
    """Create a new annotated version from new lines and parent annotations.
138
    
1551.9.17 by Aaron Bentley
Annotate for working trees across all parents
139
    :param parents_lines: List of annotated lines for all parents
1551.9.16 by Aaron Bentley
Implement Tree.annotate_iter for RevisionTree and WorkingTree
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
    """
1551.9.17 by Aaron Bentley
Annotate for working trees across all parents
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
1551.9.18 by Aaron Bentley
Updates from review comments
159
1551.9.17 by Aaron Bentley
Annotate for working trees across all parents
160
161
def _reannotate(parent_lines, new_lines, new_revision_id):
1551.9.16 by Aaron Bentley
Implement Tree.annotate_iter for RevisionTree and WorkingTree
162
    plain_parent_lines = [l for r, l in parent_lines]
1551.9.18 by Aaron Bentley
Updates from review comments
163
    matcher = patiencediff.PatienceSequenceMatcher(None, plain_parent_lines,
1551.9.16 by Aaron Bentley
Implement Tree.annotate_iter for RevisionTree and WorkingTree
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]:
1551.9.18 by Aaron Bentley
Updates from review comments
170
            yield data
1551.9.16 by Aaron Bentley
Implement Tree.annotate_iter for RevisionTree and WorkingTree
171
        new_cur = j + n