/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
2359.1.1 by Kent Gibson
Fix ``bzr log <file>`` so it only logs the revisions that changed the file, and does it faster.
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
2
#
369 by Martin Pool
- Split out log printing into new show_log function
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.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
7
#
369 by Martin Pool
- Split out log printing into new show_log function
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.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
12
#
369 by Martin Pool
- Split out log printing into new show_log function
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
375 by Martin Pool
- New command touching-revisions and function to trace
17
18
527 by Martin Pool
- refactor log command
19
"""Code to show logs of changes.
20
21
Various flavors of log can be produced:
22
23
* for one file, or the whole tree, and (not done yet) for
24
  files in a given directory
25
26
* in "verbose" mode with a description of what changed from one
27
  version to the next
28
29
* with file-ids and revision-ids shown
30
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
31
Logs are actually written out through an abstract LogFormatter
32
interface, which allows for different preferred formats.  Plugins can
33
register formats too.
34
35
Logs can be produced in either forward (oldest->newest) or reverse
36
(newest->oldest) order.
37
38
Logs can be filtered to show only revisions matching a particular
39
search string, or within a particular range of revisions.  The range
40
can be given as date/times, which are reduced to revisions before
41
calling in here.
42
43
In verbose mode we show a summary of what changed in each particular
44
revision.  Note that this is the delta for changes in that revision
2466.12.2 by Kent Gibson
shift log output with only merge revisions to the left margin
45
relative to its left-most parent, not the delta relative to the last
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
46
logged revision.  So for example if you ask for a verbose log of
47
changes touching hello.c you will get a list of those revisions also
48
listing other things that were changed in the same revision, but not
49
all the changes since the previous revision that touched hello.c.
527 by Martin Pool
- refactor log command
50
"""
51
2997.1.2 by Kent Gibson
Move all imports to top of log.py
52
import codecs
3943.5.1 by Ian Clatworthy
first cut at log --show-diff
53
from cStringIO import StringIO
2997.1.2 by Kent Gibson
Move all imports to top of log.py
54
from itertools import (
55
    izip,
56
    )
1624.1.3 by Robert Collins
Convert log to use the new tsort.merge_sort routine.
57
import re
2997.1.2 by Kent Gibson
Move all imports to top of log.py
58
import sys
59
from warnings import (
60
    warn,
61
    )
1185.33.41 by Martin Pool
Fix regression of 'bzr log -v' - it wasn't showing changed files at all. (#4676)
62
3224.5.1 by Andrew Bennetts
Lots of assorted hackery to reduce the number of imports for common operations. Improves 'rocks', 'st' and 'help' times by ~50ms on my laptop.
63
from bzrlib.lazy_import import lazy_import
64
lazy_import(globals(), """
3535.5.1 by John Arbash Meinel
cleanup a few imports to be lazily loaded.
65
3224.5.1 by Andrew Bennetts
Lots of assorted hackery to reduce the number of imports for common operations. Improves 'rocks', 'st' and 'help' times by ~50ms on my laptop.
66
from bzrlib import (
3063.3.2 by Lukáš Lalinský
Move the name and e-mail address extraction logic to config.parse_username.
67
    config,
3943.5.1 by Ian Clatworthy
first cut at log --show-diff
68
    diff,
3224.5.1 by Andrew Bennetts
Lots of assorted hackery to reduce the number of imports for common operations. Improves 'rocks', 'st' and 'help' times by ~50ms on my laptop.
69
    errors,
3535.5.1 by John Arbash Meinel
cleanup a few imports to be lazily loaded.
70
    repository as _mod_repository,
71
    revision as _mod_revision,
3224.5.1 by Andrew Bennetts
Lots of assorted hackery to reduce the number of imports for common operations. Improves 'rocks', 'st' and 'help' times by ~50ms on my laptop.
72
    revisionspec,
3535.5.1 by John Arbash Meinel
cleanup a few imports to be lazily loaded.
73
    trace,
3224.5.1 by Andrew Bennetts
Lots of assorted hackery to reduce the number of imports for common operations. Improves 'rocks', 'st' and 'help' times by ~50ms on my laptop.
74
    tsort,
75
    )
76
""")
77
3535.5.1 by John Arbash Meinel
cleanup a few imports to be lazily loaded.
78
from bzrlib import (
2221.4.10 by Aaron Bentley
Implement log options using RegistryOption
79
    registry,
2997.1.2 by Kent Gibson
Move all imports to top of log.py
80
    )
81
from bzrlib.osutils import (
82
    format_date,
2997.1.3 by Alexander Belchenko
file wrapper around stdout should use terminal encoding, not user_encoding.
83
    get_terminal_encoding,
2997.1.2 by Kent Gibson
Move all imports to top of log.py
84
    terminal_width,
85
    )
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
86
375 by Martin Pool
- New command touching-revisions and function to trace
87
88
def find_touching_revisions(branch, file_id):
89
    """Yield a description of revisions which affect the file_id.
90
91
    Each returned element is (revno, revision_id, description)
92
93
    This is the list of revisions where the file is either added,
94
    modified, renamed or deleted.
95
96
    TODO: Perhaps some way to limit this to only particular revisions,
522 by Martin Pool
todo
97
    or to traverse a non-mainline set of revisions?
375 by Martin Pool
- New command touching-revisions and function to trace
98
    """
99
    last_ie = None
100
    last_path = None
101
    revno = 1
102
    for revision_id in branch.revision_history():
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
103
        this_inv = branch.repository.get_revision_inventory(revision_id)
375 by Martin Pool
- New command touching-revisions and function to trace
104
        if file_id in this_inv:
105
            this_ie = this_inv[file_id]
106
            this_path = this_inv.id2path(file_id)
107
        else:
108
            this_ie = this_path = None
109
110
        # now we know how it was last time, and how it is in this revision.
111
        # are those two states effectively the same or not?
112
113
        if not this_ie and not last_ie:
114
            # not present in either
115
            pass
116
        elif this_ie and not last_ie:
117
            yield revno, revision_id, "added " + this_path
118
        elif not this_ie and last_ie:
119
            # deleted here
120
            yield revno, revision_id, "deleted " + last_path
121
        elif this_path != last_path:
122
            yield revno, revision_id, ("renamed %s => %s" % (last_path, this_path))
123
        elif (this_ie.text_size != last_ie.text_size
124
              or this_ie.text_sha1 != last_ie.text_sha1):
125
            yield revno, revision_id, "modified " + this_path
126
127
        last_ie = this_ie
128
        last_path = this_path
129
        revno += 1
130
131
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
132
def _enumerate_history(branch):
133
    rh = []
134
    revno = 1
135
    for rev_id in branch.revision_history():
136
        rh.append((revno, rev_id))
137
        revno += 1
138
    return rh
139
140
378 by Martin Pool
- New usage bzr log FILENAME
141
def show_log(branch,
794 by Martin Pool
- Merge John's nice short-log format.
142
             lf,
527 by Martin Pool
- refactor log command
143
             specific_fileid=None,
378 by Martin Pool
- New usage bzr log FILENAME
144
             verbose=False,
567 by Martin Pool
- New form 'bzr log -r FROM:TO'
145
             direction='reverse',
146
             start_revision=None,
900 by Martin Pool
- patch from john to search for matching commits
147
             end_revision=None,
2466.9.1 by Kent Gibson
add bzr log --limit
148
             search=None,
3943.5.1 by Ian Clatworthy
first cut at log --show-diff
149
             limit=None,
150
             show_diff=False):
369 by Martin Pool
- Split out log printing into new show_log function
151
    """Write out human-readable log of commits to this branch.
152
3874.2.2 by Vincent Ladeuil
Cleanup show_log doc string.
153
    :param lf: The LogFormatter object showing the output.
154
155
    :param specific_fileid: If not None, list only the commits affecting the
156
        specified file, rather than all commits.
157
158
    :param verbose: If True show added/changed/deleted/renamed files.
159
160
    :param direction: 'reverse' (default) is latest to earliest; 'forward' is
161
        earliest to latest.
162
163
    :param start_revision: If not None, only show revisions >= start_revision
164
165
    :param end_revision: If not None, only show revisions <= end_revision
166
167
    :param search: If not None, only show revisions with matching commit
168
        messages
169
170
    :param limit: If set, shows only 'limit' revisions, all revisions are shown
171
        if None or 0.
3943.5.1 by Ian Clatworthy
first cut at log --show-diff
172
173
    :param show_diff: If True, output a diff after each revision.
369 by Martin Pool
- Split out log printing into new show_log function
174
    """
1417.1.7 by Robert Collins
teach log it needs a read lock
175
    branch.lock_read()
176
    try:
2466.8.2 by Kent Gibson
Move begin/end calls from _show_log to show_log. Fix long line.
177
        if getattr(lf, 'begin_log', None):
178
            lf.begin_log()
179
1756.1.6 by Aaron Bentley
Revert locking fix
180
        _show_log(branch, lf, specific_fileid, verbose, direction,
3943.5.1 by Ian Clatworthy
first cut at log --show-diff
181
                  start_revision, end_revision, search, limit, show_diff)
2466.8.2 by Kent Gibson
Move begin/end calls from _show_log to show_log. Fix long line.
182
183
        if getattr(lf, 'end_log', None):
184
            lf.end_log()
1417.1.7 by Robert Collins
teach log it needs a read lock
185
    finally:
186
        branch.unlock()
2490.1.2 by John Arbash Meinel
Cleanup according to PEP8 and some other small whitespace fixes
187
3302.1.1 by Aaron Bentley
Split out _iter_revision, allow view_revisions to be an iterator
188
1417.1.7 by Robert Collins
teach log it needs a read lock
189
def _show_log(branch,
190
             lf,
191
             specific_fileid=None,
192
             verbose=False,
193
             direction='reverse',
194
             start_revision=None,
195
             end_revision=None,
2466.9.1 by Kent Gibson
add bzr log --limit
196
             search=None,
3943.5.1 by Ian Clatworthy
first cut at log --show-diff
197
             limit=None,
198
             show_diff=False):
1417.1.7 by Robert Collins
teach log it needs a read lock
199
    """Worker function for show_log - see show_log."""
794 by Martin Pool
- Merge John's nice short-log format.
200
    if not isinstance(lf, LogFormatter):
201
        warn("not a LogFormatter instance: %r" % lf)
533 by Martin Pool
- fix up asking for the log for the root of a remote branch
202
203
    if specific_fileid:
3535.5.1 by John Arbash Meinel
cleanup a few imports to be lazily loaded.
204
        trace.mutter('get log for file_id %r', specific_fileid)
3302.1.2 by Aaron Bentley
Split out the major view_revision calculation logic
205
    generate_merge_revisions = getattr(lf, 'supports_merge_revisions', False)
206
    allow_single_merge_revision = getattr(lf,
207
        'supports_single_merge_revision', False)
208
    view_revisions = calculate_view_revisions(branch, start_revision,
209
                                              end_revision, direction,
210
                                              specific_fileid,
211
                                              generate_merge_revisions,
212
                                              allow_single_merge_revision)
213
    rev_tag_dict = {}
214
    generate_tags = getattr(lf, 'supports_tags', False)
215
    if generate_tags:
216
        if branch.supports_tags():
217
            rev_tag_dict = branch.tags.get_reverse_tag_dict()
218
219
    generate_delta = verbose and getattr(lf, 'supports_delta', False)
3943.5.2 by Ian Clatworthy
hand control of diff formatting to the log formatter
220
    generate_diff = show_diff and getattr(lf, 'supports_diff', False)
3302.1.2 by Aaron Bentley
Split out the major view_revision calculation logic
221
222
    # now we just print all the revisions
3943.5.1 by Ian Clatworthy
first cut at log --show-diff
223
    repo = branch.repository
3302.1.2 by Aaron Bentley
Split out the major view_revision calculation logic
224
    log_count = 0
3642.1.5 by Robert Collins
Separate out batching of revisions.
225
    revision_iterator = make_log_rev_iterator(branch, view_revisions,
226
        generate_delta, search)
3642.1.2 by Robert Collins
Setup a log iterator that more closely matches what the code tries to do with repository operations.
227
    for revs in revision_iterator:
228
        for (rev_id, revno, merge_depth), rev, delta in revs:
3943.5.2 by Ian Clatworthy
hand control of diff formatting to the log formatter
229
            if generate_diff:
3943.5.4 by Ian Clatworthy
filter diff by file
230
                diff = _format_diff(repo, rev, rev_id, specific_fileid)
3943.5.2 by Ian Clatworthy
hand control of diff formatting to the log formatter
231
            else:
232
                diff = None
3642.1.2 by Robert Collins
Setup a log iterator that more closely matches what the code tries to do with repository operations.
233
            lr = LogRevision(rev, revno, merge_depth, delta,
3943.5.2 by Ian Clatworthy
hand control of diff formatting to the log formatter
234
                             rev_tag_dict.get(rev_id), diff)
3642.1.2 by Robert Collins
Setup a log iterator that more closely matches what the code tries to do with repository operations.
235
            lf.log_revision(lr)
236
            if limit:
237
                log_count += 1
238
                if log_count >= limit:
3660.1.1 by Robert Collins
Fix log --limit (broken by log filtering patch).
239
                    return
3302.1.2 by Aaron Bentley
Split out the major view_revision calculation logic
240
241
3943.5.4 by Ian Clatworthy
filter diff by file
242
def _format_diff(repo, rev, rev_id, specific_fileid):
3943.5.2 by Ian Clatworthy
hand control of diff formatting to the log formatter
243
    if len(rev.parent_ids) == 0:
244
        ancestor_id = _mod_revision.NULL_REVISION
245
    else:
246
        ancestor_id = rev.parent_ids[0]
247
    tree_1 = repo.revision_tree(ancestor_id)
248
    tree_2 = repo.revision_tree(rev_id)
3943.5.4 by Ian Clatworthy
filter diff by file
249
    if specific_fileid:
250
        specific_files = [tree_2.id2path(specific_fileid)]
251
    else:
252
        specific_files = None
3943.5.2 by Ian Clatworthy
hand control of diff formatting to the log formatter
253
    s = StringIO()
3943.5.4 by Ian Clatworthy
filter diff by file
254
    diff.show_diff_trees(tree_1, tree_2, s, specific_files, old_label='',
255
        new_label='')
3943.5.2 by Ian Clatworthy
hand control of diff formatting to the log formatter
256
    return s.getvalue()
257
258
3302.1.2 by Aaron Bentley
Split out the major view_revision calculation logic
259
def calculate_view_revisions(branch, start_revision, end_revision, direction,
260
                             specific_fileid, generate_merge_revisions,
261
                             allow_single_merge_revision):
3943.4.5 by John Arbash Meinel
Restore _linear_view_revisions.
262
    if (    not generate_merge_revisions
263
        and start_revision is end_revision is None
264
        and direction == 'reverse'
265
        and specific_fileid is None):
266
        return _linear_view_revisions(branch)
267
3842.2.5 by Vincent Ladeuil
Better fix for bug #300055.
268
    mainline_revs, rev_nos, start_rev_id, end_rev_id = _get_mainline_revs(
269
        branch, start_revision, end_revision)
2466.12.1 by Kent Gibson
Fix ``bzr log -r`` to support selecting merge revisions.
270
    if not mainline_revs:
3302.1.2 by Aaron Bentley
Split out the major view_revision calculation logic
271
        return []
1756.2.18 by Aaron Bentley
Factor out the revision list generation
272
2997.1.1 by Kent Gibson
Support logging single merge revisions with short and line log formatters.
273
    generate_single_revision = False
2985.1.1 by Aaron Bentley
Better behavior when user requests a log that cannot be viewed (Kent Gibson)
274
    if ((not generate_merge_revisions)
2978.3.2 by Kent Gibson
Use in rather than has_key
275
        and ((start_rev_id and (start_rev_id not in rev_nos))
276
            or (end_rev_id and (end_rev_id not in rev_nos)))):
2997.1.1 by Kent Gibson
Support logging single merge revisions with short and line log formatters.
277
        generate_single_revision = ((start_rev_id == end_rev_id)
3302.1.2 by Aaron Bentley
Split out the major view_revision calculation logic
278
            and allow_single_merge_revision)
2997.1.1 by Kent Gibson
Support logging single merge revisions with short and line log formatters.
279
        if not generate_single_revision:
3535.5.1 by John Arbash Meinel
cleanup a few imports to be lazily loaded.
280
            raise errors.BzrCommandError('Selected log formatter only supports'
281
                ' mainline revisions.')
2997.1.1 by Kent Gibson
Support logging single merge revisions with short and line log formatters.
282
        generate_merge_revisions = generate_single_revision
3940.1.3 by Ian Clatworthy
fix code
283
    include_merges = generate_merge_revisions or specific_fileid
2359.1.1 by Kent Gibson
Fix ``bzr log <file>`` so it only logs the revisions that changed the file, and does it faster.
284
    view_revs_iter = get_view_revisions(mainline_revs, rev_nos, branch,
3940.1.3 by Ian Clatworthy
fix code
285
                          direction, include_merges=include_merges)
3842.2.5 by Vincent Ladeuil
Better fix for bug #300055.
286
287
    if direction == 'reverse':
288
        start_rev_id, end_rev_id = end_rev_id, start_rev_id
2466.12.1 by Kent Gibson
Fix ``bzr log -r`` to support selecting merge revisions.
289
    view_revisions = _filter_revision_range(list(view_revs_iter),
290
                                            start_rev_id,
291
                                            end_rev_id)
2997.1.1 by Kent Gibson
Support logging single merge revisions with short and line log formatters.
292
    if view_revisions and generate_single_revision:
293
        view_revisions = view_revisions[0:1]
2359.1.1 by Kent Gibson
Fix ``bzr log <file>`` so it only logs the revisions that changed the file, and does it faster.
294
    if specific_fileid:
2466.12.1 by Kent Gibson
Fix ``bzr log -r`` to support selecting merge revisions.
295
        view_revisions = _filter_revisions_touching_file_id(branch,
3940.1.5 by Ian Clatworthy
feedback from vila
296
            specific_fileid, view_revisions,
297
            include_merges=generate_merge_revisions)
2388.1.11 by Alexander Belchenko
changes after John's review
298
2466.12.2 by Kent Gibson
shift log output with only merge revisions to the left margin
299
    # rebase merge_depth - unless there are no revisions or 
300
    # either the first or last revision have merge_depth = 0.
301
    if view_revisions and view_revisions[0][2] and view_revisions[-1][2]:
2466.12.3 by Kent Gibson
Fix JAM's review comments for left align patch
302
        min_depth = min([d for r,n,d in view_revisions])
303
        if min_depth != 0:
2466.12.2 by Kent Gibson
shift log output with only merge revisions to the left margin
304
            view_revisions = [(r,n,d-min_depth) for r,n,d in view_revisions]
3302.1.2 by Aaron Bentley
Split out the major view_revision calculation logic
305
    return view_revisions
2466.8.1 by Kent Gibson
Reworked LogFormatter API to simplify extending the attributes of the revision being logged. Added support for begin_log() and end_log() hooks in LogFormatters.
306
530 by Martin Pool
- put back verbose log support for reversed logs
307
3943.4.5 by John Arbash Meinel
Restore _linear_view_revisions.
308
def _linear_view_revisions(branch):
309
    start_revno, start_revision_id = branch.last_revision_info()
310
    repo = branch.repository
311
    revision_ids = repo.iter_reverse_revision_history(start_revision_id)
312
    for num, revision_id in enumerate(revision_ids):
313
        yield revision_id, str(start_revno - num), 0
314
315
3642.1.1 by Robert Collins
Refactoring in log towards more pluggable revision selection.
316
def make_log_rev_iterator(branch, view_revisions, generate_delta, search):
317
    """Create a revision iterator for log.
318
319
    :param branch: The branch being logged.
320
    :param view_revisions: The revisions being viewed.
321
    :param generate_delta: Whether to generate a delta for each revision.
322
    :param search: A user text search string.
3642.1.7 by Robert Collins
Review feedback.
323
    :return: An iterator over lists of ((rev_id, revno, merge_depth), rev,
3642.1.2 by Robert Collins
Setup a log iterator that more closely matches what the code tries to do with repository operations.
324
        delta).
3642.1.1 by Robert Collins
Refactoring in log towards more pluggable revision selection.
325
    """
3642.1.5 by Robert Collins
Separate out batching of revisions.
326
    # Convert view_revisions into (view, None, None) groups to fit with
327
    # the standard interface here.
328
    if type(view_revisions) == list:
3642.1.7 by Robert Collins
Review feedback.
329
        # A single batch conversion is faster than many incremental ones.
330
        # As we have all the data, do a batch conversion.
3642.1.5 by Robert Collins
Separate out batching of revisions.
331
        nones = [None] * len(view_revisions)
332
        log_rev_iterator = iter([zip(view_revisions, nones, nones)])
333
    else:
334
        def _convert():
335
            for view in view_revisions:
336
                yield (view, None, None)
337
        log_rev_iterator = iter([_convert()])
3642.1.6 by Robert Collins
Make log revision filtering pluggable.
338
    for adapter in log_adapters:
339
        log_rev_iterator = adapter(branch, generate_delta, search,
340
            log_rev_iterator)
3642.1.1 by Robert Collins
Refactoring in log towards more pluggable revision selection.
341
    return log_rev_iterator
342
343
3642.1.7 by Robert Collins
Review feedback.
344
def _make_search_filter(branch, generate_delta, search, log_rev_iterator):
3642.1.1 by Robert Collins
Refactoring in log towards more pluggable revision selection.
345
    """Create a filtered iterator of log_rev_iterator matching on a regex.
346
347
    :param branch: The branch being logged.
348
    :param generate_delta: Whether to generate a delta for each revision.
349
    :param search: A user text search string.
350
    :param log_rev_iterator: An input iterator containing all revisions that
3642.1.2 by Robert Collins
Setup a log iterator that more closely matches what the code tries to do with repository operations.
351
        could be displayed, in lists.
3642.1.7 by Robert Collins
Review feedback.
352
    :return: An iterator over lists of ((rev_id, revno, merge_depth), rev,
3642.1.2 by Robert Collins
Setup a log iterator that more closely matches what the code tries to do with repository operations.
353
        delta).
3642.1.1 by Robert Collins
Refactoring in log towards more pluggable revision selection.
354
    """
355
    if search is None:
356
        return log_rev_iterator
357
    # Compile the search now to get early errors.
358
    searchRE = re.compile(search, re.IGNORECASE)
359
    return _filter_message_re(searchRE, log_rev_iterator)
360
361
362
def _filter_message_re(searchRE, log_rev_iterator):
3642.1.2 by Robert Collins
Setup a log iterator that more closely matches what the code tries to do with repository operations.
363
    for revs in log_rev_iterator:
3642.1.3 by Robert Collins
Split out delta generation from revision content reading, and structure it after message evaluation, increasing performance of log -v -m.
364
        new_revs = []
3642.1.2 by Robert Collins
Setup a log iterator that more closely matches what the code tries to do with repository operations.
365
        for (rev_id, revno, merge_depth), rev, delta in revs:
366
            if searchRE.search(rev.message):
3642.1.3 by Robert Collins
Split out delta generation from revision content reading, and structure it after message evaluation, increasing performance of log -v -m.
367
                new_revs.append(((rev_id, revno, merge_depth), rev, delta))
368
        yield new_revs
369
370
3642.1.7 by Robert Collins
Review feedback.
371
def _make_delta_filter(branch, generate_delta, search, log_rev_iterator):
3642.1.3 by Robert Collins
Split out delta generation from revision content reading, and structure it after message evaluation, increasing performance of log -v -m.
372
    """Add revision deltas to a log iterator if needed.
373
374
    :param branch: The branch being logged.
375
    :param generate_delta: Whether to generate a delta for each revision.
376
    :param search: A user text search string.
377
    :param log_rev_iterator: An input iterator containing all revisions that
378
        could be displayed, in lists.
3642.1.7 by Robert Collins
Review feedback.
379
    :return: An iterator over lists of ((rev_id, revno, merge_depth), rev,
3642.1.3 by Robert Collins
Split out delta generation from revision content reading, and structure it after message evaluation, increasing performance of log -v -m.
380
        delta).
381
    """
382
    if not generate_delta:
383
        return log_rev_iterator
384
    return _generate_deltas(branch.repository, log_rev_iterator)
385
386
387
def _generate_deltas(repository, log_rev_iterator):
3642.1.7 by Robert Collins
Review feedback.
388
    """Create deltas for each batch of revisions in log_rev_iterator."""
3642.1.3 by Robert Collins
Split out delta generation from revision content reading, and structure it after message evaluation, increasing performance of log -v -m.
389
    for revs in log_rev_iterator:
390
        revisions = [rev[1] for rev in revs]
391
        deltas = repository.get_deltas_for_revisions(revisions)
392
        revs = [(rev[0], rev[1], delta) for rev, delta in izip(revs, deltas)]
393
        yield revs
3642.1.2 by Robert Collins
Setup a log iterator that more closely matches what the code tries to do with repository operations.
394
395
3642.1.7 by Robert Collins
Review feedback.
396
def _make_revision_objects(branch, generate_delta, search, log_rev_iterator):
3642.1.4 by Robert Collins
Factor out revision object extraction from revision batching.
397
    """Extract revision objects from the repository
398
399
    :param branch: The branch being logged.
400
    :param generate_delta: Whether to generate a delta for each revision.
401
    :param search: A user text search string.
402
    :param log_rev_iterator: An input iterator containing all revisions that
403
        could be displayed, in lists.
3642.1.7 by Robert Collins
Review feedback.
404
    :return: An iterator over lists of ((rev_id, revno, merge_depth), rev,
3642.1.4 by Robert Collins
Factor out revision object extraction from revision batching.
405
        delta).
406
    """
3642.1.5 by Robert Collins
Separate out batching of revisions.
407
    repository = branch.repository
3642.1.4 by Robert Collins
Factor out revision object extraction from revision batching.
408
    for revs in log_rev_iterator:
409
        # r = revision_id, n = revno, d = merge depth
410
        revision_ids = [view[0] for view, _, _ in revs]
411
        revisions = repository.get_revisions(revision_ids)
412
        revs = [(rev[0], revision, rev[2]) for rev, revision in
413
            izip(revs, revisions)]
414
        yield revs
415
416
3642.1.7 by Robert Collins
Review feedback.
417
def _make_batch_filter(branch, generate_delta, search, log_rev_iterator):
3642.1.5 by Robert Collins
Separate out batching of revisions.
418
    """Group up a single large batch into smaller ones.
3642.1.2 by Robert Collins
Setup a log iterator that more closely matches what the code tries to do with repository operations.
419
420
    :param branch: The branch being logged.
421
    :param generate_delta: Whether to generate a delta for each revision.
422
    :param search: A user text search string.
3642.1.5 by Robert Collins
Separate out batching of revisions.
423
    :param log_rev_iterator: An input iterator containing all revisions that
424
        could be displayed, in lists.
3874.2.4 by Vincent Ladeuil
Fix too long lines.
425
    :return: An iterator over lists of ((rev_id, revno, merge_depth), rev,
426
        delta).
3642.1.2 by Robert Collins
Setup a log iterator that more closely matches what the code tries to do with repository operations.
427
    """
428
    repository = branch.repository
3302.1.1 by Aaron Bentley
Split out _iter_revision, allow view_revisions to be an iterator
429
    num = 9
3642.1.5 by Robert Collins
Separate out batching of revisions.
430
    for batch in log_rev_iterator:
431
        batch = iter(batch)
432
        while True:
433
            step = [detail for _, detail in zip(range(num), batch)]
434
            if len(step) == 0:
435
                break
436
            yield step
437
            num = min(int(num * 1.5), 200)
3302.1.1 by Aaron Bentley
Split out _iter_revision, allow view_revisions to be an iterator
438
439
2466.12.1 by Kent Gibson
Fix ``bzr log -r`` to support selecting merge revisions.
440
def _get_mainline_revs(branch, start_revision, end_revision):
441
    """Get the mainline revisions from the branch.
442
    
443
    Generates the list of mainline revisions for the branch.
444
    
445
    :param  branch: The branch containing the revisions. 
446
447
    :param  start_revision: The first revision to be logged.
448
            For backwards compatibility this may be a mainline integer revno,
449
            but for merge revision support a RevisionInfo is expected.
450
451
    :param  end_revision: The last revision to be logged.
452
            For backwards compatibility this may be a mainline integer revno,
453
            but for merge revision support a RevisionInfo is expected.
454
455
    :return: A (mainline_revs, rev_nos, start_rev_id, end_rev_id) tuple.
456
    """
3449.2.1 by John Arbash Meinel
bzr uncommit doesn't need to work in terms of 'revision_history()'
457
    branch_revno, branch_last_revision = branch.last_revision_info()
458
    if branch_revno == 0:
2466.12.1 by Kent Gibson
Fix ``bzr log -r`` to support selecting merge revisions.
459
        return None, None, None, None
460
461
    # For mainline generation, map start_revision and end_revision to 
462
    # mainline revnos. If the revision is not on the mainline choose the 
463
    # appropriate extreme of the mainline instead - the extra will be 
464
    # filtered later.
465
    # Also map the revisions to rev_ids, to be used in the later filtering
466
    # stage.
3842.2.4 by Vincent Ladeuil
Superficial fix for bug #300055.
467
    start_rev_id = None
2466.12.1 by Kent Gibson
Fix ``bzr log -r`` to support selecting merge revisions.
468
    if start_revision is None:
469
        start_revno = 1
470
    else:
3224.5.1 by Andrew Bennetts
Lots of assorted hackery to reduce the number of imports for common operations. Improves 'rocks', 'st' and 'help' times by ~50ms on my laptop.
471
        if isinstance(start_revision, revisionspec.RevisionInfo):
2466.12.1 by Kent Gibson
Fix ``bzr log -r`` to support selecting merge revisions.
472
            start_rev_id = start_revision.rev_id
473
            start_revno = start_revision.revno or 1
474
        else:
475
            branch.check_real_revno(start_revision)
476
            start_revno = start_revision
3842.2.4 by Vincent Ladeuil
Superficial fix for bug #300055.
477
2466.12.1 by Kent Gibson
Fix ``bzr log -r`` to support selecting merge revisions.
478
    end_rev_id = None
479
    if end_revision is None:
3449.2.5 by John Arbash Meinel
Stop referencing the variable I removed.
480
        end_revno = branch_revno
2466.12.1 by Kent Gibson
Fix ``bzr log -r`` to support selecting merge revisions.
481
    else:
3224.5.1 by Andrew Bennetts
Lots of assorted hackery to reduce the number of imports for common operations. Improves 'rocks', 'st' and 'help' times by ~50ms on my laptop.
482
        if isinstance(end_revision, revisionspec.RevisionInfo):
2466.12.1 by Kent Gibson
Fix ``bzr log -r`` to support selecting merge revisions.
483
            end_rev_id = end_revision.rev_id
3449.2.5 by John Arbash Meinel
Stop referencing the variable I removed.
484
            end_revno = end_revision.revno or branch_revno
2466.12.1 by Kent Gibson
Fix ``bzr log -r`` to support selecting merge revisions.
485
        else:
486
            branch.check_real_revno(end_revision)
487
            end_revno = end_revision
488
3535.5.1 by John Arbash Meinel
cleanup a few imports to be lazily loaded.
489
    if ((start_rev_id == _mod_revision.NULL_REVISION)
490
        or (end_rev_id == _mod_revision.NULL_REVISION)):
3224.5.1 by Andrew Bennetts
Lots of assorted hackery to reduce the number of imports for common operations. Improves 'rocks', 'st' and 'help' times by ~50ms on my laptop.
491
        raise errors.BzrCommandError('Logging revision 0 is invalid.')
2466.12.1 by Kent Gibson
Fix ``bzr log -r`` to support selecting merge revisions.
492
    if start_revno > end_revno:
3224.5.1 by Andrew Bennetts
Lots of assorted hackery to reduce the number of imports for common operations. Improves 'rocks', 'st' and 'help' times by ~50ms on my laptop.
493
        raise errors.BzrCommandError("Start revision must be older than "
494
                                     "the end revision.")
2466.12.1 by Kent Gibson
Fix ``bzr log -r`` to support selecting merge revisions.
495
3449.2.7 by John Arbash Meinel
Minor tweak from Ian
496
    if end_revno < start_revno:
2466.12.1 by Kent Gibson
Fix ``bzr log -r`` to support selecting merge revisions.
497
        return None, None, None, None
3449.2.1 by John Arbash Meinel
bzr uncommit doesn't need to work in terms of 'revision_history()'
498
    cur_revno = branch_revno
499
    rev_nos = {}
500
    mainline_revs = []
501
    for revision_id in branch.repository.iter_reverse_revision_history(
502
                        branch_last_revision):
503
        if cur_revno < start_revno:
3449.2.2 by John Arbash Meinel
Fix bug #172649. Cleanup, and handle the case where we are logging to the first revision.
504
            # We have gone far enough, but we always add 1 more revision
3449.2.1 by John Arbash Meinel
bzr uncommit doesn't need to work in terms of 'revision_history()'
505
            rev_nos[revision_id] = cur_revno
506
            mainline_revs.append(revision_id)
507
            break
508
        if cur_revno <= end_revno:
509
            rev_nos[revision_id] = cur_revno
510
            mainline_revs.append(revision_id)
511
        cur_revno -= 1
3449.2.2 by John Arbash Meinel
Fix bug #172649. Cleanup, and handle the case where we are logging to the first revision.
512
    else:
513
        # We walked off the edge of all revisions, so we add a 'None' marker
514
        mainline_revs.append(None)
2466.12.1 by Kent Gibson
Fix ``bzr log -r`` to support selecting merge revisions.
515
3449.2.1 by John Arbash Meinel
bzr uncommit doesn't need to work in terms of 'revision_history()'
516
    mainline_revs.reverse()
2466.12.1 by Kent Gibson
Fix ``bzr log -r`` to support selecting merge revisions.
517
518
    # override the mainline to look like the revision history.
519
    return mainline_revs, rev_nos, start_rev_id, end_rev_id
520
521
522
def _filter_revision_range(view_revisions, start_rev_id, end_rev_id):
523
    """Filter view_revisions based on revision ranges.
524
525
    :param view_revisions: A list of (revision_id, dotted_revno, merge_depth) 
526
            tuples to be filtered.
527
528
    :param start_rev_id: If not NONE specifies the first revision to be logged.
529
            If NONE then all revisions up to the end_rev_id are logged.
530
531
    :param end_rev_id: If not NONE specifies the last revision to be logged.
532
            If NONE then all revisions up to the end of the log are logged.
533
534
    :return: The filtered view_revisions.
535
    """
3842.2.4 by Vincent Ladeuil
Superficial fix for bug #300055.
536
    if start_rev_id or end_rev_id:
2466.12.1 by Kent Gibson
Fix ``bzr log -r`` to support selecting merge revisions.
537
        revision_ids = [r for r, n, d in view_revisions]
538
        if start_rev_id:
539
            start_index = revision_ids.index(start_rev_id)
540
        else:
541
            start_index = 0
542
        if start_rev_id == end_rev_id:
543
            end_index = start_index
544
        else:
545
            if end_rev_id:
546
                end_index = revision_ids.index(end_rev_id)
547
            else:
548
                end_index = len(view_revisions) - 1
549
        # To include the revisions merged into the last revision, 
550
        # extend end_rev_id down to, but not including, the next rev
551
        # with the same or lesser merge_depth
552
        end_merge_depth = view_revisions[end_index][2]
553
        try:
554
            for index in xrange(end_index+1, len(view_revisions)+1):
555
                if view_revisions[index][2] <= end_merge_depth:
556
                    end_index = index - 1
557
                    break
558
        except IndexError:
559
            # if the search falls off the end then log to the end as well
560
            end_index = len(view_revisions) - 1
561
        view_revisions = view_revisions[start_index:end_index+1]
562
    return view_revisions
563
564
3940.1.3 by Ian Clatworthy
fix code
565
def _filter_revisions_touching_file_id(branch, file_id, view_revisions,
566
    include_merges=True):
3711.3.23 by John Arbash Meinel
Documentation and cleanup.
567
    r"""Return the list of revision ids which touch a given file id.
2359.1.4 by John Arbash Meinel
Refactor the specific revisions for file id into a helper function.
568
2466.12.1 by Kent Gibson
Fix ``bzr log -r`` to support selecting merge revisions.
569
    The function filters view_revisions and returns a subset.
2359.1.4 by John Arbash Meinel
Refactor the specific revisions for file id into a helper function.
570
    This includes the revisions which directly change the file id,
571
    and the revisions which merge these changes. So if the
572
    revision graph is::
3711.3.23 by John Arbash Meinel
Documentation and cleanup.
573
        A-.
574
        |\ \
575
        B C E
576
        |/ /
577
        D |
578
        |\|
579
        | F
2359.1.4 by John Arbash Meinel
Refactor the specific revisions for file id into a helper function.
580
        |/
3711.3.23 by John Arbash Meinel
Documentation and cleanup.
581
        G
582
583
    And 'C' changes a file, then both C and D will be returned. F will not be
584
    returned even though it brings the changes to C into the branch starting
585
    with E. (Note that if we were using F as the tip instead of G, then we
586
    would see C, D, F.)
587
588
    This will also be restricted based on a subset of the mainline.
589
590
    :param branch: The branch where we can get text revision information.
3842.2.5 by Vincent Ladeuil
Better fix for bug #300055.
591
3711.3.23 by John Arbash Meinel
Documentation and cleanup.
592
    :param file_id: Filter out revisions that do not touch file_id.
3842.2.5 by Vincent Ladeuil
Better fix for bug #300055.
593
3711.3.23 by John Arbash Meinel
Documentation and cleanup.
594
    :param view_revisions: A list of (revision_id, dotted_revno, merge_depth)
595
        tuples. This is the list of revisions which will be filtered. It is
3842.2.5 by Vincent Ladeuil
Better fix for bug #300055.
596
        assumed that view_revisions is in merge_sort order (i.e. newest
597
        revision first ).
598
3940.1.3 by Ian Clatworthy
fix code
599
    :param include_merges: include merge revisions in the result or not
600
2359.1.8 by John Arbash Meinel
doc
601
    :return: A list of (revision_id, dotted_revno, merge_depth) tuples.
2359.1.4 by John Arbash Meinel
Refactor the specific revisions for file id into a helper function.
602
    """
3711.3.23 by John Arbash Meinel
Documentation and cleanup.
603
    # Lookup all possible text keys to determine which ones actually modified
604
    # the file.
605
    text_keys = [(file_id, rev_id) for rev_id, revno, depth in view_revisions]
3711.3.16 by John Arbash Meinel
Doc update.
606
    # Looking up keys in batches of 1000 can cut the time in half, as well as
607
    # memory consumption. GraphIndex *does* like to look for a few keys in
608
    # parallel, it just doesn't like looking for *lots* of keys in parallel.
3711.3.19 by John Arbash Meinel
Add a TODO discussing how our index requests should evolve.
609
    # TODO: This code needs to be re-evaluated periodically as we tune the
610
    #       indexing layer. We might consider passing in hints as to the known
611
    #       access pattern (sparse/clustered, high success rate/low success
612
    #       rate). This particular access is clustered with a low success rate.
3711.3.15 by John Arbash Meinel
Work around GraphIndex inefficiencies by requesting keys 1000 at a time.
613
    get_parent_map = branch.repository.texts.get_parent_map
614
    modified_text_revisions = set()
615
    chunk_size = 1000
616
    for start in xrange(0, len(text_keys), chunk_size):
617
        next_keys = text_keys[start:start + chunk_size]
3711.3.23 by John Arbash Meinel
Documentation and cleanup.
618
        # Only keep the revision_id portion of the key
3711.3.15 by John Arbash Meinel
Work around GraphIndex inefficiencies by requesting keys 1000 at a time.
619
        modified_text_revisions.update(
620
            [k[1] for k in get_parent_map(next_keys)])
621
    del text_keys, next_keys
3711.3.14 by John Arbash Meinel
Change the per-file log algorithm dramatically.
622
623
    result = []
624
    # Track what revisions will merge the current revision, replace entries
625
    # with 'None' when they have been added to result
626
    current_merge_stack = [None]
3711.3.23 by John Arbash Meinel
Documentation and cleanup.
627
    for info in view_revisions:
3711.3.14 by John Arbash Meinel
Change the per-file log algorithm dramatically.
628
        rev_id, revno, depth = info
629
        if depth == len(current_merge_stack):
630
            current_merge_stack.append(info)
631
        else:
632
            del current_merge_stack[depth + 1:]
633
            current_merge_stack[-1] = info
634
635
        if rev_id in modified_text_revisions:
636
            # This needs to be logged, along with the extra revisions
637
            for idx in xrange(len(current_merge_stack)):
638
                node = current_merge_stack[idx]
639
                if node is not None:
3940.1.3 by Ian Clatworthy
fix code
640
                    if include_merges or node[2] == 0:
641
                        result.append(node)
642
                        current_merge_stack[idx] = None
3711.3.4 by John Arbash Meinel
Significantly faster, but consuming more memory.
643
    return result
2359.1.4 by John Arbash Meinel
Refactor the specific revisions for file id into a helper function.
644
645
1756.2.20 by Aaron Bentley
Optimize log formats that don't show merges
646
def get_view_revisions(mainline_revs, rev_nos, branch, direction,
1756.2.22 by Aaron Bentley
Apply review comments
647
                       include_merges=True):
1756.2.18 by Aaron Bentley
Factor out the revision list generation
648
    """Produce an iterator of revisions to show
649
    :return: an iterator of (revision_id, revno, merge_depth)
650
    (if there is no revno for a revision, None is supplied)
651
    """
3943.5.2 by Ian Clatworthy
hand control of diff formatting to the log formatter
652
    if not include_merges:
1756.2.20 by Aaron Bentley
Optimize log formats that don't show merges
653
        revision_ids = mainline_revs[1:]
654
        if direction == 'reverse':
655
            revision_ids.reverse()
656
        for revision_id in revision_ids:
1988.4.2 by Robert Collins
``bzr log`` Now shows dotted-decimal revision numbers for all revisions,
657
            yield revision_id, str(rev_nos[revision_id]), 0
1756.2.20 by Aaron Bentley
Optimize log formats that don't show merges
658
        return
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
659
    graph = branch.repository.get_graph()
660
    # This asks for all mainline revisions, which means we only have to spider
661
    # sideways, rather than depth history. That said, its still size-of-history
662
    # and should be addressed.
3373.5.4 by John Arbash Meinel
Track down another bogus location. Only triggered with --long
663
    # mainline_revisions always includes an extra revision at the beginning, so
664
    # don't request it.
3287.6.8 by Robert Collins
Reduce code duplication as per review.
665
    parent_map = dict(((key, value) for key, value in
3373.5.4 by John Arbash Meinel
Track down another bogus location. Only triggered with --long
666
        graph.iter_ancestry(mainline_revs[1:]) if value is not None))
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
667
    # filter out ghosts; merge_sort errors on ghosts.
3535.5.1 by John Arbash Meinel
cleanup a few imports to be lazily loaded.
668
    rev_graph = _mod_repository._strip_NULL_ghosts(parent_map)
3224.5.1 by Andrew Bennetts
Lots of assorted hackery to reduce the number of imports for common operations. Improves 'rocks', 'st' and 'help' times by ~50ms on my laptop.
669
    merge_sorted_revisions = tsort.merge_sort(
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
670
        rev_graph,
1756.2.18 by Aaron Bentley
Factor out the revision list generation
671
        mainline_revs[-1],
1988.4.2 by Robert Collins
``bzr log`` Now shows dotted-decimal revision numbers for all revisions,
672
        mainline_revs,
673
        generate_revno=True)
1756.2.18 by Aaron Bentley
Factor out the revision list generation
674
675
    if direction == 'forward':
676
        # forward means oldest first.
1756.2.25 by Aaron Bentley
Sort revisions at each depth, instead of just mainline revisions.
677
        merge_sorted_revisions = reverse_by_depth(merge_sorted_revisions)
1756.2.18 by Aaron Bentley
Factor out the revision list generation
678
    elif direction != 'reverse':
679
        raise ValueError('invalid direction %r' % direction)
680
3874.2.4 by Vincent Ladeuil
Fix too long lines.
681
    for (sequence, rev_id, merge_depth, revno, end_of_merge
682
         ) in merge_sorted_revisions:
1988.4.2 by Robert Collins
``bzr log`` Now shows dotted-decimal revision numbers for all revisions,
683
        yield rev_id, '.'.join(map(str, revno)), merge_depth
1756.2.18 by Aaron Bentley
Factor out the revision list generation
684
685
1756.2.25 by Aaron Bentley
Sort revisions at each depth, instead of just mainline revisions.
686
def reverse_by_depth(merge_sorted_revisions, _depth=0):
687
    """Reverse revisions by depth.
1756.2.24 by Aaron Bentley
Forward sorting shows merges under mainline revision
688
1756.2.25 by Aaron Bentley
Sort revisions at each depth, instead of just mainline revisions.
689
    Revisions with a different depth are sorted as a group with the previous
690
    revision of that depth.  There may be no topological justification for this,
1756.2.24 by Aaron Bentley
Forward sorting shows merges under mainline revision
691
    but it looks much nicer.
692
    """
3842.2.6 by Vincent Ladeuil
Fix typo.
693
    # Add a fake revision at start so that we can always attach sub revisions
3842.2.4 by Vincent Ladeuil
Superficial fix for bug #300055.
694
    merge_sorted_revisions = [(None, None, _depth)] + merge_sorted_revisions
1756.2.24 by Aaron Bentley
Forward sorting shows merges under mainline revision
695
    zd_revisions = []
696
    for val in merge_sorted_revisions:
1756.2.25 by Aaron Bentley
Sort revisions at each depth, instead of just mainline revisions.
697
        if val[2] == _depth:
3842.2.4 by Vincent Ladeuil
Superficial fix for bug #300055.
698
            # Each revision at the current depth becomes a chunk grouping all
699
            # higher depth revisions.
1756.2.24 by Aaron Bentley
Forward sorting shows merges under mainline revision
700
            zd_revisions.append([val])
701
        else:
702
            zd_revisions[-1].append(val)
1756.2.25 by Aaron Bentley
Sort revisions at each depth, instead of just mainline revisions.
703
    for revisions in zd_revisions:
704
        if len(revisions) > 1:
3842.2.4 by Vincent Ladeuil
Superficial fix for bug #300055.
705
            # We have higher depth revisions, let reverse them locally
1756.2.25 by Aaron Bentley
Sort revisions at each depth, instead of just mainline revisions.
706
            revisions[1:] = reverse_by_depth(revisions[1:], _depth + 1)
1756.2.24 by Aaron Bentley
Forward sorting shows merges under mainline revision
707
    zd_revisions.reverse()
708
    result = []
709
    for chunk in zd_revisions:
710
        result.extend(chunk)
3842.2.4 by Vincent Ladeuil
Superficial fix for bug #300055.
711
    if _depth == 0:
712
        # Top level call, get rid of the fake revisions that have been added
713
        result = [r for r in result if r[0] is not None and r[1] is not None]
1756.2.24 by Aaron Bentley
Forward sorting shows merges under mainline revision
714
    return result
715
716
2466.8.1 by Kent Gibson
Reworked LogFormatter API to simplify extending the attributes of the revision being logged. Added support for begin_log() and end_log() hooks in LogFormatters.
717
class LogRevision(object):
718
    """A revision to be logged (by LogFormatter.log_revision).
719
720
    A simple wrapper for the attributes of a revision to be logged.
721
    The attributes may or may not be populated, as determined by the 
722
    logging options and the log formatter capabilities.
723
    """
724
2490.1.2 by John Arbash Meinel
Cleanup according to PEP8 and some other small whitespace fixes
725
    def __init__(self, rev=None, revno=None, merge_depth=0, delta=None,
3943.5.2 by Ian Clatworthy
hand control of diff formatting to the log formatter
726
                 tags=None, diff=None):
2466.8.1 by Kent Gibson
Reworked LogFormatter API to simplify extending the attributes of the revision being logged. Added support for begin_log() and end_log() hooks in LogFormatters.
727
        self.rev = rev
728
        self.revno = revno
729
        self.merge_depth = merge_depth
730
        self.delta = delta
731
        self.tags = tags
3943.5.2 by Ian Clatworthy
hand control of diff formatting to the log formatter
732
        self.diff = diff
2466.8.1 by Kent Gibson
Reworked LogFormatter API to simplify extending the attributes of the revision being logged. Added support for begin_log() and end_log() hooks in LogFormatters.
733
734
794 by Martin Pool
- Merge John's nice short-log format.
735
class LogFormatter(object):
2466.8.1 by Kent Gibson
Reworked LogFormatter API to simplify extending the attributes of the revision being logged. Added support for begin_log() and end_log() hooks in LogFormatters.
736
    """Abstract class to display log messages.
737
738
    At a minimum, a derived class must implement the log_revision method.
739
740
    If the LogFormatter needs to be informed of the beginning or end of
741
    a log it should implement the begin_log and/or end_log hook methods.
742
743
    A LogFormatter should define the following supports_XXX flags 
744
    to indicate which LogRevision attributes it supports:
745
746
    - supports_delta must be True if this log formatter supports delta.
3874.1.4 by Vincent Ladeuil
Fixed as per Aarons' comment.
747
        Otherwise the delta attribute may not be populated.  The 'delta_format'
748
        attribute describes whether the 'short_status' format (1) or the long
749
        one (2) sould be used.
750
 
2466.8.1 by Kent Gibson
Reworked LogFormatter API to simplify extending the attributes of the revision being logged. Added support for begin_log() and end_log() hooks in LogFormatters.
751
    - supports_merge_revisions must be True if this log formatter supports 
2997.1.1 by Kent Gibson
Support logging single merge revisions with short and line log formatters.
752
        merge revisions.  If not, and if supports_single_merge_revisions is
753
        also not True, then only mainline revisions will be passed to the 
754
        formatter.
3943.5.2 by Ian Clatworthy
hand control of diff formatting to the log formatter
755
2997.1.1 by Kent Gibson
Support logging single merge revisions with short and line log formatters.
756
    - supports_single_merge_revision must be True if this log formatter
757
        supports logging only a single merge revision.  This flag is
758
        only relevant if supports_merge_revisions is not True.
3943.5.2 by Ian Clatworthy
hand control of diff formatting to the log formatter
759
2466.8.1 by Kent Gibson
Reworked LogFormatter API to simplify extending the attributes of the revision being logged. Added support for begin_log() and end_log() hooks in LogFormatters.
760
    - supports_tags must be True if this log formatter supports tags.
761
        Otherwise the tags attribute may not be populated.
3144.7.1 by Guillermo Gonzalez
* added show_properties to LonLogFormat and the hooks to register custom functions
762
3943.5.2 by Ian Clatworthy
hand control of diff formatting to the log formatter
763
    - supports_diff must be True if this log formatter supports diffs.
764
        Otherwise the diff attribute may not be populated.
765
3144.7.1 by Guillermo Gonzalez
* added show_properties to LonLogFormat and the hooks to register custom functions
766
    Plugins can register functions to show custom revision properties using
3144.7.13 by Guillermo Gonzalez
* fixed typo LogFormatter.show_properties in docstring
767
    the properties_handler_registry. The registered function
3144.7.1 by Guillermo Gonzalez
* added show_properties to LonLogFormat and the hooks to register custom functions
768
    must respect the following interface description:
3144.7.2 by Guillermo Gonzalez
* cleanup a bit the interface
769
        def my_show_properties(properties_dict):
770
            # code that returns a dict {'name':'value'} of the properties 
771
            # to be shown
2466.8.1 by Kent Gibson
Reworked LogFormatter API to simplify extending the attributes of the revision being logged. Added support for begin_log() and end_log() hooks in LogFormatters.
772
    """
1704.2.20 by Martin Pool
log --line shows revision numbers (Alexander)
773
3874.1.4 by Vincent Ladeuil
Fixed as per Aarons' comment.
774
    def __init__(self, to_file, show_ids=False, show_timezone='original',
775
                 delta_format=None):
794 by Martin Pool
- Merge John's nice short-log format.
776
        self.to_file = to_file
777
        self.show_ids = show_ids
778
        self.show_timezone = show_timezone
3874.1.4 by Vincent Ladeuil
Fixed as per Aarons' comment.
779
        if delta_format is None:
780
            # Ensures backward compatibility
781
            delta_format = 2 # long format
782
        self.delta_format = delta_format
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
783
2466.8.1 by Kent Gibson
Reworked LogFormatter API to simplify extending the attributes of the revision being logged. Added support for begin_log() and end_log() hooks in LogFormatters.
784
# TODO: uncomment this block after show() has been removed.
785
# Until then defining log_revision would prevent _show_log calling show() 
786
# in legacy formatters.
787
#    def log_revision(self, revision):
788
#        """Log a revision.
789
#
790
#        :param  revision:   The LogRevision to be logged.
791
#        """
792
#        raise NotImplementedError('not implemented in abstract base')
793
1185.35.19 by Aaron Bentley
Tweaked short-log as Meinel suggested
794
    def short_committer(self, rev):
3063.3.2 by Lukáš Lalinský
Move the name and e-mail address extraction logic to config.parse_username.
795
        name, address = config.parse_username(rev.committer)
796
        if name:
3063.3.1 by Lukáš Lalinský
Fall back to showing e-mail in ``log --short/--line`` if the committer/author has only e-mail.
797
            return name
3063.3.2 by Lukáš Lalinský
Move the name and e-mail address extraction logic to config.parse_username.
798
        return address
2388.1.11 by Alexander Belchenko
changes after John's review
799
2671.5.4 by Lukáš Lalinsky
Replace the committer with the author in log, the committer is displayed only in the long format and only if it's different from the author.
800
    def short_author(self, rev):
3063.3.2 by Lukáš Lalinský
Move the name and e-mail address extraction logic to config.parse_username.
801
        name, address = config.parse_username(rev.get_apparent_author())
802
        if name:
3063.3.1 by Lukáš Lalinský
Fall back to showing e-mail in ``log --short/--line`` if the committer/author has only e-mail.
803
            return name
3063.3.2 by Lukáš Lalinský
Move the name and e-mail address extraction logic to config.parse_username.
804
        return address
2671.5.4 by Lukáš Lalinsky
Replace the committer with the author in log, the committer is displayed only in the long format and only if it's different from the author.
805
3144.7.9 by Guillermo Gonzalez
* bzrlib.log.show_roperties don't hide handler errors
806
    def show_properties(self, revision, indent):
3144.7.8 by Guillermo Gonzalez
* added error handling (and logging) to LogFormatter.show_properties when a handler raise an error
807
        """Displays the custom properties returned by each registered handler.
808
        
3144.7.13 by Guillermo Gonzalez
* fixed typo LogFormatter.show_properties in docstring
809
        If a registered handler raises an error it is propagated.
3144.7.5 by Guillermo Gonzalez
* some improvements to the doctstring in show_properties method and in LogFormatter
810
        """
3144.7.9 by Guillermo Gonzalez
* bzrlib.log.show_roperties don't hide handler errors
811
        for key, handler in properties_handler_registry.iteritems():
812
            for key, value in handler(revision).items():
813
                self.to_file.write(indent + key + ': ' + value + '\n')
3144.7.8 by Guillermo Gonzalez
* added error handling (and logging) to LogFormatter.show_properties when a handler raise an error
814
3943.5.2 by Ian Clatworthy
hand control of diff formatting to the log formatter
815
    def show_diff(self, to_file, diff, indent):
816
        for l in diff.rstrip().split('\n'):
817
            to_file.write(indent + '%s\n' % (l,))
818
2388.1.11 by Alexander Belchenko
changes after John's review
819
794 by Martin Pool
- Merge John's nice short-log format.
820
class LongLogFormatter(LogFormatter):
2388.1.11 by Alexander Belchenko
changes after John's review
821
2466.8.1 by Kent Gibson
Reworked LogFormatter API to simplify extending the attributes of the revision being logged. Added support for begin_log() and end_log() hooks in LogFormatters.
822
    supports_merge_revisions = True
823
    supports_delta = True
824
    supports_tags = True
3943.5.2 by Ian Clatworthy
hand control of diff formatting to the log formatter
825
    supports_diff = True
2388.1.10 by Alexander Belchenko
Slightly reworked: use None instead of [] as default tags list; PEP-8
826
2466.8.1 by Kent Gibson
Reworked LogFormatter API to simplify extending the attributes of the revision being logged. Added support for begin_log() and end_log() hooks in LogFormatters.
827
    def log_revision(self, revision):
828
        """Log a revision, either merged or not."""
2671.2.5 by Lukáš Lalinský
Fixes for comments from the mailing list.
829
        indent = '    ' * revision.merge_depth
1433 by Robert Collins
merge in and make incremental Gustavo Niemeyers nested log patch, and remove all bare exceptions in store and transport packages.
830
        to_file = self.to_file
2911.6.1 by Blake Winton
Change 'print >> f,'s to 'f.write('s.
831
        to_file.write(indent + '-' * 60 + '\n')
2466.8.1 by Kent Gibson
Reworked LogFormatter API to simplify extending the attributes of the revision being logged. Added support for begin_log() and end_log() hooks in LogFormatters.
832
        if revision.revno is not None:
2911.6.3 by Blake Winton
Implemented suggestions from John Arbash Meinel.
833
            to_file.write(indent + 'revno: %s\n' % (revision.revno,))
2466.8.1 by Kent Gibson
Reworked LogFormatter API to simplify extending the attributes of the revision being logged. Added support for begin_log() and end_log() hooks in LogFormatters.
834
        if revision.tags:
2911.6.1 by Blake Winton
Change 'print >> f,'s to 'f.write('s.
835
            to_file.write(indent + 'tags: %s\n' % (', '.join(revision.tags)))
1433 by Robert Collins
merge in and make incremental Gustavo Niemeyers nested log patch, and remove all bare exceptions in store and transport packages.
836
        if self.show_ids:
3257.2.1 by Adeodato Simó
Add a space after "revision-id:" in log output.
837
            to_file.write(indent + 'revision-id: ' + revision.rev.revision_id)
2911.6.1 by Blake Winton
Change 'print >> f,'s to 'f.write('s.
838
            to_file.write('\n')
2466.8.1 by Kent Gibson
Reworked LogFormatter API to simplify extending the attributes of the revision being logged. Added support for begin_log() and end_log() hooks in LogFormatters.
839
            for parent_id in revision.rev.parent_ids:
2911.6.3 by Blake Winton
Implemented suggestions from John Arbash Meinel.
840
                to_file.write(indent + 'parent: %s\n' % (parent_id,))
3144.7.11 by Guillermo Gonzalez
* updates LongLogFormatter to pass revision instead of the properties dict to show_properties method
841
        self.show_properties(revision.rev, indent)
2671.2.2 by Lukáš Lalinský
Move setting of the author revision property to MutableTree.commit. Don't use try/except KeyError in LongLogFormatter to display authors and branch-nicks. Removed warning about missing e-mail in the authors name.
842
2671.5.7 by Lukáš Lalinsky
Rename get_author to get_apparent_author, revert the long log back to displaying the committer.
843
        author = revision.rev.properties.get('author', None)
844
        if author is not None:
2911.6.3 by Blake Winton
Implemented suggestions from John Arbash Meinel.
845
            to_file.write(indent + 'author: %s\n' % (author,))
846
        to_file.write(indent + 'committer: %s\n' % (revision.rev.committer,))
2671.2.2 by Lukáš Lalinský
Move setting of the author revision property to MutableTree.commit. Don't use try/except KeyError in LongLogFormatter to display authors and branch-nicks. Removed warning about missing e-mail in the authors name.
847
848
        branch_nick = revision.rev.properties.get('branch-nick', None)
849
        if branch_nick is not None:
2911.6.3 by Blake Winton
Implemented suggestions from John Arbash Meinel.
850
            to_file.write(indent + 'branch nick: %s\n' % (branch_nick,))
2671.2.2 by Lukáš Lalinský
Move setting of the author revision property to MutableTree.commit. Don't use try/except KeyError in LongLogFormatter to display authors and branch-nicks. Removed warning about missing e-mail in the authors name.
851
2466.8.1 by Kent Gibson
Reworked LogFormatter API to simplify extending the attributes of the revision being logged. Added support for begin_log() and end_log() hooks in LogFormatters.
852
        date_str = format_date(revision.rev.timestamp,
853
                               revision.rev.timezone or 0,
1433 by Robert Collins
merge in and make incremental Gustavo Niemeyers nested log patch, and remove all bare exceptions in store and transport packages.
854
                               self.show_timezone)
2911.6.3 by Blake Winton
Implemented suggestions from John Arbash Meinel.
855
        to_file.write(indent + 'timestamp: %s\n' % (date_str,))
1433 by Robert Collins
merge in and make incremental Gustavo Niemeyers nested log patch, and remove all bare exceptions in store and transport packages.
856
2911.6.1 by Blake Winton
Change 'print >> f,'s to 'f.write('s.
857
        to_file.write(indent + 'message:\n')
2466.8.1 by Kent Gibson
Reworked LogFormatter API to simplify extending the attributes of the revision being logged. Added support for begin_log() and end_log() hooks in LogFormatters.
858
        if not revision.rev.message:
2911.6.1 by Blake Winton
Change 'print >> f,'s to 'f.write('s.
859
            to_file.write(indent + '  (no message)\n')
1433 by Robert Collins
merge in and make incremental Gustavo Niemeyers nested log patch, and remove all bare exceptions in store and transport packages.
860
        else:
2466.8.1 by Kent Gibson
Reworked LogFormatter API to simplify extending the attributes of the revision being logged. Added support for begin_log() and end_log() hooks in LogFormatters.
861
            message = revision.rev.message.rstrip('\r\n')
1185.31.20 by John Arbash Meinel
Stripping trailing newlines when displaying log messages
862
            for l in message.split('\n'):
3943.5.3 by Ian Clatworthy
add tests
863
                to_file.write(indent + '  %s\n' % (l,))
2466.8.1 by Kent Gibson
Reworked LogFormatter API to simplify extending the attributes of the revision being logged. Added support for begin_log() and end_log() hooks in LogFormatters.
864
        if revision.delta is not None:
3874.1.7 by Vincent Ladeuil
Restrict '-v' change to log --short only.
865
            # We don't respect delta_format for compatibility
3874.1.4 by Vincent Ladeuil
Fixed as per Aarons' comment.
866
            revision.delta.show(to_file, self.show_ids, indent=indent,
3874.1.7 by Vincent Ladeuil
Restrict '-v' change to log --short only.
867
                                short_status=False)
3943.5.2 by Ian Clatworthy
hand control of diff formatting to the log formatter
868
        if revision.diff is not None:
869
            to_file.write(indent + 'diff:\n')
3943.5.6 by Ian Clatworthy
feedback from jam's review
870
            # Note: we explicitly don't indent the diff (relative to the
871
            # revision information) so that the output can be fed to patch -p0
872
            self.show_diff(to_file, revision.diff, indent)
794 by Martin Pool
- Merge John's nice short-log format.
873
874
875
class ShortLogFormatter(LogFormatter):
2466.8.1 by Kent Gibson
Reworked LogFormatter API to simplify extending the attributes of the revision being logged. Added support for begin_log() and end_log() hooks in LogFormatters.
876
877
    supports_delta = True
3946.3.1 by Ian Clatworthy
extend ShortLogFormatter & LineLogFormatter to support tags
878
    supports_tags = True
2997.1.1 by Kent Gibson
Support logging single merge revisions with short and line log formatters.
879
    supports_single_merge_revision = True
3943.5.2 by Ian Clatworthy
hand control of diff formatting to the log formatter
880
    supports_diff = True
2466.8.1 by Kent Gibson
Reworked LogFormatter API to simplify extending the attributes of the revision being logged. Added support for begin_log() and end_log() hooks in LogFormatters.
881
882
    def log_revision(self, revision):
794 by Martin Pool
- Merge John's nice short-log format.
883
        to_file = self.to_file
2483.2.2 by John Arbash Meinel
Add [merge] after the timestamp for revisions with merges.
884
        is_merge = ''
2483.2.5 by John Arbash Meinel
[merge] bzr.dev 2501
885
        if len(revision.rev.parent_ids) > 1:
2483.2.2 by John Arbash Meinel
Add [merge] after the timestamp for revisions with merges.
886
            is_merge = ' [merge]'
3946.3.1 by Ian Clatworthy
extend ShortLogFormatter & LineLogFormatter to support tags
887
        tags = ''
888
        if revision.tags:
3946.3.2 by Ian Clatworthy
add tests & NEWS item
889
            tags = ' {%s}' % (', '.join(revision.tags))
3946.3.1 by Ian Clatworthy
extend ShortLogFormatter & LineLogFormatter to support tags
890
891
        to_file.write("%5s %s\t%s%s%s\n" % (revision.revno,
2671.5.4 by Lukáš Lalinsky
Replace the committer with the author in log, the committer is displayed only in the long format and only if it's different from the author.
892
                self.short_author(revision.rev),
2483.2.5 by John Arbash Meinel
[merge] bzr.dev 2501
893
                format_date(revision.rev.timestamp,
2466.8.1 by Kent Gibson
Reworked LogFormatter API to simplify extending the attributes of the revision being logged. Added support for begin_log() and end_log() hooks in LogFormatters.
894
                            revision.rev.timezone or 0,
1185.35.19 by Aaron Bentley
Tweaked short-log as Meinel suggested
895
                            self.show_timezone, date_fmt="%Y-%m-%d",
2483.2.5 by John Arbash Meinel
[merge] bzr.dev 2501
896
                            show_offset=False),
3946.3.1 by Ian Clatworthy
extend ShortLogFormatter & LineLogFormatter to support tags
897
                tags, is_merge))
794 by Martin Pool
- Merge John's nice short-log format.
898
        if self.show_ids:
3874.1.4 by Vincent Ladeuil
Fixed as per Aarons' comment.
899
            to_file.write('      revision-id:%s\n'
900
                          % (revision.rev.revision_id,))
2466.8.1 by Kent Gibson
Reworked LogFormatter API to simplify extending the attributes of the revision being logged. Added support for begin_log() and end_log() hooks in LogFormatters.
901
        if not revision.rev.message:
2911.6.1 by Blake Winton
Change 'print >> f,'s to 'f.write('s.
902
            to_file.write('      (no message)\n')
794 by Martin Pool
- Merge John's nice short-log format.
903
        else:
2466.8.1 by Kent Gibson
Reworked LogFormatter API to simplify extending the attributes of the revision being logged. Added support for begin_log() and end_log() hooks in LogFormatters.
904
            message = revision.rev.message.rstrip('\r\n')
1185.31.20 by John Arbash Meinel
Stripping trailing newlines when displaying log messages
905
            for l in message.split('\n'):
2911.6.3 by Blake Winton
Implemented suggestions from John Arbash Meinel.
906
                to_file.write('      %s\n' % (l,))
794 by Martin Pool
- Merge John's nice short-log format.
907
2466.8.1 by Kent Gibson
Reworked LogFormatter API to simplify extending the attributes of the revision being logged. Added support for begin_log() and end_log() hooks in LogFormatters.
908
        if revision.delta is not None:
3874.1.4 by Vincent Ladeuil
Fixed as per Aarons' comment.
909
            revision.delta.show(to_file, self.show_ids,
910
                                short_status=self.delta_format==1)
3943.5.2 by Ian Clatworthy
hand control of diff formatting to the log formatter
911
        if revision.diff is not None:
912
            self.show_diff(to_file, revision.diff, '      ')
2911.6.1 by Blake Winton
Change 'print >> f,'s to 'f.write('s.
913
        to_file.write('\n')
794 by Martin Pool
- Merge John's nice short-log format.
914
1704.2.20 by Martin Pool
log --line shows revision numbers (Alexander)
915
1185.12.25 by Aaron Bentley
Added one-line log format
916
class LineLogFormatter(LogFormatter):
2466.8.1 by Kent Gibson
Reworked LogFormatter API to simplify extending the attributes of the revision being logged. Added support for begin_log() and end_log() hooks in LogFormatters.
917
3946.3.1 by Ian Clatworthy
extend ShortLogFormatter & LineLogFormatter to support tags
918
    supports_tags = True
2997.1.1 by Kent Gibson
Support logging single merge revisions with short and line log formatters.
919
    supports_single_merge_revision = True
920
2466.8.1 by Kent Gibson
Reworked LogFormatter API to simplify extending the attributes of the revision being logged. Added support for begin_log() and end_log() hooks in LogFormatters.
921
    def __init__(self, *args, **kwargs):
922
        super(LineLogFormatter, self).__init__(*args, **kwargs)
923
        self._max_chars = terminal_width() - 1
924
1185.12.25 by Aaron Bentley
Added one-line log format
925
    def truncate(self, str, max_len):
926
        if len(str) <= max_len:
927
            return str
928
        return str[:max_len-3]+'...'
929
930
    def date_string(self, rev):
3842.2.4 by Vincent Ladeuil
Superficial fix for bug #300055.
931
        return format_date(rev.timestamp, rev.timezone or 0,
1185.12.25 by Aaron Bentley
Added one-line log format
932
                           self.show_timezone, date_fmt="%Y-%m-%d",
933
                           show_offset=False)
934
935
    def message(self, rev):
936
        if not rev.message:
937
            return '(no message)'
938
        else:
939
            return rev.message
940
2466.8.1 by Kent Gibson
Reworked LogFormatter API to simplify extending the attributes of the revision being logged. Added support for begin_log() and end_log() hooks in LogFormatters.
941
    def log_revision(self, revision):
2911.6.1 by Blake Winton
Change 'print >> f,'s to 'f.write('s.
942
        self.to_file.write(self.log_string(revision.revno, revision.rev,
3946.3.1 by Ian Clatworthy
extend ShortLogFormatter & LineLogFormatter to support tags
943
            self._max_chars, revision.tags))
2911.6.1 by Blake Winton
Change 'print >> f,'s to 'f.write('s.
944
        self.to_file.write('\n')
2466.8.1 by Kent Gibson
Reworked LogFormatter API to simplify extending the attributes of the revision being logged. Added support for begin_log() and end_log() hooks in LogFormatters.
945
3946.3.1 by Ian Clatworthy
extend ShortLogFormatter & LineLogFormatter to support tags
946
    def log_string(self, revno, rev, max_chars, tags=None):
1704.2.20 by Martin Pool
log --line shows revision numbers (Alexander)
947
        """Format log info into one string. Truncate tail of string
3677.1.1 by Vincent Ladeuil
Begin fixing bug #233817.
948
        :param  revno:      revision number or None.
1704.2.20 by Martin Pool
log --line shows revision numbers (Alexander)
949
                            Revision numbers counts from 1.
3946.3.1 by Ian Clatworthy
extend ShortLogFormatter & LineLogFormatter to support tags
950
        :param  rev:        revision object
1704.2.20 by Martin Pool
log --line shows revision numbers (Alexander)
951
        :param  max_chars:  maximum length of resulting string
3946.3.1 by Ian Clatworthy
extend ShortLogFormatter & LineLogFormatter to support tags
952
        :param  tags:       list of tags or None
1704.2.20 by Martin Pool
log --line shows revision numbers (Alexander)
953
        :return:            formatted truncated string
954
        """
955
        out = []
956
        if revno:
957
            # show revno only when is not None
3946.3.4 by Ian Clatworthy
minor cleanup
958
            out.append("%s:" % revno)
2671.5.4 by Lukáš Lalinsky
Replace the committer with the author in log, the committer is displayed only in the long format and only if it's different from the author.
959
        out.append(self.truncate(self.short_author(rev), 20))
1185.12.25 by Aaron Bentley
Added one-line log format
960
        out.append(self.date_string(rev))
3946.3.3 by Ian Clatworthy
feedback from jelmer re position of tags in --line
961
        if tags:
962
            tag_str = '{%s}' % (', '.join(tags))
963
            out.append(tag_str)
1740.2.5 by Aaron Bentley
Merge from bzr.dev
964
        out.append(rev.get_summary())
1185.12.25 by Aaron Bentley
Added one-line log format
965
        return self.truncate(" ".join(out).rstrip('\n'), max_chars)
794 by Martin Pool
- Merge John's nice short-log format.
966
1704.2.20 by Martin Pool
log --line shows revision numbers (Alexander)
967
1185.12.27 by Aaron Bentley
Use line log for pending merges
968
def line_log(rev, max_chars):
969
    lf = LineLogFormatter(None)
1704.2.20 by Martin Pool
log --line shows revision numbers (Alexander)
970
    return lf.log_string(None, rev, max_chars)
1185.12.27 by Aaron Bentley
Use line log for pending merges
971
2221.4.10 by Aaron Bentley
Implement log options using RegistryOption
972
973
class LogFormatterRegistry(registry.Registry):
974
    """Registry for log formatters"""
975
976
    def make_formatter(self, name, *args, **kwargs):
977
        """Construct a formatter from arguments.
978
979
        :param name: Name of the formatter to construct.  'short', 'long' and
980
            'line' are built-in.
981
        """
982
        return self.get(name)(*args, **kwargs)
983
984
    def get_default(self, branch):
985
        return self.get(branch.get_config().log_format())
986
987
988
log_formatter_registry = LogFormatterRegistry()
989
990
991
log_formatter_registry.register('short', ShortLogFormatter,
992
                                'Moderately short log format')
993
log_formatter_registry.register('long', LongLogFormatter,
994
                                'Detailed log format')
995
log_formatter_registry.register('line', LineLogFormatter,
996
                                'Log format with one line per revision')
997
794 by Martin Pool
- Merge John's nice short-log format.
998
1553.2.1 by Erik Bågfors
Support for plugins to register log formatters and set default formatter
999
def register_formatter(name, formatter):
2221.4.10 by Aaron Bentley
Implement log options using RegistryOption
1000
    log_formatter_registry.register(name, formatter)
1001
1553.2.1 by Erik Bågfors
Support for plugins to register log formatters and set default formatter
1002
794 by Martin Pool
- Merge John's nice short-log format.
1003
def log_formatter(name, *args, **kwargs):
1393.1.56 by Martin Pool
- doc and small refactoring of log code
1004
    """Construct a formatter from arguments.
1005
1185.12.27 by Aaron Bentley
Use line log for pending merges
1006
    name -- Name of the formatter to construct; currently 'long', 'short' and
1007
        'line' are supported.
1393.1.56 by Martin Pool
- doc and small refactoring of log code
1008
    """
794 by Martin Pool
- Merge John's nice short-log format.
1009
    try:
2221.4.10 by Aaron Bentley
Implement log options using RegistryOption
1010
        return log_formatter_registry.make_formatter(name, *args, **kwargs)
1553.2.2 by Erik Bågfors
Made "unknown log formatter" error message work
1011
    except KeyError:
3224.5.1 by Andrew Bennetts
Lots of assorted hackery to reduce the number of imports for common operations. Improves 'rocks', 'st' and 'help' times by ~50ms on my laptop.
1012
        raise errors.BzrCommandError("unknown log formatter: %r" % name)
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
1013
2221.4.10 by Aaron Bentley
Implement log options using RegistryOption
1014
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
1015
def show_one_log(revno, rev, delta, verbose, to_file, show_timezone):
1759.2.1 by Jelmer Vernooij
Fix some types (found using aspell).
1016
    # deprecated; for compatibility
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
1017
    lf = LongLogFormatter(to_file=to_file, show_timezone=show_timezone)
1018
    lf.show(revno, rev, delta)
1185.32.2 by John Arbash Meinel
Refactor pull --verbose into a log.py function, add tests.
1019
2490.1.4 by John Arbash Meinel
Update bzrlib.log.show_changed_revisions to use the new api
1020
1551.17.2 by Aaron Bentley
Stop showing deltas in pull -v output
1021
def show_changed_revisions(branch, old_rh, new_rh, to_file=None,
1022
                           log_format='long'):
1185.32.2 by John Arbash Meinel
Refactor pull --verbose into a log.py function, add tests.
1023
    """Show the change in revision history comparing the old revision history to the new one.
1024
1025
    :param branch: The branch where the revisions exist
1026
    :param old_rh: The old revision history
1027
    :param new_rh: The new revision history
1028
    :param to_file: A file to write the results to. If None, stdout will be used
1029
    """
1030
    if to_file is None:
2997.1.3 by Alexander Belchenko
file wrapper around stdout should use terminal encoding, not user_encoding.
1031
        to_file = codecs.getwriter(get_terminal_encoding())(sys.stdout,
1032
            errors='replace')
1185.32.2 by John Arbash Meinel
Refactor pull --verbose into a log.py function, add tests.
1033
    lf = log_formatter(log_format,
1034
                       show_ids=False,
1035
                       to_file=to_file,
1036
                       show_timezone='original')
1037
1038
    # This is the first index which is different between
1039
    # old and new
1040
    base_idx = None
1041
    for i in xrange(max(len(new_rh),
1042
                        len(old_rh))):
1043
        if (len(new_rh) <= i
1044
            or len(old_rh) <= i
1045
            or new_rh[i] != old_rh[i]):
1046
            base_idx = i
1047
            break
1048
1049
    if base_idx is None:
1050
        to_file.write('Nothing seems to have changed\n')
1051
        return
1052
    ## TODO: It might be nice to do something like show_log
1053
    ##       and show the merged entries. But since this is the
1054
    ##       removed revisions, it shouldn't be as important
1055
    if base_idx < len(old_rh):
1056
        to_file.write('*'*60)
1057
        to_file.write('\nRemoved Revisions:\n')
1058
        for i in range(base_idx, len(old_rh)):
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
1059
            rev = branch.repository.get_revision(old_rh[i])
2490.1.4 by John Arbash Meinel
Update bzrlib.log.show_changed_revisions to use the new api
1060
            lr = LogRevision(rev, i+1, 0, None)
1061
            lf.log_revision(lr)
1185.32.2 by John Arbash Meinel
Refactor pull --verbose into a log.py function, add tests.
1062
        to_file.write('*'*60)
1063
        to_file.write('\n\n')
1064
    if base_idx < len(new_rh):
1065
        to_file.write('Added Revisions:\n')
1066
        show_log(branch,
1067
                 lf,
1068
                 None,
1551.17.2 by Aaron Bentley
Stop showing deltas in pull -v output
1069
                 verbose=False,
1185.32.2 by John Arbash Meinel
Refactor pull --verbose into a log.py function, add tests.
1070
                 direction='forward',
1071
                 start_revision=base_idx+1,
1072
                 end_revision=len(new_rh),
1073
                 search=None)
1074
3144.7.4 by Guillermo Gonzalez
* move the function regisstry into a real Registry instead of a list
1075
3848.1.7 by Aaron Bentley
Use repository in get_history_change
1076
def get_history_change(old_revision_id, new_revision_id, repository):
3848.1.11 by Aaron Bentley
Cleanup and use of show_branch_change
1077
    """Calculate the uncommon lefthand history between two revisions.
1078
1079
    :param old_revision_id: The original revision id.
1080
    :param new_revision_id: The new revision id.
3848.1.22 by Aaron Bentley
Fix spelling
1081
    :param repository: The repository to use for the calculation.
3848.1.11 by Aaron Bentley
Cleanup and use of show_branch_change
1082
1083
    return old_history, new_history
1084
    """
3848.1.6 by Aaron Bentley
Implement get_history_change
1085
    old_history = []
1086
    old_revisions = set()
1087
    new_history = []
1088
    new_revisions = set()
3848.1.7 by Aaron Bentley
Use repository in get_history_change
1089
    new_iter = repository.iter_reverse_revision_history(new_revision_id)
1090
    old_iter = repository.iter_reverse_revision_history(old_revision_id)
3848.1.6 by Aaron Bentley
Implement get_history_change
1091
    stop_revision = None
1092
    do_old = True
1093
    do_new = True
1094
    while do_new or do_old:
1095
        if do_new:
1096
            try:
1097
                new_revision = new_iter.next()
1098
            except StopIteration:
1099
                do_new = False
1100
            else:
1101
                new_history.append(new_revision)
1102
                new_revisions.add(new_revision)
1103
                if new_revision in old_revisions:
1104
                    stop_revision = new_revision
1105
                    break
1106
        if do_old:
1107
            try:
1108
                old_revision = old_iter.next()
1109
            except StopIteration:
1110
                do_old = False
1111
            else:
1112
                old_history.append(old_revision)
1113
                old_revisions.add(old_revision)
1114
                if old_revision in new_revisions:
1115
                    stop_revision = old_revision
1116
                    break
1117
    new_history.reverse()
1118
    old_history.reverse()
1119
    if stop_revision is not None:
1120
        new_history = new_history[new_history.index(stop_revision) + 1:]
1121
        old_history = old_history[old_history.index(stop_revision) + 1:]
1122
    return old_history, new_history
1123
1124
3848.1.11 by Aaron Bentley
Cleanup and use of show_branch_change
1125
def show_branch_change(branch, output, old_revno, old_revision_id):
1126
    """Show the changes made to a branch.
1127
1128
    :param branch: The branch to show changes about.
1129
    :param output: A file-like object to write changes to.
1130
    :param old_revno: The revno of the old tip.
1131
    :param old_revision_id: The revision_id of the old tip.
1132
    """
3848.1.8 by Aaron Bentley
Implement basic show_branch_change
1133
    new_revno, new_revision_id = branch.last_revision_info()
1134
    old_history, new_history = get_history_change(old_revision_id,
1135
                                                  new_revision_id,
1136
                                                  branch.repository)
1137
    if old_history == [] and new_history == []:
1138
        output.write('Nothing seems to have changed\n')
1139
        return
1140
3848.1.10 by Aaron Bentley
Move log display into show_flat_log
1141
    log_format = log_formatter_registry.get_default(branch)
1142
    lf = log_format(show_ids=False, to_file=output, show_timezone='original')
3848.1.8 by Aaron Bentley
Implement basic show_branch_change
1143
    if old_history != []:
1144
        output.write('*'*60)
1145
        output.write('\nRemoved Revisions:\n')
3848.1.10 by Aaron Bentley
Move log display into show_flat_log
1146
        show_flat_log(branch.repository, old_history, old_revno, lf)
3848.1.8 by Aaron Bentley
Implement basic show_branch_change
1147
        output.write('*'*60)
1148
        output.write('\n\n')
1149
    if new_history != []:
3848.1.9 by Aaron Bentley
new/old sections are omitted as appropriate.
1150
        output.write('Added Revisions:\n')
3848.1.8 by Aaron Bentley
Implement basic show_branch_change
1151
        start_revno = new_revno - len(new_history) + 1
1152
        show_log(branch, lf, None, verbose=False, direction='forward',
1153
                 start_revision=start_revno,)
1154
1155
3848.1.10 by Aaron Bentley
Move log display into show_flat_log
1156
def show_flat_log(repository, history, last_revno, lf):
3848.1.11 by Aaron Bentley
Cleanup and use of show_branch_change
1157
    """Show a simple log of the specified history.
1158
1159
    :param repository: The repository to retrieve revisions from.
1160
    :param history: A list of revision_ids indicating the lefthand history.
1161
    :param last_revno: The revno of the last revision_id in the history.
1162
    :param lf: The log formatter to use.
1163
    """
3848.1.10 by Aaron Bentley
Move log display into show_flat_log
1164
    start_revno = last_revno - len(history) + 1
1165
    revisions = repository.get_revisions(history)
1166
    for i, rev in enumerate(revisions):
1167
        lr = LogRevision(rev, i + last_revno, 0, None)
1168
        lf.log_revision(lr)
1169
1170
3144.7.9 by Guillermo Gonzalez
* bzrlib.log.show_roperties don't hide handler errors
1171
properties_handler_registry = registry.Registry()
3830.4.1 by Jelmer Vernooij
Add base classes for foreign branches.
1172
properties_handler_registry.register_lazy("foreign",
1173
                                          "bzrlib.foreign",
1174
                                          "show_foreign_properties")
1175
3642.1.6 by Robert Collins
Make log revision filtering pluggable.
1176
1177
# adapters which revision ids to log are filtered. When log is called, the
1178
# log_rev_iterator is adapted through each of these factory methods.
1179
# Plugins are welcome to mutate this list in any way they like - as long
1180
# as the overall behaviour is preserved. At this point there is no extensible
1181
# mechanism for getting parameters to each factory method, and until there is
1182
# this won't be considered a stable api.
1183
log_adapters = [
1184
    # core log logic
3642.1.7 by Robert Collins
Review feedback.
1185
    _make_batch_filter,
3642.1.6 by Robert Collins
Make log revision filtering pluggable.
1186
    # read revision objects
3642.1.7 by Robert Collins
Review feedback.
1187
    _make_revision_objects,
3642.1.6 by Robert Collins
Make log revision filtering pluggable.
1188
    # filter on log messages
3642.1.7 by Robert Collins
Review feedback.
1189
    _make_search_filter,
3642.1.6 by Robert Collins
Make log revision filtering pluggable.
1190
    # generate deltas for things we will show
3642.1.7 by Robert Collins
Review feedback.
1191
    _make_delta_filter
3642.1.6 by Robert Collins
Make log revision filtering pluggable.
1192
    ]