Mercurial > public > mercurial-scm > hg
comparison mercurial/revset.py @ 16096:b8be450638f6
revset: fix alias substitution recursion (issue3240)
The revset aliases expansion worked like:
expr = "some revset"
for alias in aliases:
expr = alias.process(expr)
where "process" was replacing the alias with its *unexpanded* substitution,
recursively. So it only worked when aliases were applied in proper dependency
order.
This patch rewrites the expansion process so all aliases are expanded
recursively at every tree level, after parent alias rewriting and variable
expansion.
author | Patrick Mezard <patrick@mezard.eu> |
---|---|
date | Thu, 09 Feb 2012 21:03:07 +0100 |
parents | f06c53ca59a9 |
children | 5a627b49b4d9 |
comparison
equal
deleted
inserted
replaced
16095:3eab42088be4 | 16096:b8be450638f6 |
---|---|
1069 '''Aliases like: | 1069 '''Aliases like: |
1070 | 1070 |
1071 h = heads(default) | 1071 h = heads(default) |
1072 b($1) = ancestors($1) - ancestors(default) | 1072 b($1) = ancestors($1) - ancestors(default) |
1073 ''' | 1073 ''' |
1074 if isinstance(name, tuple): # parameter substitution | 1074 m = self.funcre.search(name) |
1075 self.tree = name | 1075 if m: |
1076 self.replacement = value | 1076 self.name = m.group(1) |
1077 else: # alias definition | 1077 self.tree = ('func', ('symbol', m.group(1))) |
1078 m = self.funcre.search(name) | 1078 self.args = [x.strip() for x in m.group(2).split(',')] |
1079 if m: | 1079 for arg in self.args: |
1080 self.tree = ('func', ('symbol', m.group(1))) | 1080 value = value.replace(arg, repr(arg)) |
1081 self.args = [x.strip() for x in m.group(2).split(',')] | 1081 else: |
1082 for arg in self.args: | 1082 self.name = name |
1083 value = value.replace(arg, repr(arg)) | 1083 self.tree = ('symbol', name) |
1084 else: | 1084 |
1085 self.tree = ('symbol', name) | 1085 self.replacement, pos = parse(value) |
1086 | 1086 if pos != len(value): |
1087 self.replacement, pos = parse(value) | 1087 raise error.ParseError(_('invalid token'), pos) |
1088 if pos != len(value): | 1088 |
1089 raise error.ParseError(_('invalid token'), pos) | 1089 def _getalias(aliases, tree): |
1090 | 1090 """If tree looks like an unexpanded alias, return it. Return None |
1091 def process(self, tree): | 1091 otherwise. |
1092 if isinstance(tree, tuple): | 1092 """ |
1093 if self.args is None: | 1093 if isinstance(tree, tuple) and tree: |
1094 if tree == self.tree: | 1094 if tree[0] == 'symbol' and len(tree) == 2: |
1095 return self.replacement | 1095 name = tree[1] |
1096 elif tree[:2] == self.tree: | 1096 alias = aliases.get(name) |
1097 l = getlist(tree[2]) | 1097 if alias and alias.args is None and alias.tree == tree: |
1098 if len(l) != len(self.args): | 1098 return alias |
1099 raise error.ParseError( | 1099 if tree[0] == 'func' and len(tree) > 1: |
1100 _('invalid number of arguments: %s') % len(l)) | 1100 if tree[1][0] == 'symbol' and len(tree[1]) == 2: |
1101 result = self.replacement | 1101 name = tree[1][1] |
1102 for a, v in zip(self.args, l): | 1102 alias = aliases.get(name) |
1103 valalias = revsetalias(('string', a), v) | 1103 if alias and alias.args is not None and alias.tree == tree[:2]: |
1104 result = valalias.process(result) | 1104 return alias |
1105 return result | 1105 return None |
1106 return tuple(map(self.process, tree)) | 1106 |
1107 def _expandargs(tree, args): | |
1108 """Replace all occurences of ('string', name) with the | |
1109 substitution value of the same name in args, recursively. | |
1110 """ | |
1111 if not isinstance(tree, tuple): | |
1107 return tree | 1112 return tree |
1113 if len(tree) == 2 and tree[0] == 'string': | |
1114 return args.get(tree[1], tree) | |
1115 return tuple(_expandargs(t, args) for t in tree) | |
1116 | |
1117 def _expandaliases(aliases, tree, expanding): | |
1118 """Expand aliases in tree, recursively. | |
1119 | |
1120 'aliases' is a dictionary mapping user defined aliases to | |
1121 revsetalias objects. | |
1122 """ | |
1123 if not isinstance(tree, tuple): | |
1124 # Do not expand raw strings | |
1125 return tree | |
1126 alias = _getalias(aliases, tree) | |
1127 if alias is not None: | |
1128 if alias in expanding: | |
1129 raise error.ParseError(_('infinite expansion of revset alias "%s" ' | |
1130 'detected') % alias.name) | |
1131 expanding.append(alias) | |
1132 result = alias.replacement | |
1133 if alias.args is not None: | |
1134 l = getlist(tree[2]) | |
1135 if len(l) != len(alias.args): | |
1136 raise error.ParseError( | |
1137 _('invalid number of arguments: %s') % len(l)) | |
1138 result = _expandargs(result, dict(zip(alias.args, l))) | |
1139 # Recurse in place, the base expression may have been rewritten | |
1140 result = _expandaliases(aliases, result, expanding) | |
1141 expanding.pop() | |
1142 else: | |
1143 result = tuple(_expandaliases(aliases, t, expanding) | |
1144 for t in tree) | |
1145 return result | |
1108 | 1146 |
1109 def findaliases(ui, tree): | 1147 def findaliases(ui, tree): |
1148 aliases = {} | |
1110 for k, v in ui.configitems('revsetalias'): | 1149 for k, v in ui.configitems('revsetalias'): |
1111 alias = revsetalias(k, v) | 1150 alias = revsetalias(k, v) |
1112 tree = alias.process(tree) | 1151 aliases[alias.name] = alias |
1113 return tree | 1152 return _expandaliases(aliases, tree, []) |
1114 | 1153 |
1115 parse = parser.parser(tokenize, elements).parse | 1154 parse = parser.parser(tokenize, elements).parse |
1116 | 1155 |
1117 def match(ui, spec): | 1156 def match(ui, spec): |
1118 if not spec: | 1157 if not spec: |