Rev 6024: (jr) bzr log -m now matches message, author, committer and bugs instead in file:///home/pqm/archives/thelove/bzr/%2Btrunk/
Canonical.com Patch Queue Manager
pqm at pqm.ubuntu.com
Tue Jul 12 15:43:31 UTC 2011
At file:///home/pqm/archives/thelove/bzr/%2Btrunk/
------------------------------------------------------------
revno: 6024 [merge]
revision-id: pqm at pqm.ubuntu.com-20110712154328-y17bvllaw2rh0997
parent: pqm at pqm.ubuntu.com-20110712141118-09vc4hfybefzgbb2
parent: jriddell at canonical.com-20110712143006-bx2m5wr62yew0ks6
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Tue 2011-07-12 15:43:28 +0000
message:
(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)
modified:
bzrlib/builtins.py builtins.py-20050830033751-fc01482b9ca23183
bzrlib/log.py log.py-20050505065812-c40ce11702fe5fb1
bzrlib/tests/blackbox/test_log.py test_log.py-20060112090212-78f6ea560c868e24
doc/en/release-notes/bzr-2.5.txt bzr2.5.txt-20110708125756-587p0hpw7oke4h05-1
=== modified file 'bzrlib/builtins.py'
--- a/bzrlib/builtins.py 2011-07-06 13:30:21 +0000
+++ b/bzrlib/builtins.py 2011-07-12 11:09:57 +0000
@@ -2317,8 +2317,11 @@
:Other filtering:
- The --message option can be used for finding revisions that match a
- regular expression in a commit message.
+ The --match option can be used for finding revisions that match a
+ regular expression in a commit message, committer, author or bug.
+ Specifying the option several times will match any of the supplied
+ expressions. --match-author, --match-bugs, --match-committer and
+ --match-message can be used to only match a specific field.
:Tips & tricks:
@@ -2384,10 +2387,10 @@
argname='N',
type=_parse_levels),
Option('message',
- short_name='m',
help='Show revisions whose message matches this '
'regular expression.',
- type=str),
+ type=str,
+ hidden=True),
Option('limit',
short_name='l',
help='Limit the output to the first N revisions.',
@@ -2404,6 +2407,27 @@
),
Option('signatures',
help='Show digital signature validity'),
+ ListOption('match',
+ short_name='m',
+ help='Show revisions whose properties match this '
+ 'expression.',
+ type=str),
+ ListOption('match-message',
+ help='Show revisions whose message matches this '
+ 'expression.',
+ type=str),
+ ListOption('match-committer',
+ help='Show revisions whose committer matches this '
+ 'expression.',
+ type=str),
+ ListOption('match-author',
+ help='Show revisions whose authors match this '
+ 'expression.',
+ type=str),
+ ListOption('match-bugs',
+ help='Show revisions whose bugs match this '
+ 'expression.',
+ type=str)
]
encoding_type = 'replace'
@@ -2423,6 +2447,11 @@
authors=None,
exclude_common_ancestry=False,
signatures=False,
+ match=None,
+ match_message=None,
+ match_committer=None,
+ match_author=None,
+ match_bugs=None,
):
from bzrlib.log import (
Logger,
@@ -2531,6 +2560,18 @@
match_using_deltas = (len(file_ids) != 1 or filter_by_dir
or delta_type or partial_history)
+ match_dict = {}
+ if match:
+ match_dict[''] = match
+ if match_message:
+ match_dict['message'] = match_message
+ if match_committer:
+ match_dict['committer'] = match_committer
+ if match_author:
+ match_dict['author'] = match_author
+ if match_bugs:
+ match_dict['bugs'] = match_bugs
+
# Build the LogRequest and execute it
if len(file_ids) == 0:
file_ids = None
@@ -2539,7 +2580,7 @@
start_revision=rev1, end_revision=rev2, limit=limit,
message_search=message, delta_type=delta_type,
diff_type=diff_type, _match_using_deltas=match_using_deltas,
- exclude_common_ancestry=exclude_common_ancestry,
+ exclude_common_ancestry=exclude_common_ancestry, match=match_dict,
signature=signatures
)
Logger(b, rqst).show(lf)
=== modified file 'bzrlib/log.py'
--- a/bzrlib/log.py 2011-06-30 16:48:11 +0000
+++ b/bzrlib/log.py 2011-07-12 11:09:57 +0000
@@ -157,7 +157,8 @@
end_revision=None,
search=None,
limit=None,
- show_diff=False):
+ show_diff=False,
+ match=None):
"""Write out human-readable log of commits to this branch.
This function is being retained for backwards compatibility but
@@ -186,6 +187,9 @@
if None or 0.
:param show_diff: If True, output a diff after each revision.
+
+ :param match: Dictionary of search lists to use when matching revision
+ properties.
"""
# Convert old-style parameters to new-style parameters
if specific_fileid is not None:
@@ -231,7 +235,7 @@
message_search=None, levels=1, generate_tags=True,
delta_type=None,
diff_type=None, _match_using_deltas=True,
- exclude_common_ancestry=False,
+ exclude_common_ancestry=False, match=None,
signature=False,
):
"""Convenience function for making a logging request dictionary.
@@ -282,20 +286,36 @@
range operator or as a graph difference.
:param signature: show digital signature information
+
+ :param match: Dictionary of list of search strings to use when filtering
+ revisions. Keys can be 'message', 'author', 'committer', 'bugs' or
+ the empty string to match any of the preceding properties.
+
"""
+
+ # Take care of old style message_search parameter
+ if message_search:
+ if match:
+ if 'message' in match:
+ match['message'].append(message_search)
+ else:
+ match['message'] = [message_search]
+ else:
+ match={ 'message': [message_search] }
+
return {
'direction': direction,
'specific_fileids': specific_fileids,
'start_revision': start_revision,
'end_revision': end_revision,
'limit': limit,
- 'message_search': message_search,
'levels': levels,
'generate_tags': generate_tags,
'delta_type': delta_type,
'diff_type': diff_type,
'exclude_common_ancestry': exclude_common_ancestry,
'signature': signature,
+ 'match': match,
# Add 'private' attributes for features that may be deprecated
'_match_using_deltas': _match_using_deltas,
}
@@ -507,7 +527,7 @@
# Apply the other filters
return make_log_rev_iterator(self.branch, view_revisions,
- rqst.get('delta_type'), rqst.get('message_search'),
+ rqst.get('delta_type'), rqst.get('match'),
file_ids=rqst.get('specific_fileids'),
direction=rqst.get('direction'))
@@ -526,7 +546,7 @@
rqst.get('specific_fileids')[0], view_revisions,
include_merges=rqst.get('levels') != 1)
return make_log_rev_iterator(self.branch, view_revisions,
- rqst.get('delta_type'), rqst.get('message_search'))
+ rqst.get('delta_type'), rqst.get('match'))
def _calc_view_revisions(branch, start_rev_id, end_rev_id, direction,
@@ -851,31 +871,50 @@
return log_rev_iterator
-def _make_search_filter(branch, generate_delta, search, log_rev_iterator):
+def _make_search_filter(branch, generate_delta, match, log_rev_iterator):
"""Create a filtered iterator of log_rev_iterator matching on a regex.
:param branch: The branch being logged.
:param generate_delta: Whether to generate a delta for each revision.
- :param search: A user text search string.
+ :param match: A dictionary with properties as keys and lists of strings
+ as values. To match, a revision may match any of the supplied strings
+ within a single property but must match at least one string for each
+ property.
:param log_rev_iterator: An input iterator containing all revisions that
could be displayed, in lists.
:return: An iterator over lists of ((rev_id, revno, merge_depth), rev,
delta).
"""
- if search is None:
+ if match is None:
return log_rev_iterator
- searchRE = lazy_regex.lazy_compile(search, re.IGNORECASE)
- return _filter_message_re(searchRE, log_rev_iterator)
-
-
-def _filter_message_re(searchRE, log_rev_iterator):
+ searchRE = [(k, [re.compile(x, re.IGNORECASE) for x in v])
+ for (k,v) in match.iteritems()]
+ return _filter_re(searchRE, log_rev_iterator)
+
+
+def _filter_re(searchRE, log_rev_iterator):
for revs in log_rev_iterator:
- new_revs = []
- for (rev_id, revno, merge_depth), rev, delta in revs:
- if searchRE.search(rev.message):
- new_revs.append(((rev_id, revno, merge_depth), rev, delta))
- yield new_revs
-
+ new_revs = [rev for rev in revs if _match_filter(searchRE, rev[1])]
+ if new_revs:
+ yield new_revs
+
+def _match_filter(searchRE, rev):
+ strings = {
+ 'message': (rev.message,),
+ 'committer': (rev.committer,),
+ 'author': (rev.get_apparent_authors()),
+ 'bugs': list(rev.iter_bugs())
+ }
+ strings[''] = [item for inner_list in strings.itervalues()
+ for item in inner_list]
+
+ for (k,v) in searchRE:
+ if k in strings and not _match_any_filter(strings[k], v):
+ return False
+ return True
+
+def _match_any_filter(strings, res):
+ return any([filter(None, map(re.search, strings)) for re in res])
def _make_delta_filter(branch, generate_delta, search, log_rev_iterator,
fileids=None, direction='reverse'):
@@ -1001,7 +1040,6 @@
:return: An iterator over lists of ((rev_id, revno, merge_depth), rev,
delta).
"""
- repository = branch.repository
num = 9
for batch in log_rev_iterator:
batch = iter(batch)
=== modified file 'bzrlib/tests/blackbox/test_log.py'
--- a/bzrlib/tests/blackbox/test_log.py 2011-06-20 15:25:50 +0000
+++ b/bzrlib/tests/blackbox/test_log.py 2011-07-12 14:03:52 +0000
@@ -974,3 +974,58 @@
def test_log_range_open_end(self):
self.assertLogRevnos(["-r1.."], ["2", "1"])
+
+class TestLogMatch(TestLogWithLogCatcher):
+ def prepare_tree(self):
+ tree = self.make_branch_and_tree('')
+ self.build_tree(
+ ['/hello.txt', '/goodbye.txt'])
+ tree.add('hello.txt')
+ tree.commit(message='message1', committer='committer1', authors=['author1'])
+ tree.add('goodbye.txt')
+ tree.commit(message='message2', committer='committer2', authors=['author2'])
+
+ def test_message(self):
+ self.prepare_tree()
+ self.assertLogRevnos(["-m", "message1"], ["1"])
+ self.assertLogRevnos(["-m", "message2"], ["2"])
+ self.assertLogRevnos(["-m", "message"], ["2", "1"])
+ self.assertLogRevnos(["-m", "message1", "-m", "message2"], ["2", "1"])
+ self.assertLogRevnos(["--match-message", "message1"], ["1"])
+ self.assertLogRevnos(["--match-message", "message2"], ["2"])
+ self.assertLogRevnos(["--match-message", "message"], ["2", "1"])
+ self.assertLogRevnos(["--match-message", "message1",
+ "--match-message", "message2"], ["2", "1"])
+ self.assertLogRevnos(["--message", "message1"], ["1"])
+ self.assertLogRevnos(["--message", "message2"], ["2"])
+ self.assertLogRevnos(["--message", "message"], ["2", "1"])
+ self.assertLogRevnos(["--match-message", "message1",
+ "--message", "message2"], ["2", "1"])
+ self.assertLogRevnos(["--message", "message1",
+ "--match-message", "message2"], ["2", "1"])
+
+ def test_committer(self):
+ self.prepare_tree()
+ self.assertLogRevnos(["-m", "committer1"], ["1"])
+ self.assertLogRevnos(["-m", "committer2"], ["2"])
+ self.assertLogRevnos(["-m", "committer"], ["2", "1"])
+ self.assertLogRevnos(["-m", "committer1", "-m", "committer2"],
+ ["2", "1"])
+ self.assertLogRevnos(["--match-committer", "committer1"], ["1"])
+ self.assertLogRevnos(["--match-committer", "committer2"], ["2"])
+ self.assertLogRevnos(["--match-committer", "committer"], ["2", "1"])
+ self.assertLogRevnos(["--match-committer", "committer1",
+ "--match-committer", "committer2"], ["2", "1"])
+
+ def test_author(self):
+ self.prepare_tree()
+ self.assertLogRevnos(["-m", "author1"], ["1"])
+ self.assertLogRevnos(["-m", "author2"], ["2"])
+ self.assertLogRevnos(["-m", "author"], ["2", "1"])
+ self.assertLogRevnos(["-m", "author1", "-m", "author2"],
+ ["2", "1"])
+ self.assertLogRevnos(["--match-author", "author1"], ["1"])
+ self.assertLogRevnos(["--match-author", "author2"], ["2"])
+ self.assertLogRevnos(["--match-author", "author"], ["2", "1"])
+ self.assertLogRevnos(["--match-author", "author1",
+ "--match-author", "author2"], ["2", "1"])
=== modified file 'doc/en/release-notes/bzr-2.5.txt'
--- a/doc/en/release-notes/bzr-2.5.txt 2011-07-12 11:48:45 +0000
+++ b/doc/en/release-notes/bzr-2.5.txt 2011-07-12 14:30:06 +0000
@@ -20,6 +20,11 @@
.. New commands, options, etc that users may wish to try out.
+* 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.
+
* Add a config option gpg_signature_key for setting which GPG key
should be used to sign commits. Also default to using the gpg user
identity which matches user_email() as set by whoami.
More information about the bazaar-commits
mailing list