comparison hgext/histedit.py @ 27082:4898e442f392

histedit: make verification configurable Before we can add a 'base' action to histedit need to change verification so that action can specify which steps of verification should run for it. Also it's everything we need for the exec and stop actions implementation. I thought about baking verification into each histedit action (so each of them is responsible for verifying its constraints) but it felt wrong because: - every action would need to know its context (eg. the list of all other actions) - a lot of duplicated work will be added - each action will iterate through all others - the steps of the verification would need to be extracted and named anyway in order to be reused The verifyrules function grows too big now. I plan to refator it in one of the next series.
author Mateusz Kwapich <mitrandir@fb.com>
date Tue, 17 Nov 2015 16:37:26 -0800
parents 1168499e5266
children 6d5d7ac41ef4
comparison
equal deleted inserted replaced
27081:37290f2f2c3b 27082:4898e442f392
331 try: 331 try:
332 node = repo[rulehash].node() 332 node = repo[rulehash].node()
333 except error.RepoError: 333 except error.RepoError:
334 raise error.Abort(_('unknown changeset %s listed') % rulehash[:12]) 334 raise error.Abort(_('unknown changeset %s listed') % rulehash[:12])
335 return cls(state, node) 335 return cls(state, node)
336
337 def constraints(self):
338 """Return a set of constrains that this action should be verified for
339
340 Available constraints:
341 noduplicates - aborts if there are multiple rules for one node
342 noother - abort if the node doesn't belong to edited stack
343 """
344
345 return set(['noduplicates', 'noother'])
346
347 def nodetoverify(self):
348 """Returns a node associated with the action that will be used for
349 verification purposes.
350
351 If the action doesn't correspond to node it should return None
352 """
353 return self.node
336 354
337 def run(self): 355 def run(self):
338 """Runs the action. The default behavior is simply apply the action's 356 """Runs the action. The default behavior is simply apply the action's
339 rulectx onto the current parentctx.""" 357 rulectx onto the current parentctx."""
340 self.applychange() 358 self.applychange()
808 f = open(rules) 826 f = open(rules)
809 rules = f.read() 827 rules = f.read()
810 f.close() 828 f.close()
811 rules = [l for l in (r.strip() for r in rules.splitlines()) 829 rules = [l for l in (r.strip() for r in rules.splitlines())
812 if l and not l.startswith('#')] 830 if l and not l.startswith('#')]
813 rules = verifyrules(rules, repo, [repo[c] for [_a, c] in state.rules]) 831 rules = verifyrules(rules, state, [repo[c] for [_a, c] in state.rules])
814 state.rules = rules 832 state.rules = rules
815 state.write() 833 state.write()
816 return 834 return
817 elif goal == 'abort': 835 elif goal == 'abort':
818 try: 836 try:
889 f = open(rules) 907 f = open(rules)
890 rules = f.read() 908 rules = f.read()
891 f.close() 909 f.close()
892 rules = [l for l in (r.strip() for r in rules.splitlines()) 910 rules = [l for l in (r.strip() for r in rules.splitlines())
893 if l and not l.startswith('#')] 911 if l and not l.startswith('#')]
894 rules = verifyrules(rules, repo, ctxs) 912 rules = verifyrules(rules, state, ctxs)
895 913
896 parentctxnode = repo[root].parents()[0].node() 914 parentctxnode = repo[root].parents()[0].node()
897 915
898 state.parentctxnode = parentctxnode 916 state.parentctxnode = parentctxnode
899 state.rules = rules 917 state.rules = rules
1037 f.write(rules) 1055 f.write(rules)
1038 f.close() 1056 f.close()
1039 1057
1040 return rules 1058 return rules
1041 1059
1042 def verifyrules(rules, repo, ctxs): 1060 def verifyrules(rules, state, ctxs):
1043 """Verify that there exists exactly one edit rule per given changeset. 1061 """Verify that there exists exactly one edit rule per given changeset.
1044 1062
1045 Will abort if there are to many or too few rules, a malformed rule, 1063 Will abort if there are to many or too few rules, a malformed rule,
1046 or a rule on a changeset outside of the user-given range. 1064 or a rule on a changeset outside of the user-given range.
1047 """ 1065 """
1066 known_constraints = ['noother', 'noduplicates']
1048 parsed = [] 1067 parsed = []
1049 expected = set(c.hex() for c in ctxs) 1068 expected = set(c.hex() for c in ctxs)
1050 seen = set() 1069 seen = set()
1051 for r in rules: 1070 for r in rules:
1052 if ' ' not in r: 1071 if ' ' not in r:
1053 raise error.Abort(_('malformed line "%s"') % r) 1072 raise error.Abort(_('malformed line "%s"') % r)
1054 action, rest = r.split(' ', 1) 1073 verb, rest = r.split(' ', 1)
1055 ha = rest.strip().split(' ', 1)[0] 1074
1056 try: 1075 if verb not in actiontable or verb.startswith('_'):
1057 ha = repo[ha].hex() 1076 raise error.Abort(_('unknown action "%s"') % verb)
1058 except error.RepoError: 1077 action = actiontable[verb].fromrule(state, rest)
1059 raise error.Abort(_('unknown changeset %s listed') % ha[:12]) 1078 constraints = action.constraints()
1060 if ha not in expected: 1079 for constraint in constraints:
1061 raise error.Abort( 1080 if constraint not in known_constraints:
1062 _('may not use changesets other than the ones listed')) 1081 error.Abort(_('unknown constraint "%s"') % constraint)
1063 if ha in seen: 1082
1064 raise error.Abort(_('duplicated command for changeset %s') % 1083 nodetoverify = action.nodetoverify()
1065 ha[:12]) 1084 if nodetoverify is not None:
1066 seen.add(ha) 1085 ha = node.hex(nodetoverify)
1067 if action not in actiontable or action.startswith('_'): 1086 if 'noother' in constraints and ha not in expected:
1068 raise error.Abort(_('unknown action "%s"') % action) 1087 raise error.Abort(
1069 parsed.append([action, ha]) 1088 _('may not use "%s" with changesets '
1089 'other than the ones listed') % verb)
1090 if 'noduplicates' in constraints and ha in seen:
1091 raise error.Abort(_('duplicated command for changeset %s') %
1092 ha[:12])
1093 seen.add(ha)
1094 rest = ha
1095 parsed.append([verb, rest])
1070 missing = sorted(expected - seen) # sort to stabilize output 1096 missing = sorted(expected - seen) # sort to stabilize output
1071 if missing: 1097 if missing:
1072 raise error.Abort(_('missing rules for changeset %s') % 1098 raise error.Abort(_('missing rules for changeset %s') %
1073 missing[0][:12], 1099 missing[0][:12],
1074 hint=_('do you want to use the drop action?')) 1100 hint=_('do you want to use the drop action?'))