21 except (ImportError, AttributeError): |
21 except (ImportError, AttributeError): |
22 mkmatcher = util.stringmatcher |
22 mkmatcher = util.stringmatcher |
23 |
23 |
24 revsetpredicate = registrar.revsetpredicate() |
24 revsetpredicate = registrar.revsetpredicate() |
25 |
25 |
26 @revsetpredicate('topic([topic])') |
26 def getstringstrict(x, err): |
|
27 if x and (x[0] == 'string'): |
|
28 return x[1] |
|
29 raise error.ParseError(err) |
|
30 |
|
31 @revsetpredicate('topic([string or set])') |
27 def topicset(repo, subset, x): |
32 def topicset(repo, subset, x): |
28 """Specified topic or all changes with any topic specified. |
33 """All changesets with the specified topic or the topics of the given |
|
34 changesets. Without the argument, all changesets with any topic specified. |
29 |
35 |
30 If `topic` starts with `re:` the remainder of the name is treated |
36 If `string` starts with `re:` the remainder of the name is treated |
31 as a regular expression. |
37 as a regular expression. |
32 |
|
33 TODO: make `topic(revset)` work the same as `branch(revset)`. |
|
34 """ |
38 """ |
35 args = revset.getargs(x, 0, 1, 'topic takes one or no arguments') |
39 args = revset.getargs(x, 0, 1, 'topic takes one or no arguments') |
36 if args: |
|
37 # match a specific topic |
|
38 topic = revset.getstring(args[0], 'topic() argument must be a string') |
|
39 if topic == '.': |
|
40 topic = repo['.'].extra().get('topic', '') |
|
41 _kind, _pattern, matcher = mkmatcher(topic) |
|
42 else: |
|
43 matcher = lambda t: bool(t) |
|
44 |
40 |
45 mutable = revset._notpublic(repo, revset.fullreposet(repo), ()) |
41 mutable = revset._notpublic(repo, revset.fullreposet(repo), ()) |
46 |
42 |
47 rawchange = repo.changelog.changelogrevision |
43 if not args: |
48 key = constants.extrakey |
44 return (subset & mutable).filter(lambda r: bool(repo[r].topic())) |
49 |
45 |
50 def matchtopic(r): |
46 try: |
51 topic = rawchange(r).extra.get(key) |
47 topic = getstringstrict(args[0], '') |
52 if topic is None: |
48 except error.ParseError: |
|
49 # not a string, but another revset |
|
50 pass |
|
51 else: |
|
52 kind, pattern, matcher = mkmatcher(topic) |
|
53 |
|
54 def matches(r): |
|
55 topic = repo[r].topic() |
|
56 if not topic: |
|
57 return False |
|
58 return matcher(topic) |
|
59 |
|
60 if kind == 'literal': |
|
61 # note: falls through to the revset case if no topic with this name |
|
62 # exists and pattern kind is not specified explicitly |
|
63 |
|
64 if pattern not in repo.topics and topic.startswith('literal:'): |
|
65 raise error.RepoLookupError("topic '%s' does not exist" |
|
66 % pattern) |
|
67 return (subset & mutable).filter(matches) |
|
68 else: |
|
69 return (subset & mutable).filter(matches) |
|
70 |
|
71 s = revset.getset(repo, revset.fullreposet(repo), x) |
|
72 topics = set(repo[r].topic() for r in s) |
|
73 topics.discard('') |
|
74 |
|
75 def matches(r): |
|
76 if r in s: |
|
77 return True |
|
78 topic = repo[r].topic() |
|
79 if not topic: |
53 return False |
80 return False |
54 return matcher(topic) |
81 return topic in topics |
55 return (subset & mutable).filter(matchtopic) |
82 |
|
83 return (subset & mutable).filter(matches) |
56 |
84 |
57 @revsetpredicate('ngtip([branch])') |
85 @revsetpredicate('ngtip([branch])') |
58 def ngtipset(repo, subset, x): |
86 def ngtipset(repo, subset, x): |
59 """The untopiced tip. |
87 """The untopiced tip. |
60 |
88 |