~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/log.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2011-07-12 15:43:28 UTC
  • mfrom: (6022.1.4 bzr-log-author)
  • Revision ID: pqm@pqm.ubuntu.com-20110712154328-y17bvllaw2rh0997
(jr) bzr log -m now matches message, author, committer and bugs instead
 of just matching the message.  --message keeps its original meaning,
 while --match-message, --match-author, --match-committer and
 --match-bugs match each of those fields. (Jonathan Riddell)

Show diffs side-by-side

added added

removed removed

Lines of Context:
157
157
             end_revision=None,
158
158
             search=None,
159
159
             limit=None,
160
 
             show_diff=False):
 
160
             show_diff=False,
 
161
             match=None):
161
162
    """Write out human-readable log of commits to this branch.
162
163
 
163
164
    This function is being retained for backwards compatibility but
186
187
        if None or 0.
187
188
 
188
189
    :param show_diff: If True, output a diff after each revision.
 
190
    
 
191
    :param match: Dictionary of search lists to use when matching revision
 
192
      properties.
189
193
    """
190
194
    # Convert old-style parameters to new-style parameters
191
195
    if specific_fileid is not None:
231
235
                          message_search=None, levels=1, generate_tags=True,
232
236
                          delta_type=None,
233
237
                          diff_type=None, _match_using_deltas=True,
234
 
                          exclude_common_ancestry=False,
 
238
                          exclude_common_ancestry=False, match=None,
235
239
                          signature=False,
236
240
                          ):
237
241
    """Convenience function for making a logging request dictionary.
282
286
      range operator or as a graph difference.
283
287
 
284
288
    :param signature: show digital signature information
 
289
      
 
290
    :param match: Dictionary of list of search strings to use when filtering
 
291
      revisions. Keys can be 'message', 'author', 'committer', 'bugs' or
 
292
      the empty string to match any of the preceding properties. 
 
293
      
285
294
    """
 
295
    
 
296
    # Take care of old style message_search parameter
 
297
    if message_search:
 
298
        if match:
 
299
            if 'message' in match:
 
300
                match['message'].append(message_search)
 
301
            else:
 
302
                match['message'] = [message_search]
 
303
        else:
 
304
            match={ 'message': [message_search] }
 
305
        
286
306
    return {
287
307
        'direction': direction,
288
308
        'specific_fileids': specific_fileids,
289
309
        'start_revision': start_revision,
290
310
        'end_revision': end_revision,
291
311
        'limit': limit,
292
 
        'message_search': message_search,
293
312
        'levels': levels,
294
313
        'generate_tags': generate_tags,
295
314
        'delta_type': delta_type,
296
315
        'diff_type': diff_type,
297
316
        'exclude_common_ancestry': exclude_common_ancestry,
298
317
        'signature': signature,
 
318
        'match': match,
299
319
        # Add 'private' attributes for features that may be deprecated
300
320
        '_match_using_deltas': _match_using_deltas,
301
321
    }
507
527
 
508
528
        # Apply the other filters
509
529
        return make_log_rev_iterator(self.branch, view_revisions,
510
 
            rqst.get('delta_type'), rqst.get('message_search'),
 
530
            rqst.get('delta_type'), rqst.get('match'),
511
531
            file_ids=rqst.get('specific_fileids'),
512
532
            direction=rqst.get('direction'))
513
533
 
526
546
            rqst.get('specific_fileids')[0], view_revisions,
527
547
            include_merges=rqst.get('levels') != 1)
528
548
        return make_log_rev_iterator(self.branch, view_revisions,
529
 
            rqst.get('delta_type'), rqst.get('message_search'))
 
549
            rqst.get('delta_type'), rqst.get('match'))
530
550
 
531
551
 
532
552
def _calc_view_revisions(branch, start_rev_id, end_rev_id, direction,
851
871
    return log_rev_iterator
852
872
 
853
873
 
854
 
def _make_search_filter(branch, generate_delta, search, log_rev_iterator):
 
874
def _make_search_filter(branch, generate_delta, match, log_rev_iterator):
855
875
    """Create a filtered iterator of log_rev_iterator matching on a regex.
856
876
 
857
877
    :param branch: The branch being logged.
858
878
    :param generate_delta: Whether to generate a delta for each revision.
859
 
    :param search: A user text search string.
 
879
    :param match: A dictionary with properties as keys and lists of strings
 
880
        as values. To match, a revision may match any of the supplied strings
 
881
        within a single property but must match at least one string for each
 
882
        property.
860
883
    :param log_rev_iterator: An input iterator containing all revisions that
861
884
        could be displayed, in lists.
862
885
    :return: An iterator over lists of ((rev_id, revno, merge_depth), rev,
863
886
        delta).
864
887
    """
865
 
    if search is None:
 
888
    if match is None:
866
889
        return log_rev_iterator
867
 
    searchRE = lazy_regex.lazy_compile(search, re.IGNORECASE)
868
 
    return _filter_message_re(searchRE, log_rev_iterator)
869
 
 
870
 
 
871
 
def _filter_message_re(searchRE, log_rev_iterator):
 
890
    searchRE = [(k, [re.compile(x, re.IGNORECASE) for x in v]) 
 
891
                for (k,v) in match.iteritems()]
 
892
    return _filter_re(searchRE, log_rev_iterator)
 
893
 
 
894
 
 
895
def _filter_re(searchRE, log_rev_iterator):
872
896
    for revs in log_rev_iterator:
873
 
        new_revs = []
874
 
        for (rev_id, revno, merge_depth), rev, delta in revs:
875
 
            if searchRE.search(rev.message):
876
 
                new_revs.append(((rev_id, revno, merge_depth), rev, delta))
877
 
        yield new_revs
878
 
 
 
897
        new_revs = [rev for rev in revs if _match_filter(searchRE, rev[1])]
 
898
        if new_revs:
 
899
            yield new_revs
 
900
 
 
901
def _match_filter(searchRE, rev):
 
902
    strings = {
 
903
               'message': (rev.message,),
 
904
               'committer': (rev.committer,),
 
905
               'author': (rev.get_apparent_authors()),
 
906
               'bugs': list(rev.iter_bugs())
 
907
               }
 
908
    strings[''] = [item for inner_list in strings.itervalues() 
 
909
                   for item in inner_list]
 
910
    
 
911
    for (k,v) in searchRE:
 
912
        if k in strings and not _match_any_filter(strings[k], v):
 
913
            return False
 
914
    return True
 
915
 
 
916
def _match_any_filter(strings, res):
 
917
    return any([filter(None, map(re.search, strings)) for re in res])
879
918
 
880
919
def _make_delta_filter(branch, generate_delta, search, log_rev_iterator,
881
920
    fileids=None, direction='reverse'):
1001
1040
    :return: An iterator over lists of ((rev_id, revno, merge_depth), rev,
1002
1041
        delta).
1003
1042
    """
1004
 
    repository = branch.repository
1005
1043
    num = 9
1006
1044
    for batch in log_rev_iterator:
1007
1045
        batch = iter(batch)