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