~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/annotate.py

Merge bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
35
35
    tsort,
36
36
    )
37
37
from bzrlib.config import extract_email_address
 
38
from bzrlib.repository import _strip_NULL_ghosts
 
39
from bzrlib.revision import CURRENT_REVISION, Revision
38
40
 
39
41
 
40
42
def annotate_file(branch, rev_id, file_id, verbose=False, full=False,
57
59
        to_file = sys.stdout
58
60
 
59
61
    # Handle the show_ids case
60
 
    last_rev_id = None
 
62
    annotations = _annotations(branch.repository, file_id, rev_id)
61
63
    if show_ids:
62
 
        annotations = _annotations(branch.repository, file_id, rev_id)
63
 
        max_origin_len = max(len(origin) for origin, text in annotations)
64
 
        for origin, text in annotations:
65
 
            if full or last_rev_id != origin:
66
 
                this = origin
67
 
            else:
68
 
                this = ''
69
 
            to_file.write('%*s | %s' % (max_origin_len, this, text))
70
 
            last_rev_id = origin
71
 
        return
 
64
        return _show_id_annotations(annotations, to_file, full)
72
65
 
73
66
    # Calculate the lengths of the various columns
74
 
    annotation = list(_annotate_file(branch, rev_id, file_id))
 
67
    annotation = list(_expand_annotations(annotations, branch))
 
68
    _print_annotations(annotation, verbose, to_file, full)
 
69
 
 
70
 
 
71
def annotate_file_tree(tree, file_id, to_file, verbose=False, full=False,
 
72
    show_ids=False):
 
73
    """Annotate file_id in a tree.
 
74
 
 
75
    The tree should already be read_locked() when annotate_file_tree is called.
 
76
 
 
77
    :param tree: The tree to look for revision numbers and history from.
 
78
    :param file_id: The file_id to annotate.
 
79
    :param to_file: The file to output the annotation to.
 
80
    :param verbose: Show all details rather than truncating to ensure
 
81
        reasonable text width.
 
82
    :param full: XXXX Not sure what this does.
 
83
    :param show_ids: Show revision ids in the annotation output.
 
84
    """
 
85
    rev_id = tree.last_revision()
 
86
    branch = tree.branch
 
87
 
 
88
    # Handle the show_ids case
 
89
    annotations = list(tree.annotate_iter(file_id))
 
90
    if show_ids:
 
91
        return _show_id_annotations(annotations, to_file, full)
 
92
 
 
93
    # Create a virtual revision to represent the current tree state.
 
94
    # Should get some more pending commit attributes, like pending tags,
 
95
    # bugfixes etc.
 
96
    current_rev = Revision(CURRENT_REVISION)
 
97
    current_rev.parent_ids = tree.get_parent_ids()
 
98
    current_rev.committer = tree.branch.get_config().username()
 
99
    current_rev.message = "?"
 
100
    current_rev.timestamp = round(time.time(), 3)
 
101
    current_rev.timezone = osutils.local_time_offset()
 
102
    annotation = list(_expand_annotations(annotations, tree.branch,
 
103
        current_rev))
 
104
    _print_annotations(annotation, verbose, to_file, full)
 
105
 
 
106
 
 
107
def _print_annotations(annotation, verbose, to_file, full):
 
108
    """Print annotations to to_file.
 
109
 
 
110
    :param to_file: The file to output the annotation to.
 
111
    :param verbose: Show all details rather than truncating to ensure
 
112
        reasonable text width.
 
113
    :param full: XXXX Not sure what this does.
 
114
    """
75
115
    if len(annotation) == 0:
76
116
        max_origin_len = max_revno_len = max_revid_len = 0
77
117
    else:
110
150
        prevanno = anno
111
151
 
112
152
 
 
153
def _show_id_annotations(annotations, to_file, full):
 
154
    last_rev_id = None
 
155
    max_origin_len = max(len(origin) for origin, text in annotations)
 
156
    for origin, text in annotations:
 
157
        if full or last_rev_id != origin:
 
158
            this = origin
 
159
        else:
 
160
            this = ''
 
161
        to_file.write('%*s | %s' % (max_origin_len, this, text))
 
162
        last_rev_id = origin
 
163
    return
 
164
 
 
165
 
113
166
def _annotations(repo, file_id, rev_id):
114
167
    """Return the list of (origin_revision_id, line_text) for a revision of a file in a repository."""
115
168
    annotations = repo.texts.annotate((file_id, rev_id))
117
170
    return [(key[-1], line) for (key, line) in annotations]
118
171
 
119
172
 
120
 
def _annotate_file(branch, rev_id, file_id):
121
 
    """Yield the origins for each line of a file.
122
 
 
123
 
    This includes detailed information, such as the author name, and
124
 
    date string for the commit, rather than just the revision id.
 
173
def _expand_annotations(annotations, branch, current_rev=None):
 
174
    """Expand a a files annotations into command line UI ready tuples.
 
175
 
 
176
    Each tuple includes detailed information, such as the author name, and date
 
177
    string for the commit, rather than just the revision id.
 
178
 
 
179
    :param annotations: The annotations to expand.
 
180
    :param revision_id_to_revno: A map from id to revision numbers.
 
181
    :param branch: A locked branch to query for revision details.
125
182
    """
126
 
    revision_id_to_revno = branch.get_revision_id_to_revno_map()
127
 
    annotations = _annotations(branch.repository, file_id, rev_id)
 
183
    repository = branch.repository
 
184
    if current_rev is not None:
 
185
        # This can probably become a function on MutableTree, get_revno_map there,
 
186
        # or something.
 
187
        last_revision = current_rev.revision_id
 
188
        # XXX: Partially Cloned from branch, uses the old_get_graph, eep.
 
189
        graph = repository.get_graph()
 
190
        revision_graph = dict(((key, value) for key, value in
 
191
            graph.iter_ancestry(current_rev.parent_ids) if value is not None))
 
192
        revision_graph = _strip_NULL_ghosts(revision_graph)
 
193
        revision_graph[last_revision] = current_rev.parent_ids
 
194
        merge_sorted_revisions = tsort.merge_sort(
 
195
            revision_graph,
 
196
            last_revision,
 
197
            None,
 
198
            generate_revno=True)
 
199
        revision_id_to_revno = dict((rev_id, revno)
 
200
            for seq_num, rev_id, depth, revno, end_of_merge in
 
201
                merge_sorted_revisions)
 
202
    else:
 
203
        revision_id_to_revno = branch.get_revision_id_to_revno_map()
128
204
    last_origin = None
129
205
    revision_ids = set(o for o, t in annotations)
 
206
    revisions = {}
 
207
    if CURRENT_REVISION in revision_ids:
 
208
        revision_id_to_revno[CURRENT_REVISION] = (
 
209
            "%d?" % (branch.revno() + 1),)
 
210
        revisions[CURRENT_REVISION] = current_rev
130
211
    revision_ids = [o for o in revision_ids if 
131
 
                    branch.repository.has_revision(o)]
132
 
    revisions = dict((r.revision_id, r) for r in 
133
 
                     branch.repository.get_revisions(revision_ids))
 
212
                    repository.has_revision(o)]
 
213
    revisions.update((r.revision_id, r) for r in 
 
214
                     repository.get_revisions(revision_ids))
134
215
    for origin, text in annotations:
135
216
        text = text.rstrip('\r\n')
136
217
        if origin == last_origin: