/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/log.py

  • Committer: Ian Clatworthy
  • Date: 2009-03-26 05:41:59 UTC
  • mto: (4205.1.1 ianc-integration)
  • mto: This revision was merged to the branch mainline in revision 4206.
  • Revision ID: ian.clatworthy@canonical.com-20090326054159-hbcy5hc1kudyx9t4
apply review tweaks & update help

Show diffs side-by-side

added added

removed removed

Lines of Context:
201
201
        diff_type = None
202
202
 
203
203
    # Build the request and execute it
204
 
    rqst = LogRequest(direction=direction, specific_fileids=file_ids,
 
204
    rqst = make_log_request_dict(direction=direction, specific_fileids=file_ids,
205
205
        start_revision=start_revision, end_revision=end_revision,
206
206
        limit=limit, message_search=search,
207
207
        delta_type=delta_type, diff_type=diff_type)
208
208
    Logger(branch, rqst).show(lf)
209
209
 
210
210
 
211
 
class LogRequest(object):
212
 
    """Query parameters for logging a branch."""
213
 
 
214
 
    def __init__(self,
215
 
        direction='reverse',
216
 
        specific_fileids=None,
217
 
        start_revision=None,
218
 
        end_revision=None,
219
 
        limit=None,
220
 
        message_search=None,
221
 
        levels=1,
222
 
        generate_tags=True,
223
 
        delta_type=None,
224
 
        diff_type=None,
225
 
        _match_using_deltas=True,
226
 
        ):
227
 
        """Create a logging request.
228
 
 
229
 
        Each of these parameter become a public attribute of the object.
230
 
 
231
 
        :param direction: 'reverse' (default) is latest to earliest;
232
 
          'forward' is earliest to latest.
233
 
 
234
 
        :param specific_fileids: If not None, only include revisions
235
 
          affecting the specified files, rather than all revisions.
236
 
 
237
 
        :param start_revision: If not None, only generate
238
 
          revisions >= start_revision
239
 
 
240
 
        :param end_revision: If not None, only generate
241
 
          revisions <= end_revision
242
 
 
243
 
        :param limit: If set, generate only 'limit' revisions, all revisions
244
 
          are shown if None or 0.
245
 
 
246
 
        :param message_search: If not None, only include revisions with
247
 
          matching commit messages
248
 
 
249
 
        :param levels: the number of levels of revisions to
250
 
          generate; 1 for just the mainline; 0 for all levels.
251
 
 
252
 
        :param generate_tags: If True, include tags for matched revisions.
253
 
 
254
 
        :param delta_type: Either 'full', 'partial' or None.
255
 
          'full' means generate the complete delta - adds/deletes/modifies/etc;
256
 
          'partial' means filter the delta using specific_fileids;
257
 
          None means do not generate any delta.
258
 
 
259
 
        :param diff_type: Either 'full', 'partial' or None.
260
 
          'full' means generate the complete diff - adds/deletes/modifies/etc;
261
 
          'partial' means filter the diff using specific_fileids;
262
 
          None means do not generate any diff.
263
 
 
264
 
        :param _match_using_deltas: a private parameter controlling the
265
 
          algorithm used for matching specific_fileids. This parameter
266
 
          may be removed in the future so bzrlib client code should NOT
267
 
          use it.
268
 
        """
269
 
        self.direction = direction
270
 
        self.specific_fileids = specific_fileids
271
 
        self.start_revision = start_revision
272
 
        self.end_revision = end_revision
273
 
        self.limit = limit
274
 
        self.message_search = message_search
275
 
        self.levels = levels
276
 
        self.generate_tags = generate_tags
277
 
        self.delta_type = delta_type
278
 
        self.diff_type = diff_type
 
211
# Note: This needs to be kept this in sync with the defaults in
 
212
# make_log_request_dict() below
 
213
_DEFAULT_REQUEST_PARAMS = {
 
214
    'direction': 'reverse',
 
215
    'levels': 1,
 
216
    'generate_tags': True,
 
217
    '_match_using_deltas': True,
 
218
    }
 
219
 
 
220
 
 
221
def make_log_request_dict(direction='reverse', specific_fileids=None,
 
222
    start_revision=None, end_revision=None, limit=None,
 
223
    message_search=None, levels=1, generate_tags=True, delta_type=None,
 
224
    diff_type=None, _match_using_deltas=True):
 
225
    """Convenience function for making a logging request dictionary.
 
226
 
 
227
    Using this function may make code slightly safer by ensuring
 
228
    parameters have the correct names. It also provides a reference
 
229
    point for documenting the supported parameters.
 
230
 
 
231
    :param direction: 'reverse' (default) is latest to earliest;
 
232
      'forward' is earliest to latest.
 
233
 
 
234
    :param specific_fileids: If not None, only include revisions
 
235
      affecting the specified files, rather than all revisions.
 
236
 
 
237
    :param start_revision: If not None, only generate
 
238
      revisions >= start_revision
 
239
 
 
240
    :param end_revision: If not None, only generate
 
241
      revisions <= end_revision
 
242
 
 
243
    :param limit: If set, generate only 'limit' revisions, all revisions
 
244
      are shown if None or 0.
 
245
 
 
246
    :param message_search: If not None, only include revisions with
 
247
      matching commit messages
 
248
 
 
249
    :param levels: the number of levels of revisions to
 
250
      generate; 1 for just the mainline; 0 for all levels.
 
251
 
 
252
    :param generate_tags: If True, include tags for matched revisions.
 
253
 
 
254
    :param delta_type: Either 'full', 'partial' or None.
 
255
      'full' means generate the complete delta - adds/deletes/modifies/etc;
 
256
      'partial' means filter the delta using specific_fileids;
 
257
      None means do not generate any delta.
 
258
 
 
259
    :param diff_type: Either 'full', 'partial' or None.
 
260
      'full' means generate the complete diff - adds/deletes/modifies/etc;
 
261
      'partial' means filter the diff using specific_fileids;
 
262
      None means do not generate any diff.
 
263
 
 
264
    :param _match_using_deltas: a private parameter controlling the
 
265
      algorithm used for matching specific_fileids. This parameter
 
266
      may be removed in the future so bzrlib client code should NOT
 
267
      use it.
 
268
    """
 
269
    return {
 
270
        'direction': direction,
 
271
        'specific_fileids': specific_fileids,
 
272
        'start_revision': start_revision,
 
273
        'end_revision': end_revision,
 
274
        'limit': limit,
 
275
        'message_search': message_search,
 
276
        'levels': levels,
 
277
        'generate_tags': generate_tags,
 
278
        'delta_type': delta_type,
 
279
        'diff_type': diff_type,
279
280
        # Add 'private' attributes for features that may be deprecated
280
 
        self._match_using_deltas = _match_using_deltas
281
 
        self._allow_single_merge_revision = True
 
281
        '_match_using_deltas': _match_using_deltas,
 
282
        '_allow_single_merge_revision': True,
 
283
    }
 
284
 
 
285
 
 
286
def _apply_log_request_defaults(rqst):
 
287
    """Apply default values to a request dictionary."""
 
288
    result = _DEFAULT_REQUEST_PARAMS
 
289
    if rqst:
 
290
        result.update(rqst)
 
291
    return result
282
292
 
283
293
 
284
294
class LogGenerator(object):
299
309
        """Create a Logger.
300
310
 
301
311
        :param branch: the branch to log
302
 
        :param rqst: The LogRequest object specifying the query parameters.
 
312
        :param rqst: A dictionary specifying the query parameters.
 
313
          See make_log_request_dict() for supported values.
303
314
        """
304
315
        self.branch = branch
305
 
        self.rqst = rqst
 
316
        self.rqst = _apply_log_request_defaults(rqst)
306
317
 
307
318
    def show(self, lf):
308
319
        """Display the log.
330
341
        # Tweak the LogRequest based on what the LogFormatter can handle.
331
342
        # (There's no point generating stuff if the formatter can't display it.)
332
343
        rqst = self.rqst
333
 
        rqst.levels = lf.get_levels()
 
344
        rqst['levels'] = lf.get_levels()
334
345
        if not getattr(lf, 'supports_tags', False):
335
 
            rqst.generate_tags = False
 
346
            rqst['generate_tags'] = False
336
347
        if not getattr(lf, 'supports_delta', False):
337
 
            rqst.delta_type = None
 
348
            rqst['delta_type'] = None
338
349
        if not getattr(lf, 'supports_diff', False):
339
 
            rqst.diff_type = None
 
350
            rqst['diff_type'] = None
340
351
        if not getattr(lf, 'supports_merge_revisions', False):
341
 
            rqst._allow_single_merge_revision = getattr(lf,
 
352
            rqst['_allow_single_merge_revision'] = getattr(lf,
342
353
                'supports_single_merge_revision', False)
343
354
 
344
355
        # Find and print the interesting revisions
358
369
    """Raised when a start revision is not found walking left-hand history."""
359
370
 
360
371
 
361
 
class _DefaultLogGenerator(object):
362
 
    """A generator of log revisions given a branch and a LogRequest."""
 
372
class _DefaultLogGenerator(LogGenerator):
 
373
    """The default generator of log revisions."""
363
374
 
364
375
    def __init__(self, branch, rqst):
365
376
        self.branch = branch
366
377
        self.rqst = rqst
367
 
        if rqst.generate_tags and branch.supports_tags():
 
378
        if rqst.get('generate_tags') and branch.supports_tags():
368
379
            self.rev_tag_dict = branch.tags.get_reverse_tag_dict()
369
380
        else:
370
381
            self.rev_tag_dict = {}
380
391
        for revs in revision_iterator:
381
392
            for (rev_id, revno, merge_depth), rev, delta in revs:
382
393
                # 0 levels means show everything; merge_depth counts from 0
383
 
                if rqst.levels != 0 and merge_depth >= rqst.levels:
 
394
                levels = rqst.get('levels')
 
395
                if levels != 0 and merge_depth >= levels:
384
396
                    continue
385
397
                diff = self._format_diff(rev, rev_id)
386
398
                yield LogRevision(rev, revno, merge_depth, delta,
387
399
                    self.rev_tag_dict.get(rev_id), diff)
388
 
                if rqst.limit:
 
400
                limit = rqst.get('limit')
 
401
                if limit:
389
402
                    log_count += 1
390
 
                    if log_count >= rqst.limit:
 
403
                    if log_count >= limit:
391
404
                        return
392
405
 
393
406
    def _format_diff(self, rev, rev_id):
394
 
        diff_type = self.rqst.diff_type
 
407
        diff_type = self.rqst.get('diff_type')
395
408
        if diff_type is None:
396
409
            return None
397
410
        repo = self.branch.repository
401
414
            ancestor_id = rev.parent_ids[0]
402
415
        tree_1 = repo.revision_tree(ancestor_id)
403
416
        tree_2 = repo.revision_tree(rev_id)
404
 
        file_ids = self.rqst.specific_fileids
 
417
        file_ids = self.rqst.get('specific_fileids')
405
418
        if diff_type == 'partial' and file_ids is not None:
406
419
            specific_files = [tree_2.id2path(id) for id in file_ids]
407
420
        else:
418
431
            delta).
419
432
        """
420
433
        self.start_rev_id, self.end_rev_id = _get_revision_limits(
421
 
            self.branch, self.rqst.start_revision, self.rqst.end_revision)
422
 
        if self.rqst._match_using_deltas:
 
434
            self.branch, self.rqst.get('start_revision'),
 
435
            self.rqst.get('end_revision'))
 
436
        if self.rqst.get('_match_using_deltas'):
423
437
            return self._log_revision_iterator_using_delta_matching()
424
438
        else:
425
439
            # We're using the per-file-graph algorithm. This scales really
426
440
            # well but only makes sense if there is a single file and it's
427
441
            # not a directory
428
 
            file_count = len(self.rqst.specific_fileids)
 
442
            file_count = len(self.rqst.get('specific_fileids'))
429
443
            if file_count != 1:
430
444
                raise BzrError("illegal LogRequest: must match-using-deltas "
431
445
                    "when logging %d files" % file_count)
434
448
    def _log_revision_iterator_using_delta_matching(self):
435
449
        # Get the base revisions, filtering by the revision range
436
450
        rqst = self.rqst
437
 
        generate_merge_revisions = rqst.levels != 1
438
 
        delayed_graph_generation = not rqst.specific_fileids and (
439
 
                rqst.limit or self.start_rev_id or self.end_rev_id)
 
451
        generate_merge_revisions = rqst.get('levels') != 1
 
452
        delayed_graph_generation = not rqst.get('specific_fileids') and (
 
453
                rqst.get('limit') or self.start_rev_id or self.end_rev_id)
440
454
        view_revisions = _calc_view_revisions(self.branch, self.start_rev_id,
441
 
            self.end_rev_id, rqst.direction, generate_merge_revisions,
442
 
            rqst._allow_single_merge_revision,
 
455
            self.end_rev_id, rqst.get('direction'), generate_merge_revisions,
 
456
            rqst.get('_allow_single_merge_revision'),
443
457
            delayed_graph_generation=delayed_graph_generation)
444
458
 
445
459
        # Apply the other filters
446
460
        return make_log_rev_iterator(self.branch, view_revisions,
447
 
            rqst.delta_type, rqst.message_search,
448
 
            file_ids=rqst.specific_fileids, direction=rqst.direction)
 
461
            rqst.get('delta_type'), rqst.get('message_search'),
 
462
            file_ids=rqst.get('specific_fileids'),
 
463
            direction=rqst.get('direction'))
449
464
 
450
465
    def _log_revision_iterator_using_per_file_graph(self):
451
466
        # Get the base revisions, filtering by the revision range.
453
468
        # filter_revisions_touching_file_id() requires them ...
454
469
        rqst = self.rqst
455
470
        view_revisions = _calc_view_revisions(self.branch, self.start_rev_id,
456
 
            self.end_rev_id, rqst.direction, True,
457
 
            rqst._allow_single_merge_revision)
 
471
            self.end_rev_id, rqst.get('direction'), True,
 
472
            rqst.get('_allow_single_merge_revision'))
458
473
        if not isinstance(view_revisions, list):
459
474
            view_revisions = list(view_revisions)
460
475
        view_revisions = _filter_revisions_touching_file_id(self.branch,
461
 
            rqst.specific_fileids[0], view_revisions,
462
 
            include_merges=rqst.levels != 1)
 
476
            rqst.get('specific_fileids')[0], view_revisions,
 
477
            include_merges=rqst.get('levels') != 1)
463
478
        return make_log_rev_iterator(self.branch, view_revisions,
464
 
            rqst.delta_type, rqst.message_search)
 
479
            rqst.get('delta_type'), rqst.get('message_search'))
465
480
 
466
481
 
467
482
def _calc_view_revisions(branch, start_rev_id, end_rev_id, direction,