##// END OF EJS Templates
Make it possible to debug failed hook imports via use of --traceback...
Bryan O'Sullivan -
r9851:9e7b2c49 default
parent child Browse files
Show More
@@ -1,139 +1,147 b''
1 # hook.py - hook support for mercurial
1 # hook.py - hook support for mercurial
2 #
2 #
3 # Copyright 2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7
7
8 from i18n import _
8 from i18n import _
9 import os, sys
9 import os, sys
10 import extensions, util
10 import extensions, util
11
11
12 def _pythonhook(ui, repo, name, hname, funcname, args, throw):
12 def _pythonhook(ui, repo, name, hname, funcname, args, throw):
13 '''call python hook. hook is callable object, looked up as
13 '''call python hook. hook is callable object, looked up as
14 name in python module. if callable returns "true", hook
14 name in python module. if callable returns "true", hook
15 fails, else passes. if hook raises exception, treated as
15 fails, else passes. if hook raises exception, treated as
16 hook failure. exception propagates if throw is "true".
16 hook failure. exception propagates if throw is "true".
17
17
18 reason for "true" meaning "hook failed" is so that
18 reason for "true" meaning "hook failed" is so that
19 unmodified commands (e.g. mercurial.commands.update) can
19 unmodified commands (e.g. mercurial.commands.update) can
20 be run as hooks without wrappers to convert return values.'''
20 be run as hooks without wrappers to convert return values.'''
21
21
22 ui.note(_("calling hook %s: %s\n") % (hname, funcname))
22 ui.note(_("calling hook %s: %s\n") % (hname, funcname))
23 obj = funcname
23 obj = funcname
24 if not hasattr(obj, '__call__'):
24 if not hasattr(obj, '__call__'):
25 d = funcname.rfind('.')
25 d = funcname.rfind('.')
26 if d == -1:
26 if d == -1:
27 raise util.Abort(_('%s hook is invalid ("%s" not in '
27 raise util.Abort(_('%s hook is invalid ("%s" not in '
28 'a module)') % (hname, funcname))
28 'a module)') % (hname, funcname))
29 modname = funcname[:d]
29 modname = funcname[:d]
30 oldpaths = sys.path[:]
30 oldpaths = sys.path[:]
31 if hasattr(sys, "frozen"):
31 if hasattr(sys, "frozen"):
32 # binary installs require sys.path manipulation
32 # binary installs require sys.path manipulation
33 path, name = os.path.split(modname)
33 path, name = os.path.split(modname)
34 if path and name:
34 if path and name:
35 sys.path.append(path)
35 sys.path.append(path)
36 modname = name
36 modname = name
37 try:
37 try:
38 obj = __import__(modname)
38 obj = __import__(modname)
39 except ImportError:
39 except ImportError:
40 e1 = sys.exc_type, sys.exc_value, sys.exc_traceback
40 try:
41 try:
41 # extensions are loaded with hgext_ prefix
42 # extensions are loaded with hgext_ prefix
42 obj = __import__("hgext_%s" % modname)
43 obj = __import__("hgext_%s" % modname)
43 except ImportError:
44 except ImportError:
45 e2 = sys.exc_type, sys.exc_value, sys.exc_traceback
46 if ui.tracebackflag:
47 ui.warn(_('exception from first failed import attempt:\n'))
48 ui.traceback(e1)
49 if ui.tracebackflag:
50 ui.warn(_('exception from second failed import attempt:\n'))
51 ui.traceback(e2)
44 raise util.Abort(_('%s hook is invalid '
52 raise util.Abort(_('%s hook is invalid '
45 '(import of "%s" failed)') %
53 '(import of "%s" failed)') %
46 (hname, modname))
54 (hname, modname))
47 sys.path = oldpaths
55 sys.path = oldpaths
48 try:
56 try:
49 for p in funcname.split('.')[1:]:
57 for p in funcname.split('.')[1:]:
50 obj = getattr(obj, p)
58 obj = getattr(obj, p)
51 except AttributeError:
59 except AttributeError:
52 raise util.Abort(_('%s hook is invalid '
60 raise util.Abort(_('%s hook is invalid '
53 '("%s" is not defined)') %
61 '("%s" is not defined)') %
54 (hname, funcname))
62 (hname, funcname))
55 if not hasattr(obj, '__call__'):
63 if not hasattr(obj, '__call__'):
56 raise util.Abort(_('%s hook is invalid '
64 raise util.Abort(_('%s hook is invalid '
57 '("%s" is not callable)') %
65 '("%s" is not callable)') %
58 (hname, funcname))
66 (hname, funcname))
59 try:
67 try:
60 r = obj(ui=ui, repo=repo, hooktype=name, **args)
68 r = obj(ui=ui, repo=repo, hooktype=name, **args)
61 except KeyboardInterrupt:
69 except KeyboardInterrupt:
62 raise
70 raise
63 except Exception, exc:
71 except Exception, exc:
64 if isinstance(exc, util.Abort):
72 if isinstance(exc, util.Abort):
65 ui.warn(_('error: %s hook failed: %s\n') %
73 ui.warn(_('error: %s hook failed: %s\n') %
66 (hname, exc.args[0]))
74 (hname, exc.args[0]))
67 else:
75 else:
68 ui.warn(_('error: %s hook raised an exception: '
76 ui.warn(_('error: %s hook raised an exception: '
69 '%s\n') % (hname, exc))
77 '%s\n') % (hname, exc))
70 if throw:
78 if throw:
71 raise
79 raise
72 ui.traceback()
80 ui.traceback()
73 return True
81 return True
74 if r:
82 if r:
75 if throw:
83 if throw:
76 raise util.Abort(_('%s hook failed') % hname)
84 raise util.Abort(_('%s hook failed') % hname)
77 ui.warn(_('warning: %s hook failed\n') % hname)
85 ui.warn(_('warning: %s hook failed\n') % hname)
78 return r
86 return r
79
87
80 def _exthook(ui, repo, name, cmd, args, throw):
88 def _exthook(ui, repo, name, cmd, args, throw):
81 ui.note(_("running hook %s: %s\n") % (name, cmd))
89 ui.note(_("running hook %s: %s\n") % (name, cmd))
82
90
83 env = {}
91 env = {}
84 for k, v in args.iteritems():
92 for k, v in args.iteritems():
85 if hasattr(v, '__call__'):
93 if hasattr(v, '__call__'):
86 v = v()
94 v = v()
87 env['HG_' + k.upper()] = v
95 env['HG_' + k.upper()] = v
88
96
89 if repo:
97 if repo:
90 cwd = repo.root
98 cwd = repo.root
91 else:
99 else:
92 cwd = os.getcwd()
100 cwd = os.getcwd()
93 r = util.system(cmd, environ=env, cwd=cwd)
101 r = util.system(cmd, environ=env, cwd=cwd)
94 if r:
102 if r:
95 desc, r = util.explain_exit(r)
103 desc, r = util.explain_exit(r)
96 if throw:
104 if throw:
97 raise util.Abort(_('%s hook %s') % (name, desc))
105 raise util.Abort(_('%s hook %s') % (name, desc))
98 ui.warn(_('warning: %s hook %s\n') % (name, desc))
106 ui.warn(_('warning: %s hook %s\n') % (name, desc))
99 return r
107 return r
100
108
101 _redirect = False
109 _redirect = False
102 def redirect(state):
110 def redirect(state):
103 global _redirect
111 global _redirect
104 _redirect = state
112 _redirect = state
105
113
106 def hook(ui, repo, name, throw=False, **args):
114 def hook(ui, repo, name, throw=False, **args):
107 r = False
115 r = False
108
116
109 oldstdout = -1
117 oldstdout = -1
110 if _redirect:
118 if _redirect:
111 stdoutno = sys.__stdout__.fileno()
119 stdoutno = sys.__stdout__.fileno()
112 stderrno = sys.__stderr__.fileno()
120 stderrno = sys.__stderr__.fileno()
113 # temporarily redirect stdout to stderr, if possible
121 # temporarily redirect stdout to stderr, if possible
114 if stdoutno >= 0 and stderrno >= 0:
122 if stdoutno >= 0 and stderrno >= 0:
115 oldstdout = os.dup(stdoutno)
123 oldstdout = os.dup(stdoutno)
116 os.dup2(stderrno, stdoutno)
124 os.dup2(stderrno, stdoutno)
117
125
118 try:
126 try:
119 for hname, cmd in ui.configitems('hooks'):
127 for hname, cmd in ui.configitems('hooks'):
120 if hname.split('.')[0] != name or not cmd:
128 if hname.split('.')[0] != name or not cmd:
121 continue
129 continue
122 if hasattr(cmd, '__call__'):
130 if hasattr(cmd, '__call__'):
123 r = _pythonhook(ui, repo, name, hname, cmd, args, throw) or r
131 r = _pythonhook(ui, repo, name, hname, cmd, args, throw) or r
124 elif cmd.startswith('python:'):
132 elif cmd.startswith('python:'):
125 if cmd.count(':') >= 2:
133 if cmd.count(':') >= 2:
126 path, cmd = cmd[7:].rsplit(':', 1)
134 path, cmd = cmd[7:].rsplit(':', 1)
127 mod = extensions.loadpath(path, 'hghook.%s' % hname)
135 mod = extensions.loadpath(path, 'hghook.%s' % hname)
128 hookfn = getattr(mod, cmd)
136 hookfn = getattr(mod, cmd)
129 else:
137 else:
130 hookfn = cmd[7:].strip()
138 hookfn = cmd[7:].strip()
131 r = _pythonhook(ui, repo, name, hname, hookfn, args, throw) or r
139 r = _pythonhook(ui, repo, name, hname, hookfn, args, throw) or r
132 else:
140 else:
133 r = _exthook(ui, repo, hname, cmd, args, throw) or r
141 r = _exthook(ui, repo, hname, cmd, args, throw) or r
134 finally:
142 finally:
135 if _redirect and oldstdout >= 0:
143 if _redirect and oldstdout >= 0:
136 os.dup2(oldstdout, stdoutno)
144 os.dup2(oldstdout, stdoutno)
137 os.close(oldstdout)
145 os.close(oldstdout)
138
146
139 return r
147 return r
@@ -1,383 +1,386 b''
1 # ui.py - user interface bits for mercurial
1 # ui.py - user interface bits for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7
7
8 from i18n import _
8 from i18n import _
9 import errno, getpass, os, socket, sys, tempfile, traceback
9 import errno, getpass, os, socket, sys, tempfile, traceback
10 import config, util, error
10 import config, util, error
11
11
12 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True,
12 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True,
13 '0': False, 'no': False, 'false': False, 'off': False}
13 '0': False, 'no': False, 'false': False, 'off': False}
14
14
15 class ui(object):
15 class ui(object):
16 def __init__(self, src=None):
16 def __init__(self, src=None):
17 self._buffers = []
17 self._buffers = []
18 self.quiet = self.verbose = self.debugflag = self._traceback = False
18 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
19 self._reportuntrusted = True
19 self._reportuntrusted = True
20 self._ocfg = config.config() # overlay
20 self._ocfg = config.config() # overlay
21 self._tcfg = config.config() # trusted
21 self._tcfg = config.config() # trusted
22 self._ucfg = config.config() # untrusted
22 self._ucfg = config.config() # untrusted
23 self._trustusers = set()
23 self._trustusers = set()
24 self._trustgroups = set()
24 self._trustgroups = set()
25
25
26 if src:
26 if src:
27 self._tcfg = src._tcfg.copy()
27 self._tcfg = src._tcfg.copy()
28 self._ucfg = src._ucfg.copy()
28 self._ucfg = src._ucfg.copy()
29 self._ocfg = src._ocfg.copy()
29 self._ocfg = src._ocfg.copy()
30 self._trustusers = src._trustusers.copy()
30 self._trustusers = src._trustusers.copy()
31 self._trustgroups = src._trustgroups.copy()
31 self._trustgroups = src._trustgroups.copy()
32 self.fixconfig()
32 self.fixconfig()
33 else:
33 else:
34 # we always trust global config files
34 # we always trust global config files
35 for f in util.rcpath():
35 for f in util.rcpath():
36 self.readconfig(f, trust=True)
36 self.readconfig(f, trust=True)
37
37
38 def copy(self):
38 def copy(self):
39 return self.__class__(self)
39 return self.__class__(self)
40
40
41 def _is_trusted(self, fp, f):
41 def _is_trusted(self, fp, f):
42 st = util.fstat(fp)
42 st = util.fstat(fp)
43 if util.isowner(st):
43 if util.isowner(st):
44 return True
44 return True
45
45
46 tusers, tgroups = self._trustusers, self._trustgroups
46 tusers, tgroups = self._trustusers, self._trustgroups
47 if '*' in tusers or '*' in tgroups:
47 if '*' in tusers or '*' in tgroups:
48 return True
48 return True
49
49
50 user = util.username(st.st_uid)
50 user = util.username(st.st_uid)
51 group = util.groupname(st.st_gid)
51 group = util.groupname(st.st_gid)
52 if user in tusers or group in tgroups or user == util.username():
52 if user in tusers or group in tgroups or user == util.username():
53 return True
53 return True
54
54
55 if self._reportuntrusted:
55 if self._reportuntrusted:
56 self.warn(_('Not trusting file %s from untrusted '
56 self.warn(_('Not trusting file %s from untrusted '
57 'user %s, group %s\n') % (f, user, group))
57 'user %s, group %s\n') % (f, user, group))
58 return False
58 return False
59
59
60 def readconfig(self, filename, root=None, trust=False,
60 def readconfig(self, filename, root=None, trust=False,
61 sections=None, remap=None):
61 sections=None, remap=None):
62 try:
62 try:
63 fp = open(filename)
63 fp = open(filename)
64 except IOError:
64 except IOError:
65 if not sections: # ignore unless we were looking for something
65 if not sections: # ignore unless we were looking for something
66 return
66 return
67 raise
67 raise
68
68
69 cfg = config.config()
69 cfg = config.config()
70 trusted = sections or trust or self._is_trusted(fp, filename)
70 trusted = sections or trust or self._is_trusted(fp, filename)
71
71
72 try:
72 try:
73 cfg.read(filename, fp, sections=sections, remap=remap)
73 cfg.read(filename, fp, sections=sections, remap=remap)
74 except error.ConfigError, inst:
74 except error.ConfigError, inst:
75 if trusted:
75 if trusted:
76 raise
76 raise
77 self.warn(_("Ignored: %s\n") % str(inst))
77 self.warn(_("Ignored: %s\n") % str(inst))
78
78
79 if trusted:
79 if trusted:
80 self._tcfg.update(cfg)
80 self._tcfg.update(cfg)
81 self._tcfg.update(self._ocfg)
81 self._tcfg.update(self._ocfg)
82 self._ucfg.update(cfg)
82 self._ucfg.update(cfg)
83 self._ucfg.update(self._ocfg)
83 self._ucfg.update(self._ocfg)
84
84
85 if root is None:
85 if root is None:
86 root = os.path.expanduser('~')
86 root = os.path.expanduser('~')
87 self.fixconfig(root=root)
87 self.fixconfig(root=root)
88
88
89 def fixconfig(self, root=None):
89 def fixconfig(self, root=None):
90 # translate paths relative to root (or home) into absolute paths
90 # translate paths relative to root (or home) into absolute paths
91 root = root or os.getcwd()
91 root = root or os.getcwd()
92 for c in self._tcfg, self._ucfg, self._ocfg:
92 for c in self._tcfg, self._ucfg, self._ocfg:
93 for n, p in c.items('paths'):
93 for n, p in c.items('paths'):
94 if p and "://" not in p and not os.path.isabs(p):
94 if p and "://" not in p and not os.path.isabs(p):
95 c.set("paths", n, os.path.normpath(os.path.join(root, p)))
95 c.set("paths", n, os.path.normpath(os.path.join(root, p)))
96
96
97 # update ui options
97 # update ui options
98 self.debugflag = self.configbool('ui', 'debug')
98 self.debugflag = self.configbool('ui', 'debug')
99 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
99 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
100 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
100 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
101 if self.verbose and self.quiet:
101 if self.verbose and self.quiet:
102 self.quiet = self.verbose = False
102 self.quiet = self.verbose = False
103 self._reportuntrusted = self.configbool("ui", "report_untrusted", True)
103 self._reportuntrusted = self.configbool("ui", "report_untrusted", True)
104 self._traceback = self.configbool('ui', 'traceback', False)
104 self.tracebackflag = self.configbool('ui', 'traceback', False)
105
105
106 # update trust information
106 # update trust information
107 self._trustusers.update(self.configlist('trusted', 'users'))
107 self._trustusers.update(self.configlist('trusted', 'users'))
108 self._trustgroups.update(self.configlist('trusted', 'groups'))
108 self._trustgroups.update(self.configlist('trusted', 'groups'))
109
109
110 def setconfig(self, section, name, value):
110 def setconfig(self, section, name, value):
111 for cfg in (self._ocfg, self._tcfg, self._ucfg):
111 for cfg in (self._ocfg, self._tcfg, self._ucfg):
112 cfg.set(section, name, value)
112 cfg.set(section, name, value)
113 self.fixconfig()
113 self.fixconfig()
114
114
115 def _data(self, untrusted):
115 def _data(self, untrusted):
116 return untrusted and self._ucfg or self._tcfg
116 return untrusted and self._ucfg or self._tcfg
117
117
118 def configsource(self, section, name, untrusted=False):
118 def configsource(self, section, name, untrusted=False):
119 return self._data(untrusted).source(section, name) or 'none'
119 return self._data(untrusted).source(section, name) or 'none'
120
120
121 def config(self, section, name, default=None, untrusted=False):
121 def config(self, section, name, default=None, untrusted=False):
122 value = self._data(untrusted).get(section, name, default)
122 value = self._data(untrusted).get(section, name, default)
123 if self.debugflag and not untrusted and self._reportuntrusted:
123 if self.debugflag and not untrusted and self._reportuntrusted:
124 uvalue = self._ucfg.get(section, name)
124 uvalue = self._ucfg.get(section, name)
125 if uvalue is not None and uvalue != value:
125 if uvalue is not None and uvalue != value:
126 self.debug(_("ignoring untrusted configuration option "
126 self.debug(_("ignoring untrusted configuration option "
127 "%s.%s = %s\n") % (section, name, uvalue))
127 "%s.%s = %s\n") % (section, name, uvalue))
128 return value
128 return value
129
129
130 def configbool(self, section, name, default=False, untrusted=False):
130 def configbool(self, section, name, default=False, untrusted=False):
131 v = self.config(section, name, None, untrusted)
131 v = self.config(section, name, None, untrusted)
132 if v is None:
132 if v is None:
133 return default
133 return default
134 if v.lower() not in _booleans:
134 if v.lower() not in _booleans:
135 raise error.ConfigError(_("%s.%s not a boolean ('%s')")
135 raise error.ConfigError(_("%s.%s not a boolean ('%s')")
136 % (section, name, v))
136 % (section, name, v))
137 return _booleans[v.lower()]
137 return _booleans[v.lower()]
138
138
139 def configlist(self, section, name, default=None, untrusted=False):
139 def configlist(self, section, name, default=None, untrusted=False):
140 """Return a list of comma/space separated strings"""
140 """Return a list of comma/space separated strings"""
141 result = self.config(section, name, untrusted=untrusted)
141 result = self.config(section, name, untrusted=untrusted)
142 if result is None:
142 if result is None:
143 result = default or []
143 result = default or []
144 if isinstance(result, basestring):
144 if isinstance(result, basestring):
145 result = result.replace(",", " ").split()
145 result = result.replace(",", " ").split()
146 return result
146 return result
147
147
148 def has_section(self, section, untrusted=False):
148 def has_section(self, section, untrusted=False):
149 '''tell whether section exists in config.'''
149 '''tell whether section exists in config.'''
150 return section in self._data(untrusted)
150 return section in self._data(untrusted)
151
151
152 def configitems(self, section, untrusted=False):
152 def configitems(self, section, untrusted=False):
153 items = self._data(untrusted).items(section)
153 items = self._data(untrusted).items(section)
154 if self.debugflag and not untrusted and self._reportuntrusted:
154 if self.debugflag and not untrusted and self._reportuntrusted:
155 for k, v in self._ucfg.items(section):
155 for k, v in self._ucfg.items(section):
156 if self._tcfg.get(section, k) != v:
156 if self._tcfg.get(section, k) != v:
157 self.debug(_("ignoring untrusted configuration option "
157 self.debug(_("ignoring untrusted configuration option "
158 "%s.%s = %s\n") % (section, k, v))
158 "%s.%s = %s\n") % (section, k, v))
159 return items
159 return items
160
160
161 def walkconfig(self, untrusted=False):
161 def walkconfig(self, untrusted=False):
162 cfg = self._data(untrusted)
162 cfg = self._data(untrusted)
163 for section in cfg.sections():
163 for section in cfg.sections():
164 for name, value in self.configitems(section, untrusted):
164 for name, value in self.configitems(section, untrusted):
165 yield section, name, str(value).replace('\n', '\\n')
165 yield section, name, str(value).replace('\n', '\\n')
166
166
167 def username(self):
167 def username(self):
168 """Return default username to be used in commits.
168 """Return default username to be used in commits.
169
169
170 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
170 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
171 and stop searching if one of these is set.
171 and stop searching if one of these is set.
172 If not found and ui.askusername is True, ask the user, else use
172 If not found and ui.askusername is True, ask the user, else use
173 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
173 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
174 """
174 """
175 user = os.environ.get("HGUSER")
175 user = os.environ.get("HGUSER")
176 if user is None:
176 if user is None:
177 user = self.config("ui", "username")
177 user = self.config("ui", "username")
178 if user is None:
178 if user is None:
179 user = os.environ.get("EMAIL")
179 user = os.environ.get("EMAIL")
180 if user is None and self.configbool("ui", "askusername"):
180 if user is None and self.configbool("ui", "askusername"):
181 user = self.prompt(_("enter a commit username:"), default=None)
181 user = self.prompt(_("enter a commit username:"), default=None)
182 if user is None and not self.interactive():
182 if user is None and not self.interactive():
183 try:
183 try:
184 user = '%s@%s' % (util.getuser(), socket.getfqdn())
184 user = '%s@%s' % (util.getuser(), socket.getfqdn())
185 self.warn(_("No username found, using '%s' instead\n") % user)
185 self.warn(_("No username found, using '%s' instead\n") % user)
186 except KeyError:
186 except KeyError:
187 pass
187 pass
188 if not user:
188 if not user:
189 raise util.Abort(_('no username supplied (see "hg help config")'))
189 raise util.Abort(_('no username supplied (see "hg help config")'))
190 if "\n" in user:
190 if "\n" in user:
191 raise util.Abort(_("username %s contains a newline\n") % repr(user))
191 raise util.Abort(_("username %s contains a newline\n") % repr(user))
192 return user
192 return user
193
193
194 def shortuser(self, user):
194 def shortuser(self, user):
195 """Return a short representation of a user name or email address."""
195 """Return a short representation of a user name or email address."""
196 if not self.verbose: user = util.shortuser(user)
196 if not self.verbose: user = util.shortuser(user)
197 return user
197 return user
198
198
199 def _path(self, loc):
199 def _path(self, loc):
200 p = self.config('paths', loc)
200 p = self.config('paths', loc)
201 if p:
201 if p:
202 if '%%' in p:
202 if '%%' in p:
203 self.warn("(deprecated '%%' in path %s=%s from %s)\n" %
203 self.warn("(deprecated '%%' in path %s=%s from %s)\n" %
204 (loc, p, self.configsource('paths', loc)))
204 (loc, p, self.configsource('paths', loc)))
205 p = p.replace('%%', '%')
205 p = p.replace('%%', '%')
206 p = util.expandpath(p)
206 p = util.expandpath(p)
207 return p
207 return p
208
208
209 def expandpath(self, loc, default=None):
209 def expandpath(self, loc, default=None):
210 """Return repository location relative to cwd or from [paths]"""
210 """Return repository location relative to cwd or from [paths]"""
211 if "://" in loc or os.path.isdir(os.path.join(loc, '.hg')):
211 if "://" in loc or os.path.isdir(os.path.join(loc, '.hg')):
212 return loc
212 return loc
213
213
214 path = self._path(loc)
214 path = self._path(loc)
215 if not path and default is not None:
215 if not path and default is not None:
216 path = self._path(default)
216 path = self._path(default)
217 return path or loc
217 return path or loc
218
218
219 def pushbuffer(self):
219 def pushbuffer(self):
220 self._buffers.append([])
220 self._buffers.append([])
221
221
222 def popbuffer(self):
222 def popbuffer(self):
223 return "".join(self._buffers.pop())
223 return "".join(self._buffers.pop())
224
224
225 def write(self, *args):
225 def write(self, *args):
226 if self._buffers:
226 if self._buffers:
227 self._buffers[-1].extend([str(a) for a in args])
227 self._buffers[-1].extend([str(a) for a in args])
228 else:
228 else:
229 for a in args:
229 for a in args:
230 sys.stdout.write(str(a))
230 sys.stdout.write(str(a))
231
231
232 def write_err(self, *args):
232 def write_err(self, *args):
233 try:
233 try:
234 if not sys.stdout.closed: sys.stdout.flush()
234 if not sys.stdout.closed: sys.stdout.flush()
235 for a in args:
235 for a in args:
236 sys.stderr.write(str(a))
236 sys.stderr.write(str(a))
237 # stderr may be buffered under win32 when redirected to files,
237 # stderr may be buffered under win32 when redirected to files,
238 # including stdout.
238 # including stdout.
239 if not sys.stderr.closed: sys.stderr.flush()
239 if not sys.stderr.closed: sys.stderr.flush()
240 except IOError, inst:
240 except IOError, inst:
241 if inst.errno != errno.EPIPE:
241 if inst.errno != errno.EPIPE:
242 raise
242 raise
243
243
244 def flush(self):
244 def flush(self):
245 try: sys.stdout.flush()
245 try: sys.stdout.flush()
246 except: pass
246 except: pass
247 try: sys.stderr.flush()
247 try: sys.stderr.flush()
248 except: pass
248 except: pass
249
249
250 def interactive(self):
250 def interactive(self):
251 i = self.configbool("ui", "interactive", None)
251 i = self.configbool("ui", "interactive", None)
252 if i is None:
252 if i is None:
253 return sys.stdin.isatty()
253 return sys.stdin.isatty()
254 return i
254 return i
255
255
256 def _readline(self, prompt=''):
256 def _readline(self, prompt=''):
257 if sys.stdin.isatty():
257 if sys.stdin.isatty():
258 try:
258 try:
259 # magically add command line editing support, where
259 # magically add command line editing support, where
260 # available
260 # available
261 import readline
261 import readline
262 # force demandimport to really load the module
262 # force demandimport to really load the module
263 readline.read_history_file
263 readline.read_history_file
264 # windows sometimes raises something other than ImportError
264 # windows sometimes raises something other than ImportError
265 except Exception:
265 except Exception:
266 pass
266 pass
267 line = raw_input(prompt)
267 line = raw_input(prompt)
268 # When stdin is in binary mode on Windows, it can cause
268 # When stdin is in binary mode on Windows, it can cause
269 # raw_input() to emit an extra trailing carriage return
269 # raw_input() to emit an extra trailing carriage return
270 if os.linesep == '\r\n' and line and line[-1] == '\r':
270 if os.linesep == '\r\n' and line and line[-1] == '\r':
271 line = line[:-1]
271 line = line[:-1]
272 return line
272 return line
273
273
274 def prompt(self, msg, default="y"):
274 def prompt(self, msg, default="y"):
275 """Prompt user with msg, read response.
275 """Prompt user with msg, read response.
276 If ui is not interactive, the default is returned.
276 If ui is not interactive, the default is returned.
277 """
277 """
278 if not self.interactive():
278 if not self.interactive():
279 self.write(msg, ' ', default, "\n")
279 self.write(msg, ' ', default, "\n")
280 return default
280 return default
281 try:
281 try:
282 r = self._readline(msg + ' ')
282 r = self._readline(msg + ' ')
283 if not r:
283 if not r:
284 return default
284 return default
285 return r
285 return r
286 except EOFError:
286 except EOFError:
287 raise util.Abort(_('response expected'))
287 raise util.Abort(_('response expected'))
288
288
289 def promptchoice(self, msg, choices, default=0):
289 def promptchoice(self, msg, choices, default=0):
290 """Prompt user with msg, read response, and ensure it matches
290 """Prompt user with msg, read response, and ensure it matches
291 one of the provided choices. The index of the choice is returned.
291 one of the provided choices. The index of the choice is returned.
292 choices is a sequence of acceptable responses with the format:
292 choices is a sequence of acceptable responses with the format:
293 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
293 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
294 If ui is not interactive, the default is returned.
294 If ui is not interactive, the default is returned.
295 """
295 """
296 resps = [s[s.index('&')+1].lower() for s in choices]
296 resps = [s[s.index('&')+1].lower() for s in choices]
297 while True:
297 while True:
298 r = self.prompt(msg, resps[default])
298 r = self.prompt(msg, resps[default])
299 if r.lower() in resps:
299 if r.lower() in resps:
300 return resps.index(r.lower())
300 return resps.index(r.lower())
301 self.write(_("unrecognized response\n"))
301 self.write(_("unrecognized response\n"))
302
302
303
303
304 def getpass(self, prompt=None, default=None):
304 def getpass(self, prompt=None, default=None):
305 if not self.interactive(): return default
305 if not self.interactive(): return default
306 try:
306 try:
307 return getpass.getpass(prompt or _('password: '))
307 return getpass.getpass(prompt or _('password: '))
308 except EOFError:
308 except EOFError:
309 raise util.Abort(_('response expected'))
309 raise util.Abort(_('response expected'))
310 def status(self, *msg):
310 def status(self, *msg):
311 if not self.quiet: self.write(*msg)
311 if not self.quiet: self.write(*msg)
312 def warn(self, *msg):
312 def warn(self, *msg):
313 self.write_err(*msg)
313 self.write_err(*msg)
314 def note(self, *msg):
314 def note(self, *msg):
315 if self.verbose: self.write(*msg)
315 if self.verbose: self.write(*msg)
316 def debug(self, *msg):
316 def debug(self, *msg):
317 if self.debugflag: self.write(*msg)
317 if self.debugflag: self.write(*msg)
318 def edit(self, text, user):
318 def edit(self, text, user):
319 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
319 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
320 text=True)
320 text=True)
321 try:
321 try:
322 f = os.fdopen(fd, "w")
322 f = os.fdopen(fd, "w")
323 f.write(text)
323 f.write(text)
324 f.close()
324 f.close()
325
325
326 editor = self.geteditor()
326 editor = self.geteditor()
327
327
328 util.system("%s \"%s\"" % (editor, name),
328 util.system("%s \"%s\"" % (editor, name),
329 environ={'HGUSER': user},
329 environ={'HGUSER': user},
330 onerr=util.Abort, errprefix=_("edit failed"))
330 onerr=util.Abort, errprefix=_("edit failed"))
331
331
332 f = open(name)
332 f = open(name)
333 t = f.read()
333 t = f.read()
334 f.close()
334 f.close()
335 finally:
335 finally:
336 os.unlink(name)
336 os.unlink(name)
337
337
338 return t
338 return t
339
339
340 def traceback(self):
340 def traceback(self, exc=None):
341 '''print exception traceback if traceback printing enabled.
341 '''print exception traceback if traceback printing enabled.
342 only to call in exception handler. returns true if traceback
342 only to call in exception handler. returns true if traceback
343 printed.'''
343 printed.'''
344 if self._traceback:
344 if self.tracebackflag:
345 if exc:
346 traceback.print_exception(exc[0], exc[1], exc[2])
347 else:
345 traceback.print_exc()
348 traceback.print_exc()
346 return self._traceback
349 return self.tracebackflag
347
350
348 def geteditor(self):
351 def geteditor(self):
349 '''return editor to use'''
352 '''return editor to use'''
350 return (os.environ.get("HGEDITOR") or
353 return (os.environ.get("HGEDITOR") or
351 self.config("ui", "editor") or
354 self.config("ui", "editor") or
352 os.environ.get("VISUAL") or
355 os.environ.get("VISUAL") or
353 os.environ.get("EDITOR", "vi"))
356 os.environ.get("EDITOR", "vi"))
354
357
355 def progress(self, topic, pos, item="", unit="", total=None):
358 def progress(self, topic, pos, item="", unit="", total=None):
356 '''show a progress message
359 '''show a progress message
357
360
358 With stock hg, this is simply a debug message that is hidden
361 With stock hg, this is simply a debug message that is hidden
359 by default, but with extensions or GUI tools it may be
362 by default, but with extensions or GUI tools it may be
360 visible. 'topic' is the current operation, 'item' is a
363 visible. 'topic' is the current operation, 'item' is a
361 non-numeric marker of the current position (ie the currently
364 non-numeric marker of the current position (ie the currently
362 in-process file), 'pos' is the current numeric position (ie
365 in-process file), 'pos' is the current numeric position (ie
363 revision, bytes, etc.), unit is a corresponding unit label,
366 revision, bytes, etc.), unit is a corresponding unit label,
364 and total is the highest expected pos.
367 and total is the highest expected pos.
365
368
366 Multiple nested topics may be active at a time. All topics
369 Multiple nested topics may be active at a time. All topics
367 should be marked closed by setting pos to None at termination.
370 should be marked closed by setting pos to None at termination.
368 '''
371 '''
369
372
370 if pos == None or not self.debugflag:
373 if pos == None or not self.debugflag:
371 return
374 return
372
375
373 if unit:
376 if unit:
374 unit = ' ' + unit
377 unit = ' ' + unit
375 if item:
378 if item:
376 item = ' ' + item
379 item = ' ' + item
377
380
378 if total:
381 if total:
379 pct = 100.0 * pos / total
382 pct = 100.0 * pos / total
380 self.debug('%s:%s %s/%s%s (%4.2g%%)\n'
383 self.debug('%s:%s %s/%s%s (%4.2g%%)\n'
381 % (topic, item, pos, total, unit, pct))
384 % (topic, item, pos, total, unit, pct))
382 else:
385 else:
383 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
386 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
@@ -1,251 +1,265 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 cp "$TESTDIR"/printenv.py .
3 cp "$TESTDIR"/printenv.py .
4
4
5 # commit hooks can see env vars
5 # commit hooks can see env vars
6 hg init a
6 hg init a
7 cd a
7 cd a
8 echo "[hooks]" > .hg/hgrc
8 echo "[hooks]" > .hg/hgrc
9 echo 'commit = unset HG_LOCAL HG_TAG; python ../printenv.py commit' >> .hg/hgrc
9 echo 'commit = unset HG_LOCAL HG_TAG; python ../printenv.py commit' >> .hg/hgrc
10 echo 'commit.b = unset HG_LOCAL HG_TAG; python ../printenv.py commit.b' >> .hg/hgrc
10 echo 'commit.b = unset HG_LOCAL HG_TAG; python ../printenv.py commit.b' >> .hg/hgrc
11 echo 'precommit = unset HG_LOCAL HG_NODE HG_TAG; python ../printenv.py precommit' >> .hg/hgrc
11 echo 'precommit = unset HG_LOCAL HG_NODE HG_TAG; python ../printenv.py precommit' >> .hg/hgrc
12 echo 'pretxncommit = unset HG_LOCAL HG_TAG; python ../printenv.py pretxncommit' >> .hg/hgrc
12 echo 'pretxncommit = unset HG_LOCAL HG_TAG; python ../printenv.py pretxncommit' >> .hg/hgrc
13 echo 'pretxncommit.tip = hg -q tip' >> .hg/hgrc
13 echo 'pretxncommit.tip = hg -q tip' >> .hg/hgrc
14 echo 'pre-identify = python ../printenv.py pre-identify 1' >> .hg/hgrc
14 echo 'pre-identify = python ../printenv.py pre-identify 1' >> .hg/hgrc
15 echo 'pre-cat = python ../printenv.py pre-cat' >> .hg/hgrc
15 echo 'pre-cat = python ../printenv.py pre-cat' >> .hg/hgrc
16 echo 'post-cat = python ../printenv.py post-cat' >> .hg/hgrc
16 echo 'post-cat = python ../printenv.py post-cat' >> .hg/hgrc
17 echo a > a
17 echo a > a
18 hg add a
18 hg add a
19 hg commit -m a -d "1000000 0"
19 hg commit -m a -d "1000000 0"
20
20
21 hg clone . ../b
21 hg clone . ../b
22 cd ../b
22 cd ../b
23
23
24 # changegroup hooks can see env vars
24 # changegroup hooks can see env vars
25 echo '[hooks]' > .hg/hgrc
25 echo '[hooks]' > .hg/hgrc
26 echo 'prechangegroup = python ../printenv.py prechangegroup' >> .hg/hgrc
26 echo 'prechangegroup = python ../printenv.py prechangegroup' >> .hg/hgrc
27 echo 'changegroup = python ../printenv.py changegroup' >> .hg/hgrc
27 echo 'changegroup = python ../printenv.py changegroup' >> .hg/hgrc
28 echo 'incoming = python ../printenv.py incoming' >> .hg/hgrc
28 echo 'incoming = python ../printenv.py incoming' >> .hg/hgrc
29
29
30 # pretxncommit and commit hooks can see both parents of merge
30 # pretxncommit and commit hooks can see both parents of merge
31 cd ../a
31 cd ../a
32 echo b >> a
32 echo b >> a
33 hg commit -m a1 -d "1 0"
33 hg commit -m a1 -d "1 0"
34 hg update -C 0
34 hg update -C 0
35 echo b > b
35 echo b > b
36 hg add b
36 hg add b
37 hg commit -m b -d '1 0'
37 hg commit -m b -d '1 0'
38 hg merge 1
38 hg merge 1
39 hg commit -m merge -d '2 0'
39 hg commit -m merge -d '2 0'
40
40
41 # test generic hooks
41 # test generic hooks
42 hg id
42 hg id
43 hg cat b
43 hg cat b
44
44
45 cd ../b
45 cd ../b
46 hg pull ../a
46 hg pull ../a
47
47
48 # tag hooks can see env vars
48 # tag hooks can see env vars
49 cd ../a
49 cd ../a
50 echo 'pretag = python ../printenv.py pretag' >> .hg/hgrc
50 echo 'pretag = python ../printenv.py pretag' >> .hg/hgrc
51 echo 'tag = unset HG_PARENT1 HG_PARENT2; python ../printenv.py tag' >> .hg/hgrc
51 echo 'tag = unset HG_PARENT1 HG_PARENT2; python ../printenv.py tag' >> .hg/hgrc
52 hg tag -d '3 0' a
52 hg tag -d '3 0' a
53 hg tag -l la
53 hg tag -l la
54
54
55 # pretag hook can forbid tagging
55 # pretag hook can forbid tagging
56 echo 'pretag.forbid = python ../printenv.py pretag.forbid 1' >> .hg/hgrc
56 echo 'pretag.forbid = python ../printenv.py pretag.forbid 1' >> .hg/hgrc
57 hg tag -d '4 0' fa
57 hg tag -d '4 0' fa
58 hg tag -l fla
58 hg tag -l fla
59
59
60 # pretxncommit hook can see changeset, can roll back txn, changeset
60 # pretxncommit hook can see changeset, can roll back txn, changeset
61 # no more there after
61 # no more there after
62 echo 'pretxncommit.forbid0 = hg tip -q' >> .hg/hgrc
62 echo 'pretxncommit.forbid0 = hg tip -q' >> .hg/hgrc
63 echo 'pretxncommit.forbid1 = python ../printenv.py pretxncommit.forbid 1' >> .hg/hgrc
63 echo 'pretxncommit.forbid1 = python ../printenv.py pretxncommit.forbid 1' >> .hg/hgrc
64 echo z > z
64 echo z > z
65 hg add z
65 hg add z
66 hg -q tip
66 hg -q tip
67 hg commit -m 'fail' -d '4 0'
67 hg commit -m 'fail' -d '4 0'
68 hg -q tip
68 hg -q tip
69
69
70 # precommit hook can prevent commit
70 # precommit hook can prevent commit
71 echo 'precommit.forbid = python ../printenv.py precommit.forbid 1' >> .hg/hgrc
71 echo 'precommit.forbid = python ../printenv.py precommit.forbid 1' >> .hg/hgrc
72 hg commit -m 'fail' -d '4 0'
72 hg commit -m 'fail' -d '4 0'
73 hg -q tip
73 hg -q tip
74
74
75 # preupdate hook can prevent update
75 # preupdate hook can prevent update
76 echo 'preupdate = python ../printenv.py preupdate' >> .hg/hgrc
76 echo 'preupdate = python ../printenv.py preupdate' >> .hg/hgrc
77 hg update 1
77 hg update 1
78
78
79 # update hook
79 # update hook
80 echo 'update = python ../printenv.py update' >> .hg/hgrc
80 echo 'update = python ../printenv.py update' >> .hg/hgrc
81 hg update
81 hg update
82
82
83 # prechangegroup hook can prevent incoming changes
83 # prechangegroup hook can prevent incoming changes
84 cd ../b
84 cd ../b
85 hg -q tip
85 hg -q tip
86 echo '[hooks]' > .hg/hgrc
86 echo '[hooks]' > .hg/hgrc
87 echo 'prechangegroup.forbid = python ../printenv.py prechangegroup.forbid 1' >> .hg/hgrc
87 echo 'prechangegroup.forbid = python ../printenv.py prechangegroup.forbid 1' >> .hg/hgrc
88 hg pull ../a
88 hg pull ../a
89
89
90 # pretxnchangegroup hook can see incoming changes, can roll back txn,
90 # pretxnchangegroup hook can see incoming changes, can roll back txn,
91 # incoming changes no longer there after
91 # incoming changes no longer there after
92 echo '[hooks]' > .hg/hgrc
92 echo '[hooks]' > .hg/hgrc
93 echo 'pretxnchangegroup.forbid0 = hg tip -q' >> .hg/hgrc
93 echo 'pretxnchangegroup.forbid0 = hg tip -q' >> .hg/hgrc
94 echo 'pretxnchangegroup.forbid1 = python ../printenv.py pretxnchangegroup.forbid 1' >> .hg/hgrc
94 echo 'pretxnchangegroup.forbid1 = python ../printenv.py pretxnchangegroup.forbid 1' >> .hg/hgrc
95 hg pull ../a
95 hg pull ../a
96 hg -q tip
96 hg -q tip
97
97
98 # outgoing hooks can see env vars
98 # outgoing hooks can see env vars
99 rm .hg/hgrc
99 rm .hg/hgrc
100 echo '[hooks]' > ../a/.hg/hgrc
100 echo '[hooks]' > ../a/.hg/hgrc
101 echo 'preoutgoing = python ../printenv.py preoutgoing' >> ../a/.hg/hgrc
101 echo 'preoutgoing = python ../printenv.py preoutgoing' >> ../a/.hg/hgrc
102 echo 'outgoing = python ../printenv.py outgoing' >> ../a/.hg/hgrc
102 echo 'outgoing = python ../printenv.py outgoing' >> ../a/.hg/hgrc
103 hg pull ../a
103 hg pull ../a
104 hg rollback
104 hg rollback
105
105
106 # preoutgoing hook can prevent outgoing changes
106 # preoutgoing hook can prevent outgoing changes
107 echo 'preoutgoing.forbid = python ../printenv.py preoutgoing.forbid 1' >> ../a/.hg/hgrc
107 echo 'preoutgoing.forbid = python ../printenv.py preoutgoing.forbid 1' >> ../a/.hg/hgrc
108 hg pull ../a
108 hg pull ../a
109
109
110 # outgoing hooks work for local clones
110 # outgoing hooks work for local clones
111 cd ..
111 cd ..
112 echo '[hooks]' > a/.hg/hgrc
112 echo '[hooks]' > a/.hg/hgrc
113 echo 'preoutgoing = python ../printenv.py preoutgoing' >> a/.hg/hgrc
113 echo 'preoutgoing = python ../printenv.py preoutgoing' >> a/.hg/hgrc
114 echo 'outgoing = python ../printenv.py outgoing' >> a/.hg/hgrc
114 echo 'outgoing = python ../printenv.py outgoing' >> a/.hg/hgrc
115 hg clone a c
115 hg clone a c
116 rm -rf c
116 rm -rf c
117
117
118 # preoutgoing hook can prevent outgoing changes for local clones
118 # preoutgoing hook can prevent outgoing changes for local clones
119 echo 'preoutgoing.forbid = python ../printenv.py preoutgoing.forbid 1' >> a/.hg/hgrc
119 echo 'preoutgoing.forbid = python ../printenv.py preoutgoing.forbid 1' >> a/.hg/hgrc
120 hg clone a zzz
120 hg clone a zzz
121 cd b
121 cd b
122
122
123 cat > hooktests.py <<EOF
123 cat > hooktests.py <<EOF
124 from mercurial import util
124 from mercurial import util
125
125
126 uncallable = 0
126 uncallable = 0
127
127
128 def printargs(args):
128 def printargs(args):
129 args.pop('ui', None)
129 args.pop('ui', None)
130 args.pop('repo', None)
130 args.pop('repo', None)
131 a = list(args.items())
131 a = list(args.items())
132 a.sort()
132 a.sort()
133 print 'hook args:'
133 print 'hook args:'
134 for k, v in a:
134 for k, v in a:
135 print ' ', k, v
135 print ' ', k, v
136
136
137 def passhook(**args):
137 def passhook(**args):
138 printargs(args)
138 printargs(args)
139
139
140 def failhook(**args):
140 def failhook(**args):
141 printargs(args)
141 printargs(args)
142 return True
142 return True
143
143
144 class LocalException(Exception):
144 class LocalException(Exception):
145 pass
145 pass
146
146
147 def raisehook(**args):
147 def raisehook(**args):
148 raise LocalException('exception from hook')
148 raise LocalException('exception from hook')
149
149
150 def aborthook(**args):
150 def aborthook(**args):
151 raise util.Abort('raise abort from hook')
151 raise util.Abort('raise abort from hook')
152
152
153 def brokenhook(**args):
153 def brokenhook(**args):
154 return 1 + {}
154 return 1 + {}
155
155
156 class container:
156 class container:
157 unreachable = 1
157 unreachable = 1
158 EOF
158 EOF
159
159
160 echo '# test python hooks'
160 echo '# test python hooks'
161 PYTHONPATH="`pwd`:$PYTHONPATH"
161 PYTHONPATH="`pwd`:$PYTHONPATH"
162 export PYTHONPATH
162 export PYTHONPATH
163
163
164 echo '[hooks]' > ../a/.hg/hgrc
164 echo '[hooks]' > ../a/.hg/hgrc
165 echo 'preoutgoing.broken = python:hooktests.brokenhook' >> ../a/.hg/hgrc
165 echo 'preoutgoing.broken = python:hooktests.brokenhook' >> ../a/.hg/hgrc
166 hg pull ../a 2>&1 | grep 'raised an exception'
166 hg pull ../a 2>&1 | grep 'raised an exception'
167
167
168 echo '[hooks]' > ../a/.hg/hgrc
168 echo '[hooks]' > ../a/.hg/hgrc
169 echo 'preoutgoing.raise = python:hooktests.raisehook' >> ../a/.hg/hgrc
169 echo 'preoutgoing.raise = python:hooktests.raisehook' >> ../a/.hg/hgrc
170 hg pull ../a 2>&1 | grep 'raised an exception'
170 hg pull ../a 2>&1 | grep 'raised an exception'
171
171
172 echo '[hooks]' > ../a/.hg/hgrc
172 echo '[hooks]' > ../a/.hg/hgrc
173 echo 'preoutgoing.abort = python:hooktests.aborthook' >> ../a/.hg/hgrc
173 echo 'preoutgoing.abort = python:hooktests.aborthook' >> ../a/.hg/hgrc
174 hg pull ../a
174 hg pull ../a
175
175
176 echo '[hooks]' > ../a/.hg/hgrc
176 echo '[hooks]' > ../a/.hg/hgrc
177 echo 'preoutgoing.fail = python:hooktests.failhook' >> ../a/.hg/hgrc
177 echo 'preoutgoing.fail = python:hooktests.failhook' >> ../a/.hg/hgrc
178 hg pull ../a
178 hg pull ../a
179
179
180 echo '[hooks]' > ../a/.hg/hgrc
180 echo '[hooks]' > ../a/.hg/hgrc
181 echo 'preoutgoing.uncallable = python:hooktests.uncallable' >> ../a/.hg/hgrc
181 echo 'preoutgoing.uncallable = python:hooktests.uncallable' >> ../a/.hg/hgrc
182 hg pull ../a
182 hg pull ../a
183
183
184 echo '[hooks]' > ../a/.hg/hgrc
184 echo '[hooks]' > ../a/.hg/hgrc
185 echo 'preoutgoing.nohook = python:hooktests.nohook' >> ../a/.hg/hgrc
185 echo 'preoutgoing.nohook = python:hooktests.nohook' >> ../a/.hg/hgrc
186 hg pull ../a
186 hg pull ../a
187
187
188 echo '[hooks]' > ../a/.hg/hgrc
188 echo '[hooks]' > ../a/.hg/hgrc
189 echo 'preoutgoing.nomodule = python:nomodule' >> ../a/.hg/hgrc
189 echo 'preoutgoing.nomodule = python:nomodule' >> ../a/.hg/hgrc
190 hg pull ../a
190 hg pull ../a
191
191
192 echo '[hooks]' > ../a/.hg/hgrc
192 echo '[hooks]' > ../a/.hg/hgrc
193 echo 'preoutgoing.badmodule = python:nomodule.nowhere' >> ../a/.hg/hgrc
193 echo 'preoutgoing.badmodule = python:nomodule.nowhere' >> ../a/.hg/hgrc
194 hg pull ../a
194 hg pull ../a
195
195
196 echo '[hooks]' > ../a/.hg/hgrc
196 echo '[hooks]' > ../a/.hg/hgrc
197 echo 'preoutgoing.unreachable = python:hooktests.container.unreachable' >> ../a/.hg/hgrc
197 echo 'preoutgoing.unreachable = python:hooktests.container.unreachable' >> ../a/.hg/hgrc
198 hg pull ../a
198 hg pull ../a
199
199
200 echo '[hooks]' > ../a/.hg/hgrc
200 echo '[hooks]' > ../a/.hg/hgrc
201 echo 'preoutgoing.pass = python:hooktests.passhook' >> ../a/.hg/hgrc
201 echo 'preoutgoing.pass = python:hooktests.passhook' >> ../a/.hg/hgrc
202 hg pull ../a
202 hg pull ../a
203
203
204 echo '# make sure --traceback works'
204 echo '# make sure --traceback works'
205 echo '[hooks]' > .hg/hgrc
205 echo '[hooks]' > .hg/hgrc
206 echo 'commit.abort = python:hooktests.aborthook' >> .hg/hgrc
206 echo 'commit.abort = python:hooktests.aborthook' >> .hg/hgrc
207
207
208 echo a >> a
208 echo a >> a
209 hg --traceback commit -A -m a 2>&1 | grep '^Traceback'
209 hg --traceback commit -A -m a 2>&1 | grep '^Traceback'
210
210
211 cd ..
211 cd ..
212 hg init c
212 hg init c
213 cd c
213 cd c
214
214
215 cat > hookext.py <<EOF
215 cat > hookext.py <<EOF
216 def autohook(**args):
216 def autohook(**args):
217 print "Automatically installed hook"
217 print "Automatically installed hook"
218
218
219 def reposetup(ui, repo):
219 def reposetup(ui, repo):
220 repo.ui.setconfig("hooks", "commit.auto", autohook)
220 repo.ui.setconfig("hooks", "commit.auto", autohook)
221 EOF
221 EOF
222 echo '[extensions]' >> .hg/hgrc
222 echo '[extensions]' >> .hg/hgrc
223 echo 'hookext = hookext.py' >> .hg/hgrc
223 echo 'hookext = hookext.py' >> .hg/hgrc
224
224
225 touch foo
225 touch foo
226 hg add foo
226 hg add foo
227 hg ci -m 'add foo'
227 hg ci -m 'add foo'
228 echo >> foo
228 echo >> foo
229 hg ci --debug -m 'change foo' | sed -e 's/ at .*>/>/'
229 hg ci --debug -m 'change foo' | sed -e 's/ at .*>/>/'
230
230
231 hg showconfig hooks | sed -e 's/ at .*>/>/'
231 hg showconfig hooks | sed -e 's/ at .*>/>/'
232
232
233 echo '# test python hook configured with python:[file]:[hook] syntax'
233 echo '# test python hook configured with python:[file]:[hook] syntax'
234 cd ..
234 cd ..
235 mkdir d
235 mkdir d
236 cd d
236 cd d
237 hg init repo
237 hg init repo
238 mkdir hooks
238 mkdir hooks
239
239
240 cd hooks
240 cd hooks
241 cat > testhooks.py <<EOF
241 cat > testhooks.py <<EOF
242 def testhook(**args):
242 def testhook(**args):
243 print 'hook works'
243 print 'hook works'
244 EOF
244 EOF
245 echo '[hooks]' > ../repo/.hg/hgrc
245 echo '[hooks]' > ../repo/.hg/hgrc
246 echo "pre-commit.test = python:`pwd`/testhooks.py:testhook" >> ../repo/.hg/hgrc
246 echo "pre-commit.test = python:`pwd`/testhooks.py:testhook" >> ../repo/.hg/hgrc
247
247
248 cd ../repo
248 cd ../repo
249 hg commit
249 hg commit
250
250
251 cd ../../b
252 echo '# make sure --traceback works on hook import failure'
253 cat > importfail.py <<EOF
254 import somebogusmodule
255 # dereference something in the module to force demandimport to load it
256 somebogusmodule.whatever
257 EOF
258
259 echo '[hooks]' > .hg/hgrc
260 echo 'precommit.importfail = python:importfail.whatever' >> .hg/hgrc
261
262 echo a >> a
263 hg --traceback commit -Ama 2>&1 | grep '^\(exception\|Traceback\|ImportError\)'
264
251 exit 0
265 exit 0
@@ -1,165 +1,173 b''
1 precommit hook: HG_PARENT1=0000000000000000000000000000000000000000
1 precommit hook: HG_PARENT1=0000000000000000000000000000000000000000
2 pretxncommit hook: HG_NODE=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b HG_PARENT1=0000000000000000000000000000000000000000 HG_PENDING=$HGTMP/test-hook/a
2 pretxncommit hook: HG_NODE=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b HG_PARENT1=0000000000000000000000000000000000000000 HG_PENDING=$HGTMP/test-hook/a
3 0:29b62aeb769f
3 0:29b62aeb769f
4 commit hook: HG_NODE=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b HG_PARENT1=0000000000000000000000000000000000000000
4 commit hook: HG_NODE=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b HG_PARENT1=0000000000000000000000000000000000000000
5 commit.b hook: HG_NODE=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b HG_PARENT1=0000000000000000000000000000000000000000
5 commit.b hook: HG_NODE=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b HG_PARENT1=0000000000000000000000000000000000000000
6 updating to branch default
6 updating to branch default
7 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
7 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
8 precommit hook: HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b
8 precommit hook: HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b
9 pretxncommit hook: HG_NODE=b702efe9688826e3a91283852b328b84dbf37bc2 HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b HG_PENDING=$HGTMP/test-hook/a
9 pretxncommit hook: HG_NODE=b702efe9688826e3a91283852b328b84dbf37bc2 HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b HG_PENDING=$HGTMP/test-hook/a
10 1:b702efe96888
10 1:b702efe96888
11 commit hook: HG_NODE=b702efe9688826e3a91283852b328b84dbf37bc2 HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b
11 commit hook: HG_NODE=b702efe9688826e3a91283852b328b84dbf37bc2 HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b
12 commit.b hook: HG_NODE=b702efe9688826e3a91283852b328b84dbf37bc2 HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b
12 commit.b hook: HG_NODE=b702efe9688826e3a91283852b328b84dbf37bc2 HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b
13 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
13 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
14 precommit hook: HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b
14 precommit hook: HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b
15 pretxncommit hook: HG_NODE=1324a5531bac09b329c3845d35ae6a7526874edb HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b HG_PENDING=$HGTMP/test-hook/a
15 pretxncommit hook: HG_NODE=1324a5531bac09b329c3845d35ae6a7526874edb HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b HG_PENDING=$HGTMP/test-hook/a
16 2:1324a5531bac
16 2:1324a5531bac
17 commit hook: HG_NODE=1324a5531bac09b329c3845d35ae6a7526874edb HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b
17 commit hook: HG_NODE=1324a5531bac09b329c3845d35ae6a7526874edb HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b
18 commit.b hook: HG_NODE=1324a5531bac09b329c3845d35ae6a7526874edb HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b
18 commit.b hook: HG_NODE=1324a5531bac09b329c3845d35ae6a7526874edb HG_PARENT1=29b62aeb769fdf78d8d9c5f28b017f76d7ef824b
19 created new head
19 created new head
20 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
20 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
21 (branch merge, don't forget to commit)
21 (branch merge, don't forget to commit)
22 precommit hook: HG_PARENT1=1324a5531bac09b329c3845d35ae6a7526874edb HG_PARENT2=b702efe9688826e3a91283852b328b84dbf37bc2
22 precommit hook: HG_PARENT1=1324a5531bac09b329c3845d35ae6a7526874edb HG_PARENT2=b702efe9688826e3a91283852b328b84dbf37bc2
23 pretxncommit hook: HG_NODE=4c52fb2e402287dd5dc052090682536c8406c321 HG_PARENT1=1324a5531bac09b329c3845d35ae6a7526874edb HG_PARENT2=b702efe9688826e3a91283852b328b84dbf37bc2 HG_PENDING=$HGTMP/test-hook/a
23 pretxncommit hook: HG_NODE=4c52fb2e402287dd5dc052090682536c8406c321 HG_PARENT1=1324a5531bac09b329c3845d35ae6a7526874edb HG_PARENT2=b702efe9688826e3a91283852b328b84dbf37bc2 HG_PENDING=$HGTMP/test-hook/a
24 3:4c52fb2e4022
24 3:4c52fb2e4022
25 commit hook: HG_NODE=4c52fb2e402287dd5dc052090682536c8406c321 HG_PARENT1=1324a5531bac09b329c3845d35ae6a7526874edb HG_PARENT2=b702efe9688826e3a91283852b328b84dbf37bc2
25 commit hook: HG_NODE=4c52fb2e402287dd5dc052090682536c8406c321 HG_PARENT1=1324a5531bac09b329c3845d35ae6a7526874edb HG_PARENT2=b702efe9688826e3a91283852b328b84dbf37bc2
26 commit.b hook: HG_NODE=4c52fb2e402287dd5dc052090682536c8406c321 HG_PARENT1=1324a5531bac09b329c3845d35ae6a7526874edb HG_PARENT2=b702efe9688826e3a91283852b328b84dbf37bc2
26 commit.b hook: HG_NODE=4c52fb2e402287dd5dc052090682536c8406c321 HG_PARENT1=1324a5531bac09b329c3845d35ae6a7526874edb HG_PARENT2=b702efe9688826e3a91283852b328b84dbf37bc2
27 pre-identify hook: HG_ARGS=id
27 pre-identify hook: HG_ARGS=id
28 warning: pre-identify hook exited with status 1
28 warning: pre-identify hook exited with status 1
29 pre-cat hook: HG_ARGS=cat b
29 pre-cat hook: HG_ARGS=cat b
30 post-cat hook: HG_ARGS=cat b HG_RESULT=0
30 post-cat hook: HG_ARGS=cat b HG_RESULT=0
31 b
31 b
32 prechangegroup hook: HG_SOURCE=pull HG_URL=file:
32 prechangegroup hook: HG_SOURCE=pull HG_URL=file:
33 changegroup hook: HG_NODE=b702efe9688826e3a91283852b328b84dbf37bc2 HG_SOURCE=pull HG_URL=file:
33 changegroup hook: HG_NODE=b702efe9688826e3a91283852b328b84dbf37bc2 HG_SOURCE=pull HG_URL=file:
34 incoming hook: HG_NODE=b702efe9688826e3a91283852b328b84dbf37bc2 HG_SOURCE=pull HG_URL=file:
34 incoming hook: HG_NODE=b702efe9688826e3a91283852b328b84dbf37bc2 HG_SOURCE=pull HG_URL=file:
35 incoming hook: HG_NODE=1324a5531bac09b329c3845d35ae6a7526874edb HG_SOURCE=pull HG_URL=file:
35 incoming hook: HG_NODE=1324a5531bac09b329c3845d35ae6a7526874edb HG_SOURCE=pull HG_URL=file:
36 incoming hook: HG_NODE=4c52fb2e402287dd5dc052090682536c8406c321 HG_SOURCE=pull HG_URL=file:
36 incoming hook: HG_NODE=4c52fb2e402287dd5dc052090682536c8406c321 HG_SOURCE=pull HG_URL=file:
37 pulling from ../a
37 pulling from ../a
38 searching for changes
38 searching for changes
39 adding changesets
39 adding changesets
40 adding manifests
40 adding manifests
41 adding file changes
41 adding file changes
42 added 3 changesets with 2 changes to 2 files
42 added 3 changesets with 2 changes to 2 files
43 (run 'hg update' to get a working copy)
43 (run 'hg update' to get a working copy)
44 pretag hook: HG_LOCAL=0 HG_NODE=4c52fb2e402287dd5dc052090682536c8406c321 HG_TAG=a
44 pretag hook: HG_LOCAL=0 HG_NODE=4c52fb2e402287dd5dc052090682536c8406c321 HG_TAG=a
45 precommit hook: HG_PARENT1=4c52fb2e402287dd5dc052090682536c8406c321
45 precommit hook: HG_PARENT1=4c52fb2e402287dd5dc052090682536c8406c321
46 pretxncommit hook: HG_NODE=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_PARENT1=4c52fb2e402287dd5dc052090682536c8406c321 HG_PENDING=$HGTMP/test-hook/a
46 pretxncommit hook: HG_NODE=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_PARENT1=4c52fb2e402287dd5dc052090682536c8406c321 HG_PENDING=$HGTMP/test-hook/a
47 4:8ea2ef7ad3e8
47 4:8ea2ef7ad3e8
48 commit hook: HG_NODE=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_PARENT1=4c52fb2e402287dd5dc052090682536c8406c321
48 commit hook: HG_NODE=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_PARENT1=4c52fb2e402287dd5dc052090682536c8406c321
49 commit.b hook: HG_NODE=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_PARENT1=4c52fb2e402287dd5dc052090682536c8406c321
49 commit.b hook: HG_NODE=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_PARENT1=4c52fb2e402287dd5dc052090682536c8406c321
50 tag hook: HG_LOCAL=0 HG_NODE=4c52fb2e402287dd5dc052090682536c8406c321 HG_TAG=a
50 tag hook: HG_LOCAL=0 HG_NODE=4c52fb2e402287dd5dc052090682536c8406c321 HG_TAG=a
51 pretag hook: HG_LOCAL=1 HG_NODE=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_TAG=la
51 pretag hook: HG_LOCAL=1 HG_NODE=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_TAG=la
52 tag hook: HG_LOCAL=1 HG_NODE=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_TAG=la
52 tag hook: HG_LOCAL=1 HG_NODE=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_TAG=la
53 pretag hook: HG_LOCAL=0 HG_NODE=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_TAG=fa
53 pretag hook: HG_LOCAL=0 HG_NODE=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_TAG=fa
54 pretag.forbid hook: HG_LOCAL=0 HG_NODE=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_TAG=fa
54 pretag.forbid hook: HG_LOCAL=0 HG_NODE=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_TAG=fa
55 abort: pretag.forbid hook exited with status 1
55 abort: pretag.forbid hook exited with status 1
56 pretag hook: HG_LOCAL=1 HG_NODE=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_TAG=fla
56 pretag hook: HG_LOCAL=1 HG_NODE=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_TAG=fla
57 pretag.forbid hook: HG_LOCAL=1 HG_NODE=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_TAG=fla
57 pretag.forbid hook: HG_LOCAL=1 HG_NODE=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_TAG=fla
58 abort: pretag.forbid hook exited with status 1
58 abort: pretag.forbid hook exited with status 1
59 4:8ea2ef7ad3e8
59 4:8ea2ef7ad3e8
60 precommit hook: HG_PARENT1=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198
60 precommit hook: HG_PARENT1=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198
61 pretxncommit hook: HG_NODE=fad284daf8c032148abaffcd745dafeceefceb61 HG_PARENT1=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_PENDING=$HGTMP/test-hook/a
61 pretxncommit hook: HG_NODE=fad284daf8c032148abaffcd745dafeceefceb61 HG_PARENT1=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_PENDING=$HGTMP/test-hook/a
62 5:fad284daf8c0
62 5:fad284daf8c0
63 5:fad284daf8c0
63 5:fad284daf8c0
64 pretxncommit.forbid hook: HG_NODE=fad284daf8c032148abaffcd745dafeceefceb61 HG_PARENT1=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_PENDING=$HGTMP/test-hook/a
64 pretxncommit.forbid hook: HG_NODE=fad284daf8c032148abaffcd745dafeceefceb61 HG_PARENT1=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_PENDING=$HGTMP/test-hook/a
65 transaction abort!
65 transaction abort!
66 rollback completed
66 rollback completed
67 abort: pretxncommit.forbid1 hook exited with status 1
67 abort: pretxncommit.forbid1 hook exited with status 1
68 4:8ea2ef7ad3e8
68 4:8ea2ef7ad3e8
69 precommit hook: HG_PARENT1=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198
69 precommit hook: HG_PARENT1=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198
70 precommit.forbid hook: HG_PARENT1=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198
70 precommit.forbid hook: HG_PARENT1=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198
71 abort: precommit.forbid hook exited with status 1
71 abort: precommit.forbid hook exited with status 1
72 4:8ea2ef7ad3e8
72 4:8ea2ef7ad3e8
73 preupdate hook: HG_PARENT1=b702efe96888
73 preupdate hook: HG_PARENT1=b702efe96888
74 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
74 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
75 preupdate hook: HG_PARENT1=8ea2ef7ad3e8
75 preupdate hook: HG_PARENT1=8ea2ef7ad3e8
76 update hook: HG_ERROR=0 HG_PARENT1=8ea2ef7ad3e8
76 update hook: HG_ERROR=0 HG_PARENT1=8ea2ef7ad3e8
77 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
77 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
78 3:4c52fb2e4022
78 3:4c52fb2e4022
79 prechangegroup.forbid hook: HG_SOURCE=pull HG_URL=file:
79 prechangegroup.forbid hook: HG_SOURCE=pull HG_URL=file:
80 pulling from ../a
80 pulling from ../a
81 searching for changes
81 searching for changes
82 abort: prechangegroup.forbid hook exited with status 1
82 abort: prechangegroup.forbid hook exited with status 1
83 4:8ea2ef7ad3e8
83 4:8ea2ef7ad3e8
84 pretxnchangegroup.forbid hook: HG_NODE=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_PENDING=$HGTMP/test-hook/b HG_SOURCE=pull HG_URL=file:
84 pretxnchangegroup.forbid hook: HG_NODE=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_PENDING=$HGTMP/test-hook/b HG_SOURCE=pull HG_URL=file:
85 pulling from ../a
85 pulling from ../a
86 searching for changes
86 searching for changes
87 adding changesets
87 adding changesets
88 adding manifests
88 adding manifests
89 adding file changes
89 adding file changes
90 added 1 changesets with 1 changes to 1 files
90 added 1 changesets with 1 changes to 1 files
91 transaction abort!
91 transaction abort!
92 rollback completed
92 rollback completed
93 abort: pretxnchangegroup.forbid1 hook exited with status 1
93 abort: pretxnchangegroup.forbid1 hook exited with status 1
94 3:4c52fb2e4022
94 3:4c52fb2e4022
95 preoutgoing hook: HG_SOURCE=pull
95 preoutgoing hook: HG_SOURCE=pull
96 outgoing hook: HG_NODE=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_SOURCE=pull
96 outgoing hook: HG_NODE=8ea2ef7ad3e8cac946c72f1e0c79d6aebc301198 HG_SOURCE=pull
97 pulling from ../a
97 pulling from ../a
98 searching for changes
98 searching for changes
99 adding changesets
99 adding changesets
100 adding manifests
100 adding manifests
101 adding file changes
101 adding file changes
102 added 1 changesets with 1 changes to 1 files
102 added 1 changesets with 1 changes to 1 files
103 (run 'hg update' to get a working copy)
103 (run 'hg update' to get a working copy)
104 rolling back last transaction
104 rolling back last transaction
105 preoutgoing hook: HG_SOURCE=pull
105 preoutgoing hook: HG_SOURCE=pull
106 preoutgoing.forbid hook: HG_SOURCE=pull
106 preoutgoing.forbid hook: HG_SOURCE=pull
107 pulling from ../a
107 pulling from ../a
108 searching for changes
108 searching for changes
109 abort: preoutgoing.forbid hook exited with status 1
109 abort: preoutgoing.forbid hook exited with status 1
110 preoutgoing hook: HG_SOURCE=clone
110 preoutgoing hook: HG_SOURCE=clone
111 outgoing hook: HG_NODE=0000000000000000000000000000000000000000 HG_SOURCE=clone
111 outgoing hook: HG_NODE=0000000000000000000000000000000000000000 HG_SOURCE=clone
112 updating to branch default
112 updating to branch default
113 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
113 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
114 preoutgoing hook: HG_SOURCE=clone
114 preoutgoing hook: HG_SOURCE=clone
115 preoutgoing.forbid hook: HG_SOURCE=clone
115 preoutgoing.forbid hook: HG_SOURCE=clone
116 abort: preoutgoing.forbid hook exited with status 1
116 abort: preoutgoing.forbid hook exited with status 1
117 # test python hooks
117 # test python hooks
118 error: preoutgoing.broken hook raised an exception: unsupported operand type(s) for +: 'int' and 'dict'
118 error: preoutgoing.broken hook raised an exception: unsupported operand type(s) for +: 'int' and 'dict'
119 error: preoutgoing.raise hook raised an exception: exception from hook
119 error: preoutgoing.raise hook raised an exception: exception from hook
120 pulling from ../a
120 pulling from ../a
121 searching for changes
121 searching for changes
122 error: preoutgoing.abort hook failed: raise abort from hook
122 error: preoutgoing.abort hook failed: raise abort from hook
123 abort: raise abort from hook
123 abort: raise abort from hook
124 pulling from ../a
124 pulling from ../a
125 searching for changes
125 searching for changes
126 hook args:
126 hook args:
127 hooktype preoutgoing
127 hooktype preoutgoing
128 source pull
128 source pull
129 abort: preoutgoing.fail hook failed
129 abort: preoutgoing.fail hook failed
130 pulling from ../a
130 pulling from ../a
131 searching for changes
131 searching for changes
132 abort: preoutgoing.uncallable hook is invalid ("hooktests.uncallable" is not callable)
132 abort: preoutgoing.uncallable hook is invalid ("hooktests.uncallable" is not callable)
133 pulling from ../a
133 pulling from ../a
134 searching for changes
134 searching for changes
135 abort: preoutgoing.nohook hook is invalid ("hooktests.nohook" is not defined)
135 abort: preoutgoing.nohook hook is invalid ("hooktests.nohook" is not defined)
136 pulling from ../a
136 pulling from ../a
137 searching for changes
137 searching for changes
138 abort: preoutgoing.nomodule hook is invalid ("nomodule" not in a module)
138 abort: preoutgoing.nomodule hook is invalid ("nomodule" not in a module)
139 pulling from ../a
139 pulling from ../a
140 searching for changes
140 searching for changes
141 abort: preoutgoing.badmodule hook is invalid (import of "nomodule" failed)
141 abort: preoutgoing.badmodule hook is invalid (import of "nomodule" failed)
142 pulling from ../a
142 pulling from ../a
143 searching for changes
143 searching for changes
144 abort: preoutgoing.unreachable hook is invalid (import of "hooktests.container" failed)
144 abort: preoutgoing.unreachable hook is invalid (import of "hooktests.container" failed)
145 pulling from ../a
145 pulling from ../a
146 searching for changes
146 searching for changes
147 hook args:
147 hook args:
148 hooktype preoutgoing
148 hooktype preoutgoing
149 source pull
149 source pull
150 adding changesets
150 adding changesets
151 adding manifests
151 adding manifests
152 adding file changes
152 adding file changes
153 added 1 changesets with 1 changes to 1 files
153 added 1 changesets with 1 changes to 1 files
154 (run 'hg update' to get a working copy)
154 (run 'hg update' to get a working copy)
155 # make sure --traceback works
155 # make sure --traceback works
156 Traceback (most recent call last):
156 Traceback (most recent call last):
157 Automatically installed hook
157 Automatically installed hook
158 foo
158 foo
159 calling hook commit.auto: <function autohook>
159 calling hook commit.auto: <function autohook>
160 Automatically installed hook
160 Automatically installed hook
161 committed changeset 1:52998019f6252a2b893452765fcb0a47351a5708
161 committed changeset 1:52998019f6252a2b893452765fcb0a47351a5708
162 hooks.commit.auto=<function autohook>
162 hooks.commit.auto=<function autohook>
163 # test python hook configured with python:[file]:[hook] syntax
163 # test python hook configured with python:[file]:[hook] syntax
164 hook works
164 hook works
165 nothing changed
165 nothing changed
166 # make sure --traceback works on hook import failure
167 exception from first failed import attempt:
168 Traceback (most recent call last):
169 ImportError: No module named somebogusmodule
170 exception from second failed import attempt:
171 Traceback (most recent call last):
172 ImportError: No module named hgext_importfail
173 Traceback (most recent call last):
General Comments 0
You need to be logged in to leave comments. Login now