~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/log.py

  • Committer: Jelmer Vernooij
  • Date: 2011-05-03 23:16:56 UTC
  • mto: This revision was merged to the branch mainline in revision 5819.
  • Revision ID: jelmer@samba.org-20110503231656-4sxm717yejm8cpuw
Add tariff test to make sure working tree isn't opened by 'bzr serve'
that simply accesses a branch.

Show diffs side-by-side

added added

removed removed

Lines of Context:
74
74
    revision as _mod_revision,
75
75
    revisionspec,
76
76
    tsort,
77
 
    i18n,
78
77
    )
79
78
""")
80
79
 
81
80
from bzrlib import (
82
 
    lazy_regex,
83
81
    registry,
84
82
    )
85
83
from bzrlib.osutils import (
111
109
    revno = 1
112
110
    for revision_id in branch.revision_history():
113
111
        this_inv = branch.repository.get_inventory(revision_id)
114
 
        if this_inv.has_id(file_id):
 
112
        if file_id in this_inv:
115
113
            this_ie = this_inv[file_id]
116
114
            this_path = this_inv.id2path(file_id)
117
115
        else:
232
230
                          delta_type=None,
233
231
                          diff_type=None, _match_using_deltas=True,
234
232
                          exclude_common_ancestry=False,
235
 
                          signature=False,
236
233
                          ):
237
234
    """Convenience function for making a logging request dictionary.
238
235
 
262
259
      generate; 1 for just the mainline; 0 for all levels.
263
260
 
264
261
    :param generate_tags: If True, include tags for matched revisions.
265
 
`
 
262
 
266
263
    :param delta_type: Either 'full', 'partial' or None.
267
264
      'full' means generate the complete delta - adds/deletes/modifies/etc;
268
265
      'partial' means filter the delta using specific_fileids;
280
277
 
281
278
    :param exclude_common_ancestry: Whether -rX..Y should be interpreted as a
282
279
      range operator or as a graph difference.
283
 
 
284
 
    :param signature: show digital signature information
285
280
    """
286
281
    return {
287
282
        'direction': direction,
295
290
        'delta_type': delta_type,
296
291
        'diff_type': diff_type,
297
292
        'exclude_common_ancestry': exclude_common_ancestry,
298
 
        'signature': signature,
299
293
        # Add 'private' attributes for features that may be deprecated
300
294
        '_match_using_deltas': _match_using_deltas,
301
295
    }
309
303
    return result
310
304
 
311
305
 
312
 
def format_signature_validity(rev_id, repo):
313
 
    """get the signature validity
314
 
    
315
 
    :param rev_id: revision id to validate
316
 
    :param repo: repository of revision
317
 
    :return: human readable string to print to log
318
 
    """
319
 
    from bzrlib import gpg
320
 
 
321
 
    gpg_strategy = gpg.GPGStrategy(None)
322
 
    result = repo.verify_revision(rev_id, gpg_strategy)
323
 
    if result[0] == gpg.SIGNATURE_VALID:
324
 
        return "valid signature from {0}".format(result[1])
325
 
    if result[0] == gpg.SIGNATURE_KEY_MISSING:
326
 
        return "unknown key {0}".format(result[1])
327
 
    if result[0] == gpg.SIGNATURE_NOT_VALID:
328
 
        return "invalid signature!"
329
 
    if result[0] == gpg.SIGNATURE_NOT_SIGNED:
330
 
        return "no signature"
331
 
 
332
 
 
333
306
class LogGenerator(object):
334
307
    """A generator of log revisions."""
335
308
 
387
360
            rqst['delta_type'] = None
388
361
        if not getattr(lf, 'supports_diff', False):
389
362
            rqst['diff_type'] = None
390
 
        if not getattr(lf, 'supports_signatures', False):
391
 
            rqst['signature'] = False
392
363
 
393
364
        # Find and print the interesting revisions
394
365
        generator = self._generator_factory(self.branch, rqst)
428
399
        levels = rqst.get('levels')
429
400
        limit = rqst.get('limit')
430
401
        diff_type = rqst.get('diff_type')
431
 
        show_signature = rqst.get('signature')
432
402
        log_count = 0
433
403
        revision_iterator = self._create_log_revision_iterator()
434
404
        for revs in revision_iterator:
440
410
                    diff = None
441
411
                else:
442
412
                    diff = self._format_diff(rev, rev_id, diff_type)
443
 
                if show_signature:
444
 
                    signature = format_signature_validity(rev_id,
445
 
                                                self.branch.repository)
446
 
                else:
447
 
                    signature = None
448
413
                yield LogRevision(rev, revno, merge_depth, delta,
449
 
                    self.rev_tag_dict.get(rev_id), diff, signature)
 
414
                    self.rev_tag_dict.get(rev_id), diff)
450
415
                if limit:
451
416
                    log_count += 1
452
417
                    if log_count >= limit:
713
678
    """
714
679
    br_revno, br_rev_id = branch.last_revision_info()
715
680
    repo = branch.repository
716
 
    graph = repo.get_graph()
717
681
    if start_rev_id is None and end_rev_id is None:
718
682
        cur_revno = br_revno
719
 
        for revision_id in graph.iter_lefthand_ancestry(br_rev_id,
720
 
            (_mod_revision.NULL_REVISION,)):
 
683
        for revision_id in repo.iter_reverse_revision_history(br_rev_id):
721
684
            yield revision_id, str(cur_revno), 0
722
685
            cur_revno -= 1
723
686
    else:
724
687
        if end_rev_id is None:
725
688
            end_rev_id = br_rev_id
726
689
        found_start = start_rev_id is None
727
 
        for revision_id in graph.iter_lefthand_ancestry(end_rev_id,
728
 
                (_mod_revision.NULL_REVISION,)):
 
690
        for revision_id in repo.iter_reverse_revision_history(end_rev_id):
729
691
            revno_str = _compute_revno_str(branch, revision_id)
730
692
            if not found_start and revision_id == start_rev_id:
731
693
                if not exclude_common_ancestry:
864
826
    """
865
827
    if search is None:
866
828
        return log_rev_iterator
867
 
    searchRE = lazy_regex.lazy_compile(search, re.IGNORECASE)
 
829
    searchRE = re.compile(search, re.IGNORECASE)
868
830
    return _filter_message_re(searchRE, log_rev_iterator)
869
831
 
870
832
 
1124
1086
    cur_revno = branch_revno
1125
1087
    rev_nos = {}
1126
1088
    mainline_revs = []
1127
 
    graph = branch.repository.get_graph()
1128
 
    for revision_id in graph.iter_lefthand_ancestry(
1129
 
            branch_last_revision, (_mod_revision.NULL_REVISION,)):
 
1089
    for revision_id in branch.repository.iter_reverse_revision_history(
 
1090
                        branch_last_revision):
1130
1091
        if cur_revno < start_revno:
1131
1092
            # We have gone far enough, but we always add 1 more revision
1132
1093
            rev_nos[revision_id] = cur_revno
1198
1159
    This includes the revisions which directly change the file id,
1199
1160
    and the revisions which merge these changes. So if the
1200
1161
    revision graph is::
1201
 
 
1202
1162
        A-.
1203
1163
        |\ \
1204
1164
        B C E
1231
1191
    """
1232
1192
    # Lookup all possible text keys to determine which ones actually modified
1233
1193
    # the file.
1234
 
    graph = branch.repository.get_file_graph()
1235
 
    get_parent_map = graph.get_parent_map
1236
1194
    text_keys = [(file_id, rev_id) for rev_id, revno, depth in view_revisions]
1237
1195
    next_keys = None
1238
1196
    # Looking up keys in batches of 1000 can cut the time in half, as well as
1242
1200
    #       indexing layer. We might consider passing in hints as to the known
1243
1201
    #       access pattern (sparse/clustered, high success rate/low success
1244
1202
    #       rate). This particular access is clustered with a low success rate.
 
1203
    get_parent_map = branch.repository.texts.get_parent_map
1245
1204
    modified_text_revisions = set()
1246
1205
    chunk_size = 1000
1247
1206
    for start in xrange(0, len(text_keys), chunk_size):
1355
1314
    """
1356
1315
 
1357
1316
    def __init__(self, rev=None, revno=None, merge_depth=0, delta=None,
1358
 
                 tags=None, diff=None, signature=None):
 
1317
                 tags=None, diff=None):
1359
1318
        self.rev = rev
1360
1319
        if revno is None:
1361
1320
            self.revno = None
1365
1324
        self.delta = delta
1366
1325
        self.tags = tags
1367
1326
        self.diff = diff
1368
 
        self.signature = signature
1369
1327
 
1370
1328
 
1371
1329
class LogFormatter(object):
1380
1338
    to indicate which LogRevision attributes it supports:
1381
1339
 
1382
1340
    - supports_delta must be True if this log formatter supports delta.
1383
 
      Otherwise the delta attribute may not be populated.  The 'delta_format'
1384
 
      attribute describes whether the 'short_status' format (1) or the long
1385
 
      one (2) should be used.
 
1341
        Otherwise the delta attribute may not be populated.  The 'delta_format'
 
1342
        attribute describes whether the 'short_status' format (1) or the long
 
1343
        one (2) should be used.
1386
1344
 
1387
1345
    - supports_merge_revisions must be True if this log formatter supports
1388
 
      merge revisions.  If not, then only mainline revisions will be passed
1389
 
      to the formatter.
 
1346
        merge revisions.  If not, then only mainline revisions will be passed
 
1347
        to the formatter.
1390
1348
 
1391
1349
    - preferred_levels is the number of levels this formatter defaults to.
1392
 
      The default value is zero meaning display all levels.
1393
 
      This value is only relevant if supports_merge_revisions is True.
 
1350
        The default value is zero meaning display all levels.
 
1351
        This value is only relevant if supports_merge_revisions is True.
1394
1352
 
1395
1353
    - supports_tags must be True if this log formatter supports tags.
1396
 
      Otherwise the tags attribute may not be populated.
 
1354
        Otherwise the tags attribute may not be populated.
1397
1355
 
1398
1356
    - supports_diff must be True if this log formatter supports diffs.
1399
 
      Otherwise the diff attribute may not be populated.
1400
 
 
1401
 
    - supports_signatures must be True if this log formatter supports GPG
1402
 
      signatures.
 
1357
        Otherwise the diff attribute may not be populated.
1403
1358
 
1404
1359
    Plugins can register functions to show custom revision properties using
1405
1360
    the properties_handler_registry. The registered function
1406
 
    must respect the following interface description::
1407
 
 
 
1361
    must respect the following interface description:
1408
1362
        def my_show_properties(properties_dict):
1409
1363
            # code that returns a dict {'name':'value'} of the properties
1410
1364
            # to be shown
1597
1551
    supports_delta = True
1598
1552
    supports_tags = True
1599
1553
    supports_diff = True
1600
 
    supports_signatures = True
1601
1554
 
1602
1555
    def __init__(self, *args, **kwargs):
1603
1556
        super(LongLogFormatter, self).__init__(*args, **kwargs)
1642
1595
 
1643
1596
        lines.append('timestamp: %s' % (self.date_string(revision.rev),))
1644
1597
 
1645
 
        if revision.signature is not None:
1646
 
            lines.append('signature: ' + revision.signature)
1647
 
 
1648
1598
        lines.append('message:')
1649
1599
        if not revision.rev.message:
1650
1600
            lines.append('  (no message)')
1777
1727
 
1778
1728
    def log_string(self, revno, rev, max_chars, tags=None, prefix=''):
1779
1729
        """Format log info into one string. Truncate tail of string
1780
 
 
1781
 
        :param revno:      revision number or None.
1782
 
                           Revision numbers counts from 1.
1783
 
        :param rev:        revision object
1784
 
        :param max_chars:  maximum length of resulting string
1785
 
        :param tags:       list of tags or None
1786
 
        :param prefix:     string to prefix each line
1787
 
        :return:           formatted truncated string
 
1730
        :param  revno:      revision number or None.
 
1731
                            Revision numbers counts from 1.
 
1732
        :param  rev:        revision object
 
1733
        :param  max_chars:  maximum length of resulting string
 
1734
        :param  tags:       list of tags or None
 
1735
        :param  prefix:     string to prefix each line
 
1736
        :return:            formatted truncated string
1788
1737
        """
1789
1738
        out = []
1790
1739
        if revno:
1791
1740
            # show revno only when is not None
1792
1741
            out.append("%s:" % revno)
1793
 
        if max_chars is not None:
1794
 
            out.append(self.truncate(self.short_author(rev), (max_chars+3)/4))
1795
 
        else:
1796
 
            out.append(self.short_author(rev))
 
1742
        out.append(self.truncate(self.short_author(rev), 20))
1797
1743
        out.append(self.date_string(rev))
1798
1744
        if len(rev.parent_ids) > 1:
1799
1745
            out.append('[merge]')
1992
1938
    old_revisions = set()
1993
1939
    new_history = []
1994
1940
    new_revisions = set()
1995
 
    graph = repository.get_graph()
1996
 
    new_iter = graph.iter_lefthand_ancestry(new_revision_id)
1997
 
    old_iter = graph.iter_lefthand_ancestry(old_revision_id)
 
1941
    new_iter = repository.iter_reverse_revision_history(new_revision_id)
 
1942
    old_iter = repository.iter_reverse_revision_history(old_revision_id)
1998
1943
    stop_revision = None
1999
1944
    do_old = True
2000
1945
    do_new = True