##// END OF EJS Templates
transaction: run abort callback in all cases...
marmoute -
r50889:3128018e default
parent child Browse files
Show More
@@ -1,785 +1,786 b''
1 # transaction.py - simple journaling scheme for mercurial
1 # transaction.py - simple journaling scheme for mercurial
2 #
2 #
3 # This transaction scheme is intended to gracefully handle program
3 # This transaction scheme is intended to gracefully handle program
4 # errors and interruptions. More serious failures like system crashes
4 # errors and interruptions. More serious failures like system crashes
5 # can be recovered with an fsck-like tool. As the whole repository is
5 # can be recovered with an fsck-like tool. As the whole repository is
6 # effectively log-structured, this should amount to simply truncating
6 # effectively log-structured, this should amount to simply truncating
7 # anything that isn't referenced in the changelog.
7 # anything that isn't referenced in the changelog.
8 #
8 #
9 # Copyright 2005, 2006 Olivia Mackall <olivia@selenic.com>
9 # Copyright 2005, 2006 Olivia Mackall <olivia@selenic.com>
10 #
10 #
11 # This software may be used and distributed according to the terms of the
11 # This software may be used and distributed according to the terms of the
12 # GNU General Public License version 2 or any later version.
12 # GNU General Public License version 2 or any later version.
13
13
14
14
15 from .i18n import _
15 from .i18n import _
16 from . import (
16 from . import (
17 error,
17 error,
18 pycompat,
18 pycompat,
19 util,
19 util,
20 )
20 )
21 from .utils import stringutil
21 from .utils import stringutil
22
22
23 version = 2
23 version = 2
24
24
25 GEN_GROUP_ALL = b'all'
25 GEN_GROUP_ALL = b'all'
26 GEN_GROUP_PRE_FINALIZE = b'prefinalize'
26 GEN_GROUP_PRE_FINALIZE = b'prefinalize'
27 GEN_GROUP_POST_FINALIZE = b'postfinalize'
27 GEN_GROUP_POST_FINALIZE = b'postfinalize'
28
28
29
29
30 def active(func):
30 def active(func):
31 def _active(self, *args, **kwds):
31 def _active(self, *args, **kwds):
32 if self._count == 0:
32 if self._count == 0:
33 raise error.ProgrammingError(
33 raise error.ProgrammingError(
34 b'cannot use transaction when it is already committed/aborted'
34 b'cannot use transaction when it is already committed/aborted'
35 )
35 )
36 return func(self, *args, **kwds)
36 return func(self, *args, **kwds)
37
37
38 return _active
38 return _active
39
39
40
40
41 def _playback(
41 def _playback(
42 journal,
42 journal,
43 report,
43 report,
44 opener,
44 opener,
45 vfsmap,
45 vfsmap,
46 entries,
46 entries,
47 backupentries,
47 backupentries,
48 unlink=True,
48 unlink=True,
49 checkambigfiles=None,
49 checkambigfiles=None,
50 ):
50 ):
51 for f, o in sorted(dict(entries).items()):
51 for f, o in sorted(dict(entries).items()):
52 if o or not unlink:
52 if o or not unlink:
53 checkambig = checkambigfiles and (f, b'') in checkambigfiles
53 checkambig = checkambigfiles and (f, b'') in checkambigfiles
54 try:
54 try:
55 fp = opener(f, b'a', checkambig=checkambig)
55 fp = opener(f, b'a', checkambig=checkambig)
56 if fp.tell() < o:
56 if fp.tell() < o:
57 raise error.Abort(
57 raise error.Abort(
58 _(
58 _(
59 b"attempted to truncate %s to %d bytes, but it was "
59 b"attempted to truncate %s to %d bytes, but it was "
60 b"already %d bytes\n"
60 b"already %d bytes\n"
61 )
61 )
62 % (f, o, fp.tell())
62 % (f, o, fp.tell())
63 )
63 )
64 fp.truncate(o)
64 fp.truncate(o)
65 fp.close()
65 fp.close()
66 except IOError:
66 except IOError:
67 report(_(b"failed to truncate %s\n") % f)
67 report(_(b"failed to truncate %s\n") % f)
68 raise
68 raise
69 else:
69 else:
70 try:
70 try:
71 opener.unlink(f)
71 opener.unlink(f)
72 except FileNotFoundError:
72 except FileNotFoundError:
73 pass
73 pass
74
74
75 backupfiles = []
75 backupfiles = []
76 for l, f, b, c in backupentries:
76 for l, f, b, c in backupentries:
77 if l not in vfsmap and c:
77 if l not in vfsmap and c:
78 report(b"couldn't handle %s: unknown cache location %s\n" % (b, l))
78 report(b"couldn't handle %s: unknown cache location %s\n" % (b, l))
79 vfs = vfsmap[l]
79 vfs = vfsmap[l]
80 try:
80 try:
81 if f and b:
81 if f and b:
82 filepath = vfs.join(f)
82 filepath = vfs.join(f)
83 backuppath = vfs.join(b)
83 backuppath = vfs.join(b)
84 checkambig = checkambigfiles and (f, l) in checkambigfiles
84 checkambig = checkambigfiles and (f, l) in checkambigfiles
85 try:
85 try:
86 util.copyfile(backuppath, filepath, checkambig=checkambig)
86 util.copyfile(backuppath, filepath, checkambig=checkambig)
87 backupfiles.append(b)
87 backupfiles.append(b)
88 except IOError as exc:
88 except IOError as exc:
89 e_msg = stringutil.forcebytestr(exc)
89 e_msg = stringutil.forcebytestr(exc)
90 report(_(b"failed to recover %s (%s)\n") % (f, e_msg))
90 report(_(b"failed to recover %s (%s)\n") % (f, e_msg))
91 else:
91 else:
92 target = f or b
92 target = f or b
93 try:
93 try:
94 vfs.unlink(target)
94 vfs.unlink(target)
95 except FileNotFoundError:
95 except FileNotFoundError:
96 pass
96 pass
97 except (IOError, OSError, error.Abort):
97 except (IOError, OSError, error.Abort):
98 if not c:
98 if not c:
99 raise
99 raise
100
100
101 backuppath = b"%s.backupfiles" % journal
101 backuppath = b"%s.backupfiles" % journal
102 if opener.exists(backuppath):
102 if opener.exists(backuppath):
103 opener.unlink(backuppath)
103 opener.unlink(backuppath)
104 opener.unlink(journal)
104 opener.unlink(journal)
105 try:
105 try:
106 for f in backupfiles:
106 for f in backupfiles:
107 if opener.exists(f):
107 if opener.exists(f):
108 opener.unlink(f)
108 opener.unlink(f)
109 except (IOError, OSError, error.Abort):
109 except (IOError, OSError, error.Abort):
110 # only pure backup file remains, it is sage to ignore any error
110 # only pure backup file remains, it is sage to ignore any error
111 pass
111 pass
112
112
113
113
114 class transaction(util.transactional):
114 class transaction(util.transactional):
115 def __init__(
115 def __init__(
116 self,
116 self,
117 report,
117 report,
118 opener,
118 opener,
119 vfsmap,
119 vfsmap,
120 journalname,
120 journalname,
121 undoname=None,
121 undoname=None,
122 after=None,
122 after=None,
123 createmode=None,
123 createmode=None,
124 validator=None,
124 validator=None,
125 releasefn=None,
125 releasefn=None,
126 checkambigfiles=None,
126 checkambigfiles=None,
127 name='<unnamed>',
127 name='<unnamed>',
128 ):
128 ):
129 """Begin a new transaction
129 """Begin a new transaction
130
130
131 Begins a new transaction that allows rolling back writes in the event of
131 Begins a new transaction that allows rolling back writes in the event of
132 an exception.
132 an exception.
133
133
134 * `after`: called after the transaction has been committed
134 * `after`: called after the transaction has been committed
135 * `createmode`: the mode of the journal file that will be created
135 * `createmode`: the mode of the journal file that will be created
136 * `releasefn`: called after releasing (with transaction and result)
136 * `releasefn`: called after releasing (with transaction and result)
137
137
138 `checkambigfiles` is a set of (path, vfs-location) tuples,
138 `checkambigfiles` is a set of (path, vfs-location) tuples,
139 which determine whether file stat ambiguity should be avoided
139 which determine whether file stat ambiguity should be avoided
140 for corresponded files.
140 for corresponded files.
141 """
141 """
142 self._count = 1
142 self._count = 1
143 self._usages = 1
143 self._usages = 1
144 self._report = report
144 self._report = report
145 # a vfs to the store content
145 # a vfs to the store content
146 self._opener = opener
146 self._opener = opener
147 # a map to access file in various {location -> vfs}
147 # a map to access file in various {location -> vfs}
148 vfsmap = vfsmap.copy()
148 vfsmap = vfsmap.copy()
149 vfsmap[b''] = opener # set default value
149 vfsmap[b''] = opener # set default value
150 self._vfsmap = vfsmap
150 self._vfsmap = vfsmap
151 self._after = after
151 self._after = after
152 self._offsetmap = {}
152 self._offsetmap = {}
153 self._newfiles = set()
153 self._newfiles = set()
154 self._journal = journalname
154 self._journal = journalname
155 self._undoname = undoname
155 self._undoname = undoname
156 self._queue = []
156 self._queue = []
157 # A callback to do something just after releasing transaction.
157 # A callback to do something just after releasing transaction.
158 if releasefn is None:
158 if releasefn is None:
159 releasefn = lambda tr, success: None
159 releasefn = lambda tr, success: None
160 self._releasefn = releasefn
160 self._releasefn = releasefn
161
161
162 self._checkambigfiles = set()
162 self._checkambigfiles = set()
163 if checkambigfiles:
163 if checkambigfiles:
164 self._checkambigfiles.update(checkambigfiles)
164 self._checkambigfiles.update(checkambigfiles)
165
165
166 self._names = [name]
166 self._names = [name]
167
167
168 # A dict dedicated to precisely tracking the changes introduced in the
168 # A dict dedicated to precisely tracking the changes introduced in the
169 # transaction.
169 # transaction.
170 self.changes = {}
170 self.changes = {}
171
171
172 # a dict of arguments to be passed to hooks
172 # a dict of arguments to be passed to hooks
173 self.hookargs = {}
173 self.hookargs = {}
174 self._file = opener.open(self._journal, b"w+")
174 self._file = opener.open(self._journal, b"w+")
175
175
176 # a list of ('location', 'path', 'backuppath', cache) entries.
176 # a list of ('location', 'path', 'backuppath', cache) entries.
177 # - if 'backuppath' is empty, no file existed at backup time
177 # - if 'backuppath' is empty, no file existed at backup time
178 # - if 'path' is empty, this is a temporary transaction file
178 # - if 'path' is empty, this is a temporary transaction file
179 # - if 'location' is not empty, the path is outside main opener reach.
179 # - if 'location' is not empty, the path is outside main opener reach.
180 # use 'location' value as a key in a vfsmap to find the right 'vfs'
180 # use 'location' value as a key in a vfsmap to find the right 'vfs'
181 # (cache is currently unused)
181 # (cache is currently unused)
182 self._backupentries = []
182 self._backupentries = []
183 self._backupmap = {}
183 self._backupmap = {}
184 self._backupjournal = b"%s.backupfiles" % self._journal
184 self._backupjournal = b"%s.backupfiles" % self._journal
185 self._backupsfile = opener.open(self._backupjournal, b'w')
185 self._backupsfile = opener.open(self._backupjournal, b'w')
186 self._backupsfile.write(b'%d\n' % version)
186 self._backupsfile.write(b'%d\n' % version)
187
187
188 if createmode is not None:
188 if createmode is not None:
189 opener.chmod(self._journal, createmode & 0o666)
189 opener.chmod(self._journal, createmode & 0o666)
190 opener.chmod(self._backupjournal, createmode & 0o666)
190 opener.chmod(self._backupjournal, createmode & 0o666)
191
191
192 # hold file generations to be performed on commit
192 # hold file generations to be performed on commit
193 self._filegenerators = {}
193 self._filegenerators = {}
194 # hold callback to write pending data for hooks
194 # hold callback to write pending data for hooks
195 self._pendingcallback = {}
195 self._pendingcallback = {}
196 # True is any pending data have been written ever
196 # True is any pending data have been written ever
197 self._anypending = False
197 self._anypending = False
198 # holds callback to call when writing the transaction
198 # holds callback to call when writing the transaction
199 self._finalizecallback = {}
199 self._finalizecallback = {}
200 # holds callback to call when validating the transaction
200 # holds callback to call when validating the transaction
201 # should raise exception if anything is wrong
201 # should raise exception if anything is wrong
202 self._validatecallback = {}
202 self._validatecallback = {}
203 if validator is not None:
203 if validator is not None:
204 self._validatecallback[b'001-userhooks'] = validator
204 self._validatecallback[b'001-userhooks'] = validator
205 # hold callback for post transaction close
205 # hold callback for post transaction close
206 self._postclosecallback = {}
206 self._postclosecallback = {}
207 # holds callbacks to call during abort
207 # holds callbacks to call during abort
208 self._abortcallback = {}
208 self._abortcallback = {}
209
209
210 def __repr__(self):
210 def __repr__(self):
211 name = '/'.join(self._names)
211 name = '/'.join(self._names)
212 return '<transaction name=%s, count=%d, usages=%d>' % (
212 return '<transaction name=%s, count=%d, usages=%d>' % (
213 name,
213 name,
214 self._count,
214 self._count,
215 self._usages,
215 self._usages,
216 )
216 )
217
217
218 def __del__(self):
218 def __del__(self):
219 if self._journal:
219 if self._journal:
220 self._abort()
220 self._abort()
221
221
222 @property
222 @property
223 def finalized(self):
223 def finalized(self):
224 return self._finalizecallback is None
224 return self._finalizecallback is None
225
225
226 @active
226 @active
227 def startgroup(self):
227 def startgroup(self):
228 """delay registration of file entry
228 """delay registration of file entry
229
229
230 This is used by strip to delay vision of strip offset. The transaction
230 This is used by strip to delay vision of strip offset. The transaction
231 sees either none or all of the strip actions to be done."""
231 sees either none or all of the strip actions to be done."""
232 self._queue.append([])
232 self._queue.append([])
233
233
234 @active
234 @active
235 def endgroup(self):
235 def endgroup(self):
236 """apply delayed registration of file entry.
236 """apply delayed registration of file entry.
237
237
238 This is used by strip to delay vision of strip offset. The transaction
238 This is used by strip to delay vision of strip offset. The transaction
239 sees either none or all of the strip actions to be done."""
239 sees either none or all of the strip actions to be done."""
240 q = self._queue.pop()
240 q = self._queue.pop()
241 for f, o in q:
241 for f, o in q:
242 self._addentry(f, o)
242 self._addentry(f, o)
243
243
244 @active
244 @active
245 def add(self, file, offset):
245 def add(self, file, offset):
246 """record the state of an append-only file before update"""
246 """record the state of an append-only file before update"""
247 if (
247 if (
248 file in self._newfiles
248 file in self._newfiles
249 or file in self._offsetmap
249 or file in self._offsetmap
250 or file in self._backupmap
250 or file in self._backupmap
251 ):
251 ):
252 return
252 return
253 if self._queue:
253 if self._queue:
254 self._queue[-1].append((file, offset))
254 self._queue[-1].append((file, offset))
255 return
255 return
256
256
257 self._addentry(file, offset)
257 self._addentry(file, offset)
258
258
259 def _addentry(self, file, offset):
259 def _addentry(self, file, offset):
260 """add a append-only entry to memory and on-disk state"""
260 """add a append-only entry to memory and on-disk state"""
261 if (
261 if (
262 file in self._newfiles
262 file in self._newfiles
263 or file in self._offsetmap
263 or file in self._offsetmap
264 or file in self._backupmap
264 or file in self._backupmap
265 ):
265 ):
266 return
266 return
267 if offset:
267 if offset:
268 self._offsetmap[file] = offset
268 self._offsetmap[file] = offset
269 else:
269 else:
270 self._newfiles.add(file)
270 self._newfiles.add(file)
271 # add enough data to the journal to do the truncate
271 # add enough data to the journal to do the truncate
272 self._file.write(b"%s\0%d\n" % (file, offset))
272 self._file.write(b"%s\0%d\n" % (file, offset))
273 self._file.flush()
273 self._file.flush()
274
274
275 @active
275 @active
276 def addbackup(self, file, hardlink=True, location=b''):
276 def addbackup(self, file, hardlink=True, location=b''):
277 """Adds a backup of the file to the transaction
277 """Adds a backup of the file to the transaction
278
278
279 Calling addbackup() creates a hardlink backup of the specified file
279 Calling addbackup() creates a hardlink backup of the specified file
280 that is used to recover the file in the event of the transaction
280 that is used to recover the file in the event of the transaction
281 aborting.
281 aborting.
282
282
283 * `file`: the file path, relative to .hg/store
283 * `file`: the file path, relative to .hg/store
284 * `hardlink`: use a hardlink to quickly create the backup
284 * `hardlink`: use a hardlink to quickly create the backup
285 """
285 """
286 if self._queue:
286 if self._queue:
287 msg = b'cannot use transaction.addbackup inside "group"'
287 msg = b'cannot use transaction.addbackup inside "group"'
288 raise error.ProgrammingError(msg)
288 raise error.ProgrammingError(msg)
289
289
290 if (
290 if (
291 file in self._newfiles
291 file in self._newfiles
292 or file in self._offsetmap
292 or file in self._offsetmap
293 or file in self._backupmap
293 or file in self._backupmap
294 ):
294 ):
295 return
295 return
296 vfs = self._vfsmap[location]
296 vfs = self._vfsmap[location]
297 dirname, filename = vfs.split(file)
297 dirname, filename = vfs.split(file)
298 backupfilename = b"%s.backup.%s" % (self._journal, filename)
298 backupfilename = b"%s.backup.%s" % (self._journal, filename)
299 backupfile = vfs.reljoin(dirname, backupfilename)
299 backupfile = vfs.reljoin(dirname, backupfilename)
300 if vfs.exists(file):
300 if vfs.exists(file):
301 filepath = vfs.join(file)
301 filepath = vfs.join(file)
302 backuppath = vfs.join(backupfile)
302 backuppath = vfs.join(backupfile)
303 util.copyfile(filepath, backuppath, hardlink=hardlink)
303 util.copyfile(filepath, backuppath, hardlink=hardlink)
304 else:
304 else:
305 backupfile = b''
305 backupfile = b''
306
306
307 self._addbackupentry((location, file, backupfile, False))
307 self._addbackupentry((location, file, backupfile, False))
308
308
309 def _addbackupentry(self, entry):
309 def _addbackupentry(self, entry):
310 """register a new backup entry and write it to disk"""
310 """register a new backup entry and write it to disk"""
311 self._backupentries.append(entry)
311 self._backupentries.append(entry)
312 self._backupmap[entry[1]] = len(self._backupentries) - 1
312 self._backupmap[entry[1]] = len(self._backupentries) - 1
313 self._backupsfile.write(b"%s\0%s\0%s\0%d\n" % entry)
313 self._backupsfile.write(b"%s\0%s\0%s\0%d\n" % entry)
314 self._backupsfile.flush()
314 self._backupsfile.flush()
315
315
316 @active
316 @active
317 def registertmp(self, tmpfile, location=b''):
317 def registertmp(self, tmpfile, location=b''):
318 """register a temporary transaction file
318 """register a temporary transaction file
319
319
320 Such files will be deleted when the transaction exits (on both
320 Such files will be deleted when the transaction exits (on both
321 failure and success).
321 failure and success).
322 """
322 """
323 self._addbackupentry((location, b'', tmpfile, False))
323 self._addbackupentry((location, b'', tmpfile, False))
324
324
325 @active
325 @active
326 def addfilegenerator(
326 def addfilegenerator(
327 self,
327 self,
328 genid,
328 genid,
329 filenames,
329 filenames,
330 genfunc,
330 genfunc,
331 order=0,
331 order=0,
332 location=b'',
332 location=b'',
333 post_finalize=False,
333 post_finalize=False,
334 ):
334 ):
335 """add a function to generates some files at transaction commit
335 """add a function to generates some files at transaction commit
336
336
337 The `genfunc` argument is a function capable of generating proper
337 The `genfunc` argument is a function capable of generating proper
338 content of each entry in the `filename` tuple.
338 content of each entry in the `filename` tuple.
339
339
340 At transaction close time, `genfunc` will be called with one file
340 At transaction close time, `genfunc` will be called with one file
341 object argument per entries in `filenames`.
341 object argument per entries in `filenames`.
342
342
343 The transaction itself is responsible for the backup, creation and
343 The transaction itself is responsible for the backup, creation and
344 final write of such file.
344 final write of such file.
345
345
346 The `genid` argument is used to ensure the same set of file is only
346 The `genid` argument is used to ensure the same set of file is only
347 generated once. Call to `addfilegenerator` for a `genid` already
347 generated once. Call to `addfilegenerator` for a `genid` already
348 present will overwrite the old entry.
348 present will overwrite the old entry.
349
349
350 The `order` argument may be used to control the order in which multiple
350 The `order` argument may be used to control the order in which multiple
351 generator will be executed.
351 generator will be executed.
352
352
353 The `location` arguments may be used to indicate the files are located
353 The `location` arguments may be used to indicate the files are located
354 outside of the the standard directory for transaction. It should match
354 outside of the the standard directory for transaction. It should match
355 one of the key of the `transaction.vfsmap` dictionary.
355 one of the key of the `transaction.vfsmap` dictionary.
356
356
357 The `post_finalize` argument can be set to `True` for file generation
357 The `post_finalize` argument can be set to `True` for file generation
358 that must be run after the transaction has been finalized.
358 that must be run after the transaction has been finalized.
359 """
359 """
360 # For now, we are unable to do proper backup and restore of custom vfs
360 # For now, we are unable to do proper backup and restore of custom vfs
361 # but for bookmarks that are handled outside this mechanism.
361 # but for bookmarks that are handled outside this mechanism.
362 entry = (order, filenames, genfunc, location, post_finalize)
362 entry = (order, filenames, genfunc, location, post_finalize)
363 self._filegenerators[genid] = entry
363 self._filegenerators[genid] = entry
364
364
365 @active
365 @active
366 def removefilegenerator(self, genid):
366 def removefilegenerator(self, genid):
367 """reverse of addfilegenerator, remove a file generator function"""
367 """reverse of addfilegenerator, remove a file generator function"""
368 if genid in self._filegenerators:
368 if genid in self._filegenerators:
369 del self._filegenerators[genid]
369 del self._filegenerators[genid]
370
370
371 def _generatefiles(self, suffix=b'', group=GEN_GROUP_ALL):
371 def _generatefiles(self, suffix=b'', group=GEN_GROUP_ALL):
372 # write files registered for generation
372 # write files registered for generation
373 any = False
373 any = False
374
374
375 if group == GEN_GROUP_ALL:
375 if group == GEN_GROUP_ALL:
376 skip_post = skip_pre = False
376 skip_post = skip_pre = False
377 else:
377 else:
378 skip_pre = group == GEN_GROUP_POST_FINALIZE
378 skip_pre = group == GEN_GROUP_POST_FINALIZE
379 skip_post = group == GEN_GROUP_PRE_FINALIZE
379 skip_post = group == GEN_GROUP_PRE_FINALIZE
380
380
381 for id, entry in sorted(self._filegenerators.items()):
381 for id, entry in sorted(self._filegenerators.items()):
382 any = True
382 any = True
383 order, filenames, genfunc, location, post_finalize = entry
383 order, filenames, genfunc, location, post_finalize = entry
384
384
385 # for generation at closing, check if it's before or after finalize
385 # for generation at closing, check if it's before or after finalize
386 if skip_post and post_finalize:
386 if skip_post and post_finalize:
387 continue
387 continue
388 elif skip_pre and not post_finalize:
388 elif skip_pre and not post_finalize:
389 continue
389 continue
390
390
391 vfs = self._vfsmap[location]
391 vfs = self._vfsmap[location]
392 files = []
392 files = []
393 try:
393 try:
394 for name in filenames:
394 for name in filenames:
395 name += suffix
395 name += suffix
396 if suffix:
396 if suffix:
397 self.registertmp(name, location=location)
397 self.registertmp(name, location=location)
398 checkambig = False
398 checkambig = False
399 else:
399 else:
400 self.addbackup(name, location=location)
400 self.addbackup(name, location=location)
401 checkambig = (name, location) in self._checkambigfiles
401 checkambig = (name, location) in self._checkambigfiles
402 files.append(
402 files.append(
403 vfs(name, b'w', atomictemp=True, checkambig=checkambig)
403 vfs(name, b'w', atomictemp=True, checkambig=checkambig)
404 )
404 )
405 genfunc(*files)
405 genfunc(*files)
406 for f in files:
406 for f in files:
407 f.close()
407 f.close()
408 # skip discard() loop since we're sure no open file remains
408 # skip discard() loop since we're sure no open file remains
409 del files[:]
409 del files[:]
410 finally:
410 finally:
411 for f in files:
411 for f in files:
412 f.discard()
412 f.discard()
413 return any
413 return any
414
414
415 @active
415 @active
416 def findoffset(self, file):
416 def findoffset(self, file):
417 if file in self._newfiles:
417 if file in self._newfiles:
418 return 0
418 return 0
419 return self._offsetmap.get(file)
419 return self._offsetmap.get(file)
420
420
421 @active
421 @active
422 def readjournal(self):
422 def readjournal(self):
423 self._file.seek(0)
423 self._file.seek(0)
424 entries = []
424 entries = []
425 for l in self._file.readlines():
425 for l in self._file.readlines():
426 file, troffset = l.split(b'\0')
426 file, troffset = l.split(b'\0')
427 entries.append((file, int(troffset)))
427 entries.append((file, int(troffset)))
428 return entries
428 return entries
429
429
430 @active
430 @active
431 def replace(self, file, offset):
431 def replace(self, file, offset):
432 """
432 """
433 replace can only replace already committed entries
433 replace can only replace already committed entries
434 that are not pending in the queue
434 that are not pending in the queue
435 """
435 """
436 if file in self._newfiles:
436 if file in self._newfiles:
437 if not offset:
437 if not offset:
438 return
438 return
439 self._newfiles.remove(file)
439 self._newfiles.remove(file)
440 self._offsetmap[file] = offset
440 self._offsetmap[file] = offset
441 elif file in self._offsetmap:
441 elif file in self._offsetmap:
442 if not offset:
442 if not offset:
443 del self._offsetmap[file]
443 del self._offsetmap[file]
444 self._newfiles.add(file)
444 self._newfiles.add(file)
445 else:
445 else:
446 self._offsetmap[file] = offset
446 self._offsetmap[file] = offset
447 else:
447 else:
448 raise KeyError(file)
448 raise KeyError(file)
449 self._file.write(b"%s\0%d\n" % (file, offset))
449 self._file.write(b"%s\0%d\n" % (file, offset))
450 self._file.flush()
450 self._file.flush()
451
451
452 @active
452 @active
453 def nest(self, name='<unnamed>'):
453 def nest(self, name='<unnamed>'):
454 self._count += 1
454 self._count += 1
455 self._usages += 1
455 self._usages += 1
456 self._names.append(name)
456 self._names.append(name)
457 return self
457 return self
458
458
459 def release(self):
459 def release(self):
460 if self._count > 0:
460 if self._count > 0:
461 self._usages -= 1
461 self._usages -= 1
462 if self._names:
462 if self._names:
463 self._names.pop()
463 self._names.pop()
464 # if the transaction scopes are left without being closed, fail
464 # if the transaction scopes are left without being closed, fail
465 if self._count > 0 and self._usages == 0:
465 if self._count > 0 and self._usages == 0:
466 self._abort()
466 self._abort()
467
467
468 def running(self):
468 def running(self):
469 return self._count > 0
469 return self._count > 0
470
470
471 def addpending(self, category, callback):
471 def addpending(self, category, callback):
472 """add a callback to be called when the transaction is pending
472 """add a callback to be called when the transaction is pending
473
473
474 The transaction will be given as callback's first argument.
474 The transaction will be given as callback's first argument.
475
475
476 Category is a unique identifier to allow overwriting an old callback
476 Category is a unique identifier to allow overwriting an old callback
477 with a newer callback.
477 with a newer callback.
478 """
478 """
479 self._pendingcallback[category] = callback
479 self._pendingcallback[category] = callback
480
480
481 @active
481 @active
482 def writepending(self):
482 def writepending(self):
483 """write pending file to temporary version
483 """write pending file to temporary version
484
484
485 This is used to allow hooks to view a transaction before commit"""
485 This is used to allow hooks to view a transaction before commit"""
486 categories = sorted(self._pendingcallback)
486 categories = sorted(self._pendingcallback)
487 for cat in categories:
487 for cat in categories:
488 # remove callback since the data will have been flushed
488 # remove callback since the data will have been flushed
489 any = self._pendingcallback.pop(cat)(self)
489 any = self._pendingcallback.pop(cat)(self)
490 self._anypending = self._anypending or any
490 self._anypending = self._anypending or any
491 self._anypending |= self._generatefiles(suffix=b'.pending')
491 self._anypending |= self._generatefiles(suffix=b'.pending')
492 return self._anypending
492 return self._anypending
493
493
494 @active
494 @active
495 def hasfinalize(self, category):
495 def hasfinalize(self, category):
496 """check is a callback already exist for a category"""
496 """check is a callback already exist for a category"""
497 return category in self._finalizecallback
497 return category in self._finalizecallback
498
498
499 @active
499 @active
500 def addfinalize(self, category, callback):
500 def addfinalize(self, category, callback):
501 """add a callback to be called when the transaction is closed
501 """add a callback to be called when the transaction is closed
502
502
503 The transaction will be given as callback's first argument.
503 The transaction will be given as callback's first argument.
504
504
505 Category is a unique identifier to allow overwriting old callbacks with
505 Category is a unique identifier to allow overwriting old callbacks with
506 newer callbacks.
506 newer callbacks.
507 """
507 """
508 self._finalizecallback[category] = callback
508 self._finalizecallback[category] = callback
509
509
510 @active
510 @active
511 def addpostclose(self, category, callback):
511 def addpostclose(self, category, callback):
512 """add or replace a callback to be called after the transaction closed
512 """add or replace a callback to be called after the transaction closed
513
513
514 The transaction will be given as callback's first argument.
514 The transaction will be given as callback's first argument.
515
515
516 Category is a unique identifier to allow overwriting an old callback
516 Category is a unique identifier to allow overwriting an old callback
517 with a newer callback.
517 with a newer callback.
518 """
518 """
519 self._postclosecallback[category] = callback
519 self._postclosecallback[category] = callback
520
520
521 @active
521 @active
522 def getpostclose(self, category):
522 def getpostclose(self, category):
523 """return a postclose callback added before, or None"""
523 """return a postclose callback added before, or None"""
524 return self._postclosecallback.get(category, None)
524 return self._postclosecallback.get(category, None)
525
525
526 @active
526 @active
527 def addabort(self, category, callback):
527 def addabort(self, category, callback):
528 """add a callback to be called when the transaction is aborted.
528 """add a callback to be called when the transaction is aborted.
529
529
530 The transaction will be given as the first argument to the callback.
530 The transaction will be given as the first argument to the callback.
531
531
532 Category is a unique identifier to allow overwriting an old callback
532 Category is a unique identifier to allow overwriting an old callback
533 with a newer callback.
533 with a newer callback.
534 """
534 """
535 self._abortcallback[category] = callback
535 self._abortcallback[category] = callback
536
536
537 @active
537 @active
538 def addvalidator(self, category, callback):
538 def addvalidator(self, category, callback):
539 """adds a callback to be called when validating the transaction.
539 """adds a callback to be called when validating the transaction.
540
540
541 The transaction will be given as the first argument to the callback.
541 The transaction will be given as the first argument to the callback.
542
542
543 callback should raise exception if to abort transaction"""
543 callback should raise exception if to abort transaction"""
544 self._validatecallback[category] = callback
544 self._validatecallback[category] = callback
545
545
546 @active
546 @active
547 def close(self):
547 def close(self):
548 '''commit the transaction'''
548 '''commit the transaction'''
549 if self._count == 1:
549 if self._count == 1:
550 for category in sorted(self._validatecallback):
550 for category in sorted(self._validatecallback):
551 self._validatecallback[category](self)
551 self._validatecallback[category](self)
552 self._validatecallback = None # Help prevent cycles.
552 self._validatecallback = None # Help prevent cycles.
553 self._generatefiles(group=GEN_GROUP_PRE_FINALIZE)
553 self._generatefiles(group=GEN_GROUP_PRE_FINALIZE)
554 while self._finalizecallback:
554 while self._finalizecallback:
555 callbacks = self._finalizecallback
555 callbacks = self._finalizecallback
556 self._finalizecallback = {}
556 self._finalizecallback = {}
557 categories = sorted(callbacks)
557 categories = sorted(callbacks)
558 for cat in categories:
558 for cat in categories:
559 callbacks[cat](self)
559 callbacks[cat](self)
560 # Prevent double usage and help clear cycles.
560 # Prevent double usage and help clear cycles.
561 self._finalizecallback = None
561 self._finalizecallback = None
562 self._generatefiles(group=GEN_GROUP_POST_FINALIZE)
562 self._generatefiles(group=GEN_GROUP_POST_FINALIZE)
563
563
564 self._count -= 1
564 self._count -= 1
565 if self._count != 0:
565 if self._count != 0:
566 return
566 return
567 self._file.close()
567 self._file.close()
568 self._backupsfile.close()
568 self._backupsfile.close()
569 # cleanup temporary files
569 # cleanup temporary files
570 for l, f, b, c in self._backupentries:
570 for l, f, b, c in self._backupentries:
571 if l not in self._vfsmap and c:
571 if l not in self._vfsmap and c:
572 self._report(
572 self._report(
573 b"couldn't remove %s: unknown cache location %s\n" % (b, l)
573 b"couldn't remove %s: unknown cache location %s\n" % (b, l)
574 )
574 )
575 continue
575 continue
576 vfs = self._vfsmap[l]
576 vfs = self._vfsmap[l]
577 if not f and b and vfs.exists(b):
577 if not f and b and vfs.exists(b):
578 try:
578 try:
579 vfs.unlink(b)
579 vfs.unlink(b)
580 except (IOError, OSError, error.Abort) as inst:
580 except (IOError, OSError, error.Abort) as inst:
581 if not c:
581 if not c:
582 raise
582 raise
583 # Abort may be raise by read only opener
583 # Abort may be raise by read only opener
584 self._report(
584 self._report(
585 b"couldn't remove %s: %s\n" % (vfs.join(b), inst)
585 b"couldn't remove %s: %s\n" % (vfs.join(b), inst)
586 )
586 )
587 self._offsetmap = {}
587 self._offsetmap = {}
588 self._newfiles = set()
588 self._newfiles = set()
589 self._writeundo()
589 self._writeundo()
590 if self._after:
590 if self._after:
591 self._after()
591 self._after()
592 self._after = None # Help prevent cycles.
592 self._after = None # Help prevent cycles.
593 if self._opener.isfile(self._backupjournal):
593 if self._opener.isfile(self._backupjournal):
594 self._opener.unlink(self._backupjournal)
594 self._opener.unlink(self._backupjournal)
595 if self._opener.isfile(self._journal):
595 if self._opener.isfile(self._journal):
596 self._opener.unlink(self._journal)
596 self._opener.unlink(self._journal)
597 for l, _f, b, c in self._backupentries:
597 for l, _f, b, c in self._backupentries:
598 if l not in self._vfsmap and c:
598 if l not in self._vfsmap and c:
599 self._report(
599 self._report(
600 b"couldn't remove %s: unknown cache location"
600 b"couldn't remove %s: unknown cache location"
601 b"%s\n" % (b, l)
601 b"%s\n" % (b, l)
602 )
602 )
603 continue
603 continue
604 vfs = self._vfsmap[l]
604 vfs = self._vfsmap[l]
605 if b and vfs.exists(b):
605 if b and vfs.exists(b):
606 try:
606 try:
607 vfs.unlink(b)
607 vfs.unlink(b)
608 except (IOError, OSError, error.Abort) as inst:
608 except (IOError, OSError, error.Abort) as inst:
609 if not c:
609 if not c:
610 raise
610 raise
611 # Abort may be raise by read only opener
611 # Abort may be raise by read only opener
612 self._report(
612 self._report(
613 b"couldn't remove %s: %s\n" % (vfs.join(b), inst)
613 b"couldn't remove %s: %s\n" % (vfs.join(b), inst)
614 )
614 )
615 self._backupentries = []
615 self._backupentries = []
616 self._journal = None
616 self._journal = None
617
617
618 self._releasefn(self, True) # notify success of closing transaction
618 self._releasefn(self, True) # notify success of closing transaction
619 self._releasefn = None # Help prevent cycles.
619 self._releasefn = None # Help prevent cycles.
620
620
621 # run post close action
621 # run post close action
622 categories = sorted(self._postclosecallback)
622 categories = sorted(self._postclosecallback)
623 for cat in categories:
623 for cat in categories:
624 self._postclosecallback[cat](self)
624 self._postclosecallback[cat](self)
625 # Prevent double usage and help clear cycles.
625 # Prevent double usage and help clear cycles.
626 self._postclosecallback = None
626 self._postclosecallback = None
627
627
628 @active
628 @active
629 def abort(self):
629 def abort(self):
630 """abort the transaction (generally called on error, or when the
630 """abort the transaction (generally called on error, or when the
631 transaction is not explicitly committed before going out of
631 transaction is not explicitly committed before going out of
632 scope)"""
632 scope)"""
633 self._abort()
633 self._abort()
634
634
635 def _writeundo(self):
635 def _writeundo(self):
636 """write transaction data for possible future undo call"""
636 """write transaction data for possible future undo call"""
637 if self._undoname is None:
637 if self._undoname is None:
638 return
638 return
639
639
640 undo_backup_path = b"%s.backupfiles" % self._undoname
640 undo_backup_path = b"%s.backupfiles" % self._undoname
641 undobackupfile = self._opener.open(undo_backup_path, b'w')
641 undobackupfile = self._opener.open(undo_backup_path, b'w')
642 undobackupfile.write(b'%d\n' % version)
642 undobackupfile.write(b'%d\n' % version)
643 for l, f, b, c in self._backupentries:
643 for l, f, b, c in self._backupentries:
644 if not f: # temporary file
644 if not f: # temporary file
645 continue
645 continue
646 if not b:
646 if not b:
647 u = b''
647 u = b''
648 else:
648 else:
649 if l not in self._vfsmap and c:
649 if l not in self._vfsmap and c:
650 self._report(
650 self._report(
651 b"couldn't remove %s: unknown cache location"
651 b"couldn't remove %s: unknown cache location"
652 b"%s\n" % (b, l)
652 b"%s\n" % (b, l)
653 )
653 )
654 continue
654 continue
655 vfs = self._vfsmap[l]
655 vfs = self._vfsmap[l]
656 base, name = vfs.split(b)
656 base, name = vfs.split(b)
657 assert name.startswith(self._journal), name
657 assert name.startswith(self._journal), name
658 uname = name.replace(self._journal, self._undoname, 1)
658 uname = name.replace(self._journal, self._undoname, 1)
659 u = vfs.reljoin(base, uname)
659 u = vfs.reljoin(base, uname)
660 util.copyfile(vfs.join(b), vfs.join(u), hardlink=True)
660 util.copyfile(vfs.join(b), vfs.join(u), hardlink=True)
661 undobackupfile.write(b"%s\0%s\0%s\0%d\n" % (l, f, u, c))
661 undobackupfile.write(b"%s\0%s\0%s\0%d\n" % (l, f, u, c))
662 undobackupfile.close()
662 undobackupfile.close()
663
663
664 def _abort(self):
664 def _abort(self):
665 entries = self.readjournal()
665 entries = self.readjournal()
666 self._count = 0
666 self._count = 0
667 self._usages = 0
667 self._usages = 0
668 self._file.close()
668 self._file.close()
669 self._backupsfile.close()
669 self._backupsfile.close()
670
670
671 quick = self._can_quick_abort(entries)
671 quick = self._can_quick_abort(entries)
672 try:
672 try:
673 if not quick:
674 self._report(_(b"transaction abort!\n"))
675 for cat in sorted(self._abortcallback):
676 self._abortcallback[cat](self)
677 # Prevent double usage and help clear cycles.
678 self._abortcallback = None
673 if quick:
679 if quick:
674 self._do_quick_abort(entries)
680 self._do_quick_abort(entries)
675 else:
681 else:
676 self._do_full_abort(entries)
682 self._do_full_abort(entries)
677 finally:
683 finally:
678 self._journal = None
684 self._journal = None
679 self._releasefn(self, False) # notify failure of transaction
685 self._releasefn(self, False) # notify failure of transaction
680 self._releasefn = None # Help prevent cycles.
686 self._releasefn = None # Help prevent cycles.
681
687
682 def _can_quick_abort(self, entries):
688 def _can_quick_abort(self, entries):
683 """False if any semantic content have been written on disk
689 """False if any semantic content have been written on disk
684
690
685 True if nothing, except temporary files has been writen on disk."""
691 True if nothing, except temporary files has been writen on disk."""
686 if entries:
692 if entries:
687 return False
693 return False
688 if self._backupentries:
694 if self._backupentries:
689 return False
695 return False
690 return True
696 return True
691
697
692 def _do_quick_abort(self, entries):
698 def _do_quick_abort(self, entries):
693 """(Silently) do a quick cleanup (see _can_quick_abort)"""
699 """(Silently) do a quick cleanup (see _can_quick_abort)"""
694 assert self._can_quick_abort(entries)
700 assert self._can_quick_abort(entries)
695 if self._backupjournal:
701 if self._backupjournal:
696 self._opener.unlink(self._backupjournal)
702 self._opener.unlink(self._backupjournal)
697 if self._journal:
703 if self._journal:
698 self._opener.unlink(self._journal)
704 self._opener.unlink(self._journal)
699
705
700 def _do_full_abort(self, entries):
706 def _do_full_abort(self, entries):
701 """(Noisily) rollback all the change introduced by the transaction"""
707 """(Noisily) rollback all the change introduced by the transaction"""
702 self._report(_(b"transaction abort!\n"))
703 try:
708 try:
704 for cat in sorted(self._abortcallback):
705 self._abortcallback[cat](self)
706 # Prevent double usage and help clear cycles.
707 self._abortcallback = None
708 _playback(
709 _playback(
709 self._journal,
710 self._journal,
710 self._report,
711 self._report,
711 self._opener,
712 self._opener,
712 self._vfsmap,
713 self._vfsmap,
713 entries,
714 entries,
714 self._backupentries,
715 self._backupentries,
715 False,
716 False,
716 checkambigfiles=self._checkambigfiles,
717 checkambigfiles=self._checkambigfiles,
717 )
718 )
718 self._report(_(b"rollback completed\n"))
719 self._report(_(b"rollback completed\n"))
719 except BaseException as exc:
720 except BaseException as exc:
720 self._report(_(b"rollback failed - please run hg recover\n"))
721 self._report(_(b"rollback failed - please run hg recover\n"))
721 self._report(
722 self._report(
722 _(b"(failure reason: %s)\n") % stringutil.forcebytestr(exc)
723 _(b"(failure reason: %s)\n") % stringutil.forcebytestr(exc)
723 )
724 )
724
725
725
726
726 BAD_VERSION_MSG = _(
727 BAD_VERSION_MSG = _(
727 b"journal was created by a different version of Mercurial\n"
728 b"journal was created by a different version of Mercurial\n"
728 )
729 )
729
730
730
731
731 def rollback(opener, vfsmap, file, report, checkambigfiles=None):
732 def rollback(opener, vfsmap, file, report, checkambigfiles=None):
732 """Rolls back the transaction contained in the given file
733 """Rolls back the transaction contained in the given file
733
734
734 Reads the entries in the specified file, and the corresponding
735 Reads the entries in the specified file, and the corresponding
735 '*.backupfiles' file, to recover from an incomplete transaction.
736 '*.backupfiles' file, to recover from an incomplete transaction.
736
737
737 * `file`: a file containing a list of entries, specifying where
738 * `file`: a file containing a list of entries, specifying where
738 to truncate each file. The file should contain a list of
739 to truncate each file. The file should contain a list of
739 file\0offset pairs, delimited by newlines. The corresponding
740 file\0offset pairs, delimited by newlines. The corresponding
740 '*.backupfiles' file should contain a list of file\0backupfile
741 '*.backupfiles' file should contain a list of file\0backupfile
741 pairs, delimited by \0.
742 pairs, delimited by \0.
742
743
743 `checkambigfiles` is a set of (path, vfs-location) tuples,
744 `checkambigfiles` is a set of (path, vfs-location) tuples,
744 which determine whether file stat ambiguity should be avoided at
745 which determine whether file stat ambiguity should be avoided at
745 restoring corresponded files.
746 restoring corresponded files.
746 """
747 """
747 entries = []
748 entries = []
748 backupentries = []
749 backupentries = []
749
750
750 with opener.open(file) as fp:
751 with opener.open(file) as fp:
751 lines = fp.readlines()
752 lines = fp.readlines()
752 for l in lines:
753 for l in lines:
753 try:
754 try:
754 f, o = l.split(b'\0')
755 f, o = l.split(b'\0')
755 entries.append((f, int(o)))
756 entries.append((f, int(o)))
756 except ValueError:
757 except ValueError:
757 report(
758 report(
758 _(b"couldn't read journal entry %r!\n") % pycompat.bytestr(l)
759 _(b"couldn't read journal entry %r!\n") % pycompat.bytestr(l)
759 )
760 )
760
761
761 backupjournal = b"%s.backupfiles" % file
762 backupjournal = b"%s.backupfiles" % file
762 if opener.exists(backupjournal):
763 if opener.exists(backupjournal):
763 fp = opener.open(backupjournal)
764 fp = opener.open(backupjournal)
764 lines = fp.readlines()
765 lines = fp.readlines()
765 if lines:
766 if lines:
766 ver = lines[0][:-1]
767 ver = lines[0][:-1]
767 if ver != (b'%d' % version):
768 if ver != (b'%d' % version):
768 report(BAD_VERSION_MSG)
769 report(BAD_VERSION_MSG)
769 else:
770 else:
770 for line in lines[1:]:
771 for line in lines[1:]:
771 if line:
772 if line:
772 # Shave off the trailing newline
773 # Shave off the trailing newline
773 line = line[:-1]
774 line = line[:-1]
774 l, f, b, c = line.split(b'\0')
775 l, f, b, c = line.split(b'\0')
775 backupentries.append((l, f, b, bool(c)))
776 backupentries.append((l, f, b, bool(c)))
776
777
777 _playback(
778 _playback(
778 file,
779 file,
779 report,
780 report,
780 opener,
781 opener,
781 vfsmap,
782 vfsmap,
782 entries,
783 entries,
783 backupentries,
784 backupentries,
784 checkambigfiles=checkambigfiles,
785 checkambigfiles=checkambigfiles,
785 )
786 )
@@ -1,1425 +1,1434 b''
1 commit hooks can see env vars
1 commit hooks can see env vars
2 (and post-transaction one are run unlocked)
2 (and post-transaction one are run unlocked)
3
3
4
4
5 $ cat > $TESTTMP/txnabort.checkargs.py <<EOF
5 $ cat > $TESTTMP/txnabort.checkargs.py <<EOF
6 > from mercurial import pycompat
6 > from mercurial import pycompat
7 > def showargs(ui, repo, hooktype, **kwargs):
7 > def showargs(ui, repo, hooktype, **kwargs):
8 > kwargs = pycompat.byteskwargs(kwargs)
8 > kwargs = pycompat.byteskwargs(kwargs)
9 > ui.write(b'%s Python hook: %s\n' % (hooktype,
9 > ui.write(b'%s Python hook: %s\n' % (hooktype,
10 > b','.join(sorted(kwargs))))
10 > b','.join(sorted(kwargs))))
11 > EOF
11 > EOF
12
12
13 $ hg init a
13 $ hg init a
14 $ cd a
14 $ cd a
15 $ cat > .hg/hgrc <<EOF
15 $ cat > .hg/hgrc <<EOF
16 > [hooks]
16 > [hooks]
17 > commit = sh -c "HG_LOCAL= HG_TAG= printenv.py --line commit"
17 > commit = sh -c "HG_LOCAL= HG_TAG= printenv.py --line commit"
18 > commit.b = sh -c "HG_LOCAL= HG_TAG= printenv.py --line commit.b"
18 > commit.b = sh -c "HG_LOCAL= HG_TAG= printenv.py --line commit.b"
19 > precommit = sh -c "HG_LOCAL= HG_NODE= HG_TAG= printenv.py --line precommit"
19 > precommit = sh -c "HG_LOCAL= HG_NODE= HG_TAG= printenv.py --line precommit"
20 > pretxncommit = sh -c "HG_LOCAL= HG_TAG= printenv.py --line pretxncommit"
20 > pretxncommit = sh -c "HG_LOCAL= HG_TAG= printenv.py --line pretxncommit"
21 > pretxncommit.tip = hg -q tip
21 > pretxncommit.tip = hg -q tip
22 > pre-identify = sh -c "printenv.py --line pre-identify 1"
22 > pre-identify = sh -c "printenv.py --line pre-identify 1"
23 > pre-cat = sh -c "printenv.py --line pre-cat"
23 > pre-cat = sh -c "printenv.py --line pre-cat"
24 > post-cat = sh -c "printenv.py --line post-cat"
24 > post-cat = sh -c "printenv.py --line post-cat"
25 > pretxnopen = sh -c "HG_LOCAL= HG_TAG= printenv.py --line pretxnopen"
25 > pretxnopen = sh -c "HG_LOCAL= HG_TAG= printenv.py --line pretxnopen"
26 > pretxnclose = sh -c "HG_LOCAL= HG_TAG= printenv.py --line pretxnclose"
26 > pretxnclose = sh -c "HG_LOCAL= HG_TAG= printenv.py --line pretxnclose"
27 > txnclose = sh -c "HG_LOCAL= HG_TAG= printenv.py --line txnclose"
27 > txnclose = sh -c "HG_LOCAL= HG_TAG= printenv.py --line txnclose"
28 > txnabort.0 = python:$TESTTMP/txnabort.checkargs.py:showargs
28 > txnabort.0 = python:$TESTTMP/txnabort.checkargs.py:showargs
29 > txnabort.1 = sh -c "HG_LOCAL= HG_TAG= printenv.py --line txnabort"
29 > txnabort.1 = sh -c "HG_LOCAL= HG_TAG= printenv.py --line txnabort"
30 > txnclose.checklock = sh -c "hg debuglock > /dev/null"
30 > txnclose.checklock = sh -c "hg debuglock > /dev/null"
31 > EOF
31 > EOF
32 $ echo a > a
32 $ echo a > a
33 $ hg add a
33 $ hg add a
34 $ hg commit -m a
34 $ hg commit -m a
35 precommit hook: HG_HOOKNAME=precommit
35 precommit hook: HG_HOOKNAME=precommit
36 HG_HOOKTYPE=precommit
36 HG_HOOKTYPE=precommit
37 HG_PARENT1=0000000000000000000000000000000000000000
37 HG_PARENT1=0000000000000000000000000000000000000000
38
38
39 pretxnopen hook: HG_HOOKNAME=pretxnopen
39 pretxnopen hook: HG_HOOKNAME=pretxnopen
40 HG_HOOKTYPE=pretxnopen
40 HG_HOOKTYPE=pretxnopen
41 HG_TXNID=TXN:$ID$
41 HG_TXNID=TXN:$ID$
42 HG_TXNNAME=commit
42 HG_TXNNAME=commit
43
43
44 pretxncommit hook: HG_HOOKNAME=pretxncommit
44 pretxncommit hook: HG_HOOKNAME=pretxncommit
45 HG_HOOKTYPE=pretxncommit
45 HG_HOOKTYPE=pretxncommit
46 HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
46 HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
47 HG_PARENT1=0000000000000000000000000000000000000000
47 HG_PARENT1=0000000000000000000000000000000000000000
48 HG_PENDING=$TESTTMP/a
48 HG_PENDING=$TESTTMP/a
49
49
50 0:cb9a9f314b8b
50 0:cb9a9f314b8b
51 pretxnclose hook: HG_HOOKNAME=pretxnclose
51 pretxnclose hook: HG_HOOKNAME=pretxnclose
52 HG_HOOKTYPE=pretxnclose
52 HG_HOOKTYPE=pretxnclose
53 HG_PENDING=$TESTTMP/a
53 HG_PENDING=$TESTTMP/a
54 HG_PHASES_MOVED=1
54 HG_PHASES_MOVED=1
55 HG_TXNID=TXN:$ID$
55 HG_TXNID=TXN:$ID$
56 HG_TXNNAME=commit
56 HG_TXNNAME=commit
57
57
58 txnclose hook: HG_HOOKNAME=txnclose
58 txnclose hook: HG_HOOKNAME=txnclose
59 HG_HOOKTYPE=txnclose
59 HG_HOOKTYPE=txnclose
60 HG_PHASES_MOVED=1
60 HG_PHASES_MOVED=1
61 HG_TXNID=TXN:$ID$
61 HG_TXNID=TXN:$ID$
62 HG_TXNNAME=commit
62 HG_TXNNAME=commit
63
63
64 commit hook: HG_HOOKNAME=commit
64 commit hook: HG_HOOKNAME=commit
65 HG_HOOKTYPE=commit
65 HG_HOOKTYPE=commit
66 HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
66 HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
67 HG_PARENT1=0000000000000000000000000000000000000000
67 HG_PARENT1=0000000000000000000000000000000000000000
68
68
69 commit.b hook: HG_HOOKNAME=commit.b
69 commit.b hook: HG_HOOKNAME=commit.b
70 HG_HOOKTYPE=commit
70 HG_HOOKTYPE=commit
71 HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
71 HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
72 HG_PARENT1=0000000000000000000000000000000000000000
72 HG_PARENT1=0000000000000000000000000000000000000000
73
73
74
74
75 $ hg clone . ../b
75 $ hg clone . ../b
76 updating to branch default
76 updating to branch default
77 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
77 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
78 $ cd ../b
78 $ cd ../b
79
79
80 changegroup hooks can see env vars
80 changegroup hooks can see env vars
81
81
82 $ cat > .hg/hgrc <<EOF
82 $ cat > .hg/hgrc <<EOF
83 > [hooks]
83 > [hooks]
84 > prechangegroup = sh -c "printenv.py --line prechangegroup"
84 > prechangegroup = sh -c "printenv.py --line prechangegroup"
85 > changegroup = sh -c "printenv.py --line changegroup"
85 > changegroup = sh -c "printenv.py --line changegroup"
86 > incoming = sh -c "printenv.py --line incoming"
86 > incoming = sh -c "printenv.py --line incoming"
87 > EOF
87 > EOF
88
88
89 pretxncommit and commit hooks can see both parents of merge
89 pretxncommit and commit hooks can see both parents of merge
90
90
91 $ cd ../a
91 $ cd ../a
92 $ echo b >> a
92 $ echo b >> a
93 $ hg commit -m a1 -d "1 0"
93 $ hg commit -m a1 -d "1 0"
94 precommit hook: HG_HOOKNAME=precommit
94 precommit hook: HG_HOOKNAME=precommit
95 HG_HOOKTYPE=precommit
95 HG_HOOKTYPE=precommit
96 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
96 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
97
97
98 pretxnopen hook: HG_HOOKNAME=pretxnopen
98 pretxnopen hook: HG_HOOKNAME=pretxnopen
99 HG_HOOKTYPE=pretxnopen
99 HG_HOOKTYPE=pretxnopen
100 HG_TXNID=TXN:$ID$
100 HG_TXNID=TXN:$ID$
101 HG_TXNNAME=commit
101 HG_TXNNAME=commit
102
102
103 pretxncommit hook: HG_HOOKNAME=pretxncommit
103 pretxncommit hook: HG_HOOKNAME=pretxncommit
104 HG_HOOKTYPE=pretxncommit
104 HG_HOOKTYPE=pretxncommit
105 HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd
105 HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd
106 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
106 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
107 HG_PENDING=$TESTTMP/a
107 HG_PENDING=$TESTTMP/a
108
108
109 1:ab228980c14d
109 1:ab228980c14d
110 pretxnclose hook: HG_HOOKNAME=pretxnclose
110 pretxnclose hook: HG_HOOKNAME=pretxnclose
111 HG_HOOKTYPE=pretxnclose
111 HG_HOOKTYPE=pretxnclose
112 HG_PENDING=$TESTTMP/a
112 HG_PENDING=$TESTTMP/a
113 HG_TXNID=TXN:$ID$
113 HG_TXNID=TXN:$ID$
114 HG_TXNNAME=commit
114 HG_TXNNAME=commit
115
115
116 txnclose hook: HG_HOOKNAME=txnclose
116 txnclose hook: HG_HOOKNAME=txnclose
117 HG_HOOKTYPE=txnclose
117 HG_HOOKTYPE=txnclose
118 HG_TXNID=TXN:$ID$
118 HG_TXNID=TXN:$ID$
119 HG_TXNNAME=commit
119 HG_TXNNAME=commit
120
120
121 commit hook: HG_HOOKNAME=commit
121 commit hook: HG_HOOKNAME=commit
122 HG_HOOKTYPE=commit
122 HG_HOOKTYPE=commit
123 HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd
123 HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd
124 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
124 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
125
125
126 commit.b hook: HG_HOOKNAME=commit.b
126 commit.b hook: HG_HOOKNAME=commit.b
127 HG_HOOKTYPE=commit
127 HG_HOOKTYPE=commit
128 HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd
128 HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd
129 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
129 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
130
130
131 $ hg update -C 0
131 $ hg update -C 0
132 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
132 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
133 $ echo b > b
133 $ echo b > b
134 $ hg add b
134 $ hg add b
135 $ hg commit -m b -d '1 0'
135 $ hg commit -m b -d '1 0'
136 precommit hook: HG_HOOKNAME=precommit
136 precommit hook: HG_HOOKNAME=precommit
137 HG_HOOKTYPE=precommit
137 HG_HOOKTYPE=precommit
138 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
138 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
139
139
140 pretxnopen hook: HG_HOOKNAME=pretxnopen
140 pretxnopen hook: HG_HOOKNAME=pretxnopen
141 HG_HOOKTYPE=pretxnopen
141 HG_HOOKTYPE=pretxnopen
142 HG_TXNID=TXN:$ID$
142 HG_TXNID=TXN:$ID$
143 HG_TXNNAME=commit
143 HG_TXNNAME=commit
144
144
145 pretxncommit hook: HG_HOOKNAME=pretxncommit
145 pretxncommit hook: HG_HOOKNAME=pretxncommit
146 HG_HOOKTYPE=pretxncommit
146 HG_HOOKTYPE=pretxncommit
147 HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2
147 HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2
148 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
148 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
149 HG_PENDING=$TESTTMP/a
149 HG_PENDING=$TESTTMP/a
150
150
151 2:ee9deb46ab31
151 2:ee9deb46ab31
152 pretxnclose hook: HG_HOOKNAME=pretxnclose
152 pretxnclose hook: HG_HOOKNAME=pretxnclose
153 HG_HOOKTYPE=pretxnclose
153 HG_HOOKTYPE=pretxnclose
154 HG_PENDING=$TESTTMP/a
154 HG_PENDING=$TESTTMP/a
155 HG_TXNID=TXN:$ID$
155 HG_TXNID=TXN:$ID$
156 HG_TXNNAME=commit
156 HG_TXNNAME=commit
157
157
158 created new head
158 created new head
159 txnclose hook: HG_HOOKNAME=txnclose
159 txnclose hook: HG_HOOKNAME=txnclose
160 HG_HOOKTYPE=txnclose
160 HG_HOOKTYPE=txnclose
161 HG_TXNID=TXN:$ID$
161 HG_TXNID=TXN:$ID$
162 HG_TXNNAME=commit
162 HG_TXNNAME=commit
163
163
164 commit hook: HG_HOOKNAME=commit
164 commit hook: HG_HOOKNAME=commit
165 HG_HOOKTYPE=commit
165 HG_HOOKTYPE=commit
166 HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2
166 HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2
167 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
167 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
168
168
169 commit.b hook: HG_HOOKNAME=commit.b
169 commit.b hook: HG_HOOKNAME=commit.b
170 HG_HOOKTYPE=commit
170 HG_HOOKTYPE=commit
171 HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2
171 HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2
172 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
172 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
173
173
174 $ hg merge 1
174 $ hg merge 1
175 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
175 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
176 (branch merge, don't forget to commit)
176 (branch merge, don't forget to commit)
177 $ hg commit -m merge -d '2 0'
177 $ hg commit -m merge -d '2 0'
178 precommit hook: HG_HOOKNAME=precommit
178 precommit hook: HG_HOOKNAME=precommit
179 HG_HOOKTYPE=precommit
179 HG_HOOKTYPE=precommit
180 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2
180 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2
181 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
181 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
182
182
183 pretxnopen hook: HG_HOOKNAME=pretxnopen
183 pretxnopen hook: HG_HOOKNAME=pretxnopen
184 HG_HOOKTYPE=pretxnopen
184 HG_HOOKTYPE=pretxnopen
185 HG_TXNID=TXN:$ID$
185 HG_TXNID=TXN:$ID$
186 HG_TXNNAME=commit
186 HG_TXNNAME=commit
187
187
188 pretxncommit hook: HG_HOOKNAME=pretxncommit
188 pretxncommit hook: HG_HOOKNAME=pretxncommit
189 HG_HOOKTYPE=pretxncommit
189 HG_HOOKTYPE=pretxncommit
190 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2
190 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2
191 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2
191 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2
192 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
192 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
193 HG_PENDING=$TESTTMP/a
193 HG_PENDING=$TESTTMP/a
194
194
195 3:07f3376c1e65
195 3:07f3376c1e65
196 pretxnclose hook: HG_HOOKNAME=pretxnclose
196 pretxnclose hook: HG_HOOKNAME=pretxnclose
197 HG_HOOKTYPE=pretxnclose
197 HG_HOOKTYPE=pretxnclose
198 HG_PENDING=$TESTTMP/a
198 HG_PENDING=$TESTTMP/a
199 HG_TXNID=TXN:$ID$
199 HG_TXNID=TXN:$ID$
200 HG_TXNNAME=commit
200 HG_TXNNAME=commit
201
201
202 txnclose hook: HG_HOOKNAME=txnclose
202 txnclose hook: HG_HOOKNAME=txnclose
203 HG_HOOKTYPE=txnclose
203 HG_HOOKTYPE=txnclose
204 HG_TXNID=TXN:$ID$
204 HG_TXNID=TXN:$ID$
205 HG_TXNNAME=commit
205 HG_TXNNAME=commit
206
206
207 commit hook: HG_HOOKNAME=commit
207 commit hook: HG_HOOKNAME=commit
208 HG_HOOKTYPE=commit
208 HG_HOOKTYPE=commit
209 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2
209 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2
210 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2
210 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2
211 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
211 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
212
212
213 commit.b hook: HG_HOOKNAME=commit.b
213 commit.b hook: HG_HOOKNAME=commit.b
214 HG_HOOKTYPE=commit
214 HG_HOOKTYPE=commit
215 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2
215 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2
216 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2
216 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2
217 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
217 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
218
218
219
219
220 test generic hooks
220 test generic hooks
221
221
222 $ hg id
222 $ hg id
223 pre-identify hook: HG_ARGS=id
223 pre-identify hook: HG_ARGS=id
224 HG_HOOKNAME=pre-identify
224 HG_HOOKNAME=pre-identify
225 HG_HOOKTYPE=pre-identify
225 HG_HOOKTYPE=pre-identify
226 HG_OPTS={'bookmarks': None, 'branch': None, 'id': None, 'insecure': None, 'num': None, 'remotecmd': '', 'rev': '', 'ssh': '', 'tags': None, 'template': ''}
226 HG_OPTS={'bookmarks': None, 'branch': None, 'id': None, 'insecure': None, 'num': None, 'remotecmd': '', 'rev': '', 'ssh': '', 'tags': None, 'template': ''}
227 HG_PATS=[]
227 HG_PATS=[]
228
228
229 abort: pre-identify hook exited with status 1
229 abort: pre-identify hook exited with status 1
230 [40]
230 [40]
231 $ hg cat b
231 $ hg cat b
232 pre-cat hook: HG_ARGS=cat b
232 pre-cat hook: HG_ARGS=cat b
233 HG_HOOKNAME=pre-cat
233 HG_HOOKNAME=pre-cat
234 HG_HOOKTYPE=pre-cat
234 HG_HOOKTYPE=pre-cat
235 HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': '', 'template': ''}
235 HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': '', 'template': ''}
236 HG_PATS=['b']
236 HG_PATS=['b']
237
237
238 b
238 b
239 post-cat hook: HG_ARGS=cat b
239 post-cat hook: HG_ARGS=cat b
240 HG_HOOKNAME=post-cat
240 HG_HOOKNAME=post-cat
241 HG_HOOKTYPE=post-cat
241 HG_HOOKTYPE=post-cat
242 HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': '', 'template': ''}
242 HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': '', 'template': ''}
243 HG_PATS=['b']
243 HG_PATS=['b']
244 HG_RESULT=0
244 HG_RESULT=0
245
245
246
246
247 $ cd ../b
247 $ cd ../b
248 $ hg pull ../a
248 $ hg pull ../a
249 pulling from ../a
249 pulling from ../a
250 searching for changes
250 searching for changes
251 prechangegroup hook: HG_HOOKNAME=prechangegroup
251 prechangegroup hook: HG_HOOKNAME=prechangegroup
252 HG_HOOKTYPE=prechangegroup
252 HG_HOOKTYPE=prechangegroup
253 HG_SOURCE=pull
253 HG_SOURCE=pull
254 HG_TXNID=TXN:$ID$
254 HG_TXNID=TXN:$ID$
255 HG_TXNNAME=pull
255 HG_TXNNAME=pull
256 file:/*/$TESTTMP/a (glob)
256 file:/*/$TESTTMP/a (glob)
257 HG_URL=file:$TESTTMP/a
257 HG_URL=file:$TESTTMP/a
258
258
259 adding changesets
259 adding changesets
260 adding manifests
260 adding manifests
261 adding file changes
261 adding file changes
262 added 3 changesets with 2 changes to 2 files
262 added 3 changesets with 2 changes to 2 files
263 new changesets ab228980c14d:07f3376c1e65
263 new changesets ab228980c14d:07f3376c1e65
264 changegroup hook: HG_HOOKNAME=changegroup
264 changegroup hook: HG_HOOKNAME=changegroup
265 HG_HOOKTYPE=changegroup
265 HG_HOOKTYPE=changegroup
266 HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd
266 HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd
267 HG_NODE_LAST=07f3376c1e655977439df2a814e3cc14b27abac2
267 HG_NODE_LAST=07f3376c1e655977439df2a814e3cc14b27abac2
268 HG_SOURCE=pull
268 HG_SOURCE=pull
269 HG_TXNID=TXN:$ID$
269 HG_TXNID=TXN:$ID$
270 HG_TXNNAME=pull
270 HG_TXNNAME=pull
271 file:/*/$TESTTMP/a (glob)
271 file:/*/$TESTTMP/a (glob)
272 HG_URL=file:$TESTTMP/a
272 HG_URL=file:$TESTTMP/a
273
273
274 incoming hook: HG_HOOKNAME=incoming
274 incoming hook: HG_HOOKNAME=incoming
275 HG_HOOKTYPE=incoming
275 HG_HOOKTYPE=incoming
276 HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd
276 HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd
277 HG_SOURCE=pull
277 HG_SOURCE=pull
278 HG_TXNID=TXN:$ID$
278 HG_TXNID=TXN:$ID$
279 HG_TXNNAME=pull
279 HG_TXNNAME=pull
280 file:/*/$TESTTMP/a (glob)
280 file:/*/$TESTTMP/a (glob)
281 HG_URL=file:$TESTTMP/a
281 HG_URL=file:$TESTTMP/a
282
282
283 incoming hook: HG_HOOKNAME=incoming
283 incoming hook: HG_HOOKNAME=incoming
284 HG_HOOKTYPE=incoming
284 HG_HOOKTYPE=incoming
285 HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2
285 HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2
286 HG_SOURCE=pull
286 HG_SOURCE=pull
287 HG_TXNID=TXN:$ID$
287 HG_TXNID=TXN:$ID$
288 HG_TXNNAME=pull
288 HG_TXNNAME=pull
289 file:/*/$TESTTMP/a (glob)
289 file:/*/$TESTTMP/a (glob)
290 HG_URL=file:$TESTTMP/a
290 HG_URL=file:$TESTTMP/a
291
291
292 incoming hook: HG_HOOKNAME=incoming
292 incoming hook: HG_HOOKNAME=incoming
293 HG_HOOKTYPE=incoming
293 HG_HOOKTYPE=incoming
294 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2
294 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2
295 HG_SOURCE=pull
295 HG_SOURCE=pull
296 HG_TXNID=TXN:$ID$
296 HG_TXNID=TXN:$ID$
297 HG_TXNNAME=pull
297 HG_TXNNAME=pull
298 file:/*/$TESTTMP/a (glob)
298 file:/*/$TESTTMP/a (glob)
299 HG_URL=file:$TESTTMP/a
299 HG_URL=file:$TESTTMP/a
300
300
301 (run 'hg update' to get a working copy)
301 (run 'hg update' to get a working copy)
302
302
303 tag hooks can see env vars
303 tag hooks can see env vars
304
304
305 $ cd ../a
305 $ cd ../a
306 $ cat >> .hg/hgrc <<EOF
306 $ cat >> .hg/hgrc <<EOF
307 > pretag = sh -c "printenv.py --line pretag"
307 > pretag = sh -c "printenv.py --line pretag"
308 > tag = sh -c "HG_PARENT1= HG_PARENT2= printenv.py --line tag"
308 > tag = sh -c "HG_PARENT1= HG_PARENT2= printenv.py --line tag"
309 > EOF
309 > EOF
310 $ hg tag -d '3 0' a
310 $ hg tag -d '3 0' a
311 pretag hook: HG_HOOKNAME=pretag
311 pretag hook: HG_HOOKNAME=pretag
312 HG_HOOKTYPE=pretag
312 HG_HOOKTYPE=pretag
313 HG_LOCAL=0
313 HG_LOCAL=0
314 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2
314 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2
315 HG_TAG=a
315 HG_TAG=a
316
316
317 precommit hook: HG_HOOKNAME=precommit
317 precommit hook: HG_HOOKNAME=precommit
318 HG_HOOKTYPE=precommit
318 HG_HOOKTYPE=precommit
319 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
319 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
320
320
321 pretxnopen hook: HG_HOOKNAME=pretxnopen
321 pretxnopen hook: HG_HOOKNAME=pretxnopen
322 HG_HOOKTYPE=pretxnopen
322 HG_HOOKTYPE=pretxnopen
323 HG_TXNID=TXN:$ID$
323 HG_TXNID=TXN:$ID$
324 HG_TXNNAME=commit
324 HG_TXNNAME=commit
325
325
326 pretxncommit hook: HG_HOOKNAME=pretxncommit
326 pretxncommit hook: HG_HOOKNAME=pretxncommit
327 HG_HOOKTYPE=pretxncommit
327 HG_HOOKTYPE=pretxncommit
328 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
328 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
329 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
329 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
330 HG_PENDING=$TESTTMP/a
330 HG_PENDING=$TESTTMP/a
331
331
332 4:539e4b31b6dc
332 4:539e4b31b6dc
333 pretxnclose hook: HG_HOOKNAME=pretxnclose
333 pretxnclose hook: HG_HOOKNAME=pretxnclose
334 HG_HOOKTYPE=pretxnclose
334 HG_HOOKTYPE=pretxnclose
335 HG_PENDING=$TESTTMP/a
335 HG_PENDING=$TESTTMP/a
336 HG_TXNID=TXN:$ID$
336 HG_TXNID=TXN:$ID$
337 HG_TXNNAME=commit
337 HG_TXNNAME=commit
338
338
339 tag hook: HG_HOOKNAME=tag
339 tag hook: HG_HOOKNAME=tag
340 HG_HOOKTYPE=tag
340 HG_HOOKTYPE=tag
341 HG_LOCAL=0
341 HG_LOCAL=0
342 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2
342 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2
343 HG_TAG=a
343 HG_TAG=a
344
344
345 txnclose hook: HG_HOOKNAME=txnclose
345 txnclose hook: HG_HOOKNAME=txnclose
346 HG_HOOKTYPE=txnclose
346 HG_HOOKTYPE=txnclose
347 HG_TXNID=TXN:$ID$
347 HG_TXNID=TXN:$ID$
348 HG_TXNNAME=commit
348 HG_TXNNAME=commit
349
349
350 commit hook: HG_HOOKNAME=commit
350 commit hook: HG_HOOKNAME=commit
351 HG_HOOKTYPE=commit
351 HG_HOOKTYPE=commit
352 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
352 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
353 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
353 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
354
354
355 commit.b hook: HG_HOOKNAME=commit.b
355 commit.b hook: HG_HOOKNAME=commit.b
356 HG_HOOKTYPE=commit
356 HG_HOOKTYPE=commit
357 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
357 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
358 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
358 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
359
359
360 $ hg tag -l la
360 $ hg tag -l la
361 pretag hook: HG_HOOKNAME=pretag
361 pretag hook: HG_HOOKNAME=pretag
362 HG_HOOKTYPE=pretag
362 HG_HOOKTYPE=pretag
363 HG_LOCAL=1
363 HG_LOCAL=1
364 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
364 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
365 HG_TAG=la
365 HG_TAG=la
366
366
367 tag hook: HG_HOOKNAME=tag
367 tag hook: HG_HOOKNAME=tag
368 HG_HOOKTYPE=tag
368 HG_HOOKTYPE=tag
369 HG_LOCAL=1
369 HG_LOCAL=1
370 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
370 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
371 HG_TAG=la
371 HG_TAG=la
372
372
373
373
374 pretag hook can forbid tagging
374 pretag hook can forbid tagging
375
375
376 $ cat >> .hg/hgrc <<EOF
376 $ cat >> .hg/hgrc <<EOF
377 > pretag.forbid = sh -c "printenv.py --line pretag.forbid 1"
377 > pretag.forbid = sh -c "printenv.py --line pretag.forbid 1"
378 > EOF
378 > EOF
379 $ hg tag -d '4 0' fa
379 $ hg tag -d '4 0' fa
380 pretag hook: HG_HOOKNAME=pretag
380 pretag hook: HG_HOOKNAME=pretag
381 HG_HOOKTYPE=pretag
381 HG_HOOKTYPE=pretag
382 HG_LOCAL=0
382 HG_LOCAL=0
383 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
383 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
384 HG_TAG=fa
384 HG_TAG=fa
385
385
386 pretag.forbid hook: HG_HOOKNAME=pretag.forbid
386 pretag.forbid hook: HG_HOOKNAME=pretag.forbid
387 HG_HOOKTYPE=pretag
387 HG_HOOKTYPE=pretag
388 HG_LOCAL=0
388 HG_LOCAL=0
389 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
389 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
390 HG_TAG=fa
390 HG_TAG=fa
391
391
392 abort: pretag.forbid hook exited with status 1
392 abort: pretag.forbid hook exited with status 1
393 [40]
393 [40]
394 $ hg tag -l fla
394 $ hg tag -l fla
395 pretag hook: HG_HOOKNAME=pretag
395 pretag hook: HG_HOOKNAME=pretag
396 HG_HOOKTYPE=pretag
396 HG_HOOKTYPE=pretag
397 HG_LOCAL=1
397 HG_LOCAL=1
398 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
398 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
399 HG_TAG=fla
399 HG_TAG=fla
400
400
401 pretag.forbid hook: HG_HOOKNAME=pretag.forbid
401 pretag.forbid hook: HG_HOOKNAME=pretag.forbid
402 HG_HOOKTYPE=pretag
402 HG_HOOKTYPE=pretag
403 HG_LOCAL=1
403 HG_LOCAL=1
404 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
404 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
405 HG_TAG=fla
405 HG_TAG=fla
406
406
407 abort: pretag.forbid hook exited with status 1
407 abort: pretag.forbid hook exited with status 1
408 [40]
408 [40]
409
409
410 pretxncommit hook can see changeset, can roll back txn, changeset no
410 pretxncommit hook can see changeset, can roll back txn, changeset no
411 more there after
411 more there after
412
412
413 $ cat >> .hg/hgrc <<EOF
413 $ cat >> .hg/hgrc <<EOF
414 > pretxncommit.forbid0 = sh -c "hg tip -q"
414 > pretxncommit.forbid0 = sh -c "hg tip -q"
415 > pretxncommit.forbid1 = sh -c "printenv.py --line pretxncommit.forbid 1"
415 > pretxncommit.forbid1 = sh -c "printenv.py --line pretxncommit.forbid 1"
416 > EOF
416 > EOF
417 $ echo z > z
417 $ echo z > z
418 $ hg add z
418 $ hg add z
419 $ hg -q tip
419 $ hg -q tip
420 4:539e4b31b6dc
420 4:539e4b31b6dc
421 $ hg commit -m 'fail' -d '4 0'
421 $ hg commit -m 'fail' -d '4 0'
422 precommit hook: HG_HOOKNAME=precommit
422 precommit hook: HG_HOOKNAME=precommit
423 HG_HOOKTYPE=precommit
423 HG_HOOKTYPE=precommit
424 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
424 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
425
425
426 pretxnopen hook: HG_HOOKNAME=pretxnopen
426 pretxnopen hook: HG_HOOKNAME=pretxnopen
427 HG_HOOKTYPE=pretxnopen
427 HG_HOOKTYPE=pretxnopen
428 HG_TXNID=TXN:$ID$
428 HG_TXNID=TXN:$ID$
429 HG_TXNNAME=commit
429 HG_TXNNAME=commit
430
430
431 pretxncommit hook: HG_HOOKNAME=pretxncommit
431 pretxncommit hook: HG_HOOKNAME=pretxncommit
432 HG_HOOKTYPE=pretxncommit
432 HG_HOOKTYPE=pretxncommit
433 HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567
433 HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567
434 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
434 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
435 HG_PENDING=$TESTTMP/a
435 HG_PENDING=$TESTTMP/a
436
436
437 5:6f611f8018c1
437 5:6f611f8018c1
438 5:6f611f8018c1
438 5:6f611f8018c1
439 pretxncommit.forbid hook: HG_HOOKNAME=pretxncommit.forbid1
439 pretxncommit.forbid hook: HG_HOOKNAME=pretxncommit.forbid1
440 HG_HOOKTYPE=pretxncommit
440 HG_HOOKTYPE=pretxncommit
441 HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567
441 HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567
442 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
442 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
443 HG_PENDING=$TESTTMP/a
443 HG_PENDING=$TESTTMP/a
444
444
445 transaction abort!
445 transaction abort!
446 txnabort Python hook: changes,txnid,txnname
446 txnabort Python hook: changes,txnid,txnname
447 txnabort hook: HG_HOOKNAME=txnabort.1
447 txnabort hook: HG_HOOKNAME=txnabort.1
448 HG_HOOKTYPE=txnabort
448 HG_HOOKTYPE=txnabort
449 HG_TXNID=TXN:$ID$
449 HG_TXNID=TXN:$ID$
450 HG_TXNNAME=commit
450 HG_TXNNAME=commit
451
451
452 rollback completed
452 rollback completed
453 abort: pretxncommit.forbid1 hook exited with status 1
453 abort: pretxncommit.forbid1 hook exited with status 1
454 [40]
454 [40]
455 $ hg -q tip
455 $ hg -q tip
456 4:539e4b31b6dc
456 4:539e4b31b6dc
457
457
458 (Check that no 'changelog.i.a' file were left behind)
458 (Check that no 'changelog.i.a' file were left behind)
459
459
460 $ ls -1 .hg/store/
460 $ ls -1 .hg/store/
461 00changelog.i
461 00changelog.i
462 00manifest.i
462 00manifest.i
463 data
463 data
464 fncache (repofncache !)
464 fncache (repofncache !)
465 journal.phaseroots
465 journal.phaseroots
466 phaseroots
466 phaseroots
467 requires
467 requires
468 undo
468 undo
469 undo.backup.fncache (repofncache !)
469 undo.backup.fncache (repofncache !)
470 undo.backupfiles
470 undo.backupfiles
471 undo.phaseroots
471 undo.phaseroots
472
472
473
473
474 precommit hook can prevent commit
474 precommit hook can prevent commit
475
475
476 $ cat >> .hg/hgrc <<EOF
476 $ cat >> .hg/hgrc <<EOF
477 > precommit.forbid = sh -c "printenv.py --line precommit.forbid 1"
477 > precommit.forbid = sh -c "printenv.py --line precommit.forbid 1"
478 > EOF
478 > EOF
479 $ hg commit -m 'fail' -d '4 0'
479 $ hg commit -m 'fail' -d '4 0'
480 precommit hook: HG_HOOKNAME=precommit
480 precommit hook: HG_HOOKNAME=precommit
481 HG_HOOKTYPE=precommit
481 HG_HOOKTYPE=precommit
482 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
482 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
483
483
484 precommit.forbid hook: HG_HOOKNAME=precommit.forbid
484 precommit.forbid hook: HG_HOOKNAME=precommit.forbid
485 HG_HOOKTYPE=precommit
485 HG_HOOKTYPE=precommit
486 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
486 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
487
487
488 abort: precommit.forbid hook exited with status 1
488 abort: precommit.forbid hook exited with status 1
489 [40]
489 [40]
490 $ hg -q tip
490 $ hg -q tip
491 4:539e4b31b6dc
491 4:539e4b31b6dc
492
492
493 preupdate hook can prevent update
493 preupdate hook can prevent update
494
494
495 $ cat >> .hg/hgrc <<EOF
495 $ cat >> .hg/hgrc <<EOF
496 > preupdate = sh -c "printenv.py --line preupdate"
496 > preupdate = sh -c "printenv.py --line preupdate"
497 > EOF
497 > EOF
498 $ hg update 1
498 $ hg update 1
499 preupdate hook: HG_HOOKNAME=preupdate
499 preupdate hook: HG_HOOKNAME=preupdate
500 HG_HOOKTYPE=preupdate
500 HG_HOOKTYPE=preupdate
501 HG_PARENT1=ab228980c14d
501 HG_PARENT1=ab228980c14d
502
502
503 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
503 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
504
504
505 update hook
505 update hook
506
506
507 $ cat >> .hg/hgrc <<EOF
507 $ cat >> .hg/hgrc <<EOF
508 > update = sh -c "printenv.py --line update"
508 > update = sh -c "printenv.py --line update"
509 > EOF
509 > EOF
510 $ hg update
510 $ hg update
511 preupdate hook: HG_HOOKNAME=preupdate
511 preupdate hook: HG_HOOKNAME=preupdate
512 HG_HOOKTYPE=preupdate
512 HG_HOOKTYPE=preupdate
513 HG_PARENT1=539e4b31b6dc
513 HG_PARENT1=539e4b31b6dc
514
514
515 update hook: HG_ERROR=0
515 update hook: HG_ERROR=0
516 HG_HOOKNAME=update
516 HG_HOOKNAME=update
517 HG_HOOKTYPE=update
517 HG_HOOKTYPE=update
518 HG_PARENT1=539e4b31b6dc
518 HG_PARENT1=539e4b31b6dc
519
519
520 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
520 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
521
521
522 pushkey hook
522 pushkey hook
523
523
524 $ cat >> .hg/hgrc <<EOF
524 $ cat >> .hg/hgrc <<EOF
525 > pushkey = sh -c "printenv.py --line pushkey"
525 > pushkey = sh -c "printenv.py --line pushkey"
526 > EOF
526 > EOF
527 $ cd ../b
527 $ cd ../b
528 $ hg bookmark -r null foo
528 $ hg bookmark -r null foo
529 $ hg push -B foo ../a
529 $ hg push -B foo ../a
530 pushing to ../a
530 pushing to ../a
531 searching for changes
531 searching for changes
532 no changes found
532 no changes found
533 pretxnopen hook: HG_HOOKNAME=pretxnopen
533 pretxnopen hook: HG_HOOKNAME=pretxnopen
534 HG_HOOKTYPE=pretxnopen
534 HG_HOOKTYPE=pretxnopen
535 HG_TXNID=TXN:$ID$
535 HG_TXNID=TXN:$ID$
536 HG_TXNNAME=push
536 HG_TXNNAME=push
537
537
538 pretxnclose hook: HG_BOOKMARK_MOVED=1
538 pretxnclose hook: HG_BOOKMARK_MOVED=1
539 HG_BUNDLE2=1
539 HG_BUNDLE2=1
540 HG_HOOKNAME=pretxnclose
540 HG_HOOKNAME=pretxnclose
541 HG_HOOKTYPE=pretxnclose
541 HG_HOOKTYPE=pretxnclose
542 HG_PENDING=$TESTTMP/a
542 HG_PENDING=$TESTTMP/a
543 HG_SOURCE=push
543 HG_SOURCE=push
544 HG_TXNID=TXN:$ID$
544 HG_TXNID=TXN:$ID$
545 HG_TXNNAME=push
545 HG_TXNNAME=push
546 HG_URL=file:$TESTTMP/a
546 HG_URL=file:$TESTTMP/a
547
547
548 pushkey hook: HG_BUNDLE2=1
548 pushkey hook: HG_BUNDLE2=1
549 HG_HOOKNAME=pushkey
549 HG_HOOKNAME=pushkey
550 HG_HOOKTYPE=pushkey
550 HG_HOOKTYPE=pushkey
551 HG_KEY=foo
551 HG_KEY=foo
552 HG_NAMESPACE=bookmarks
552 HG_NAMESPACE=bookmarks
553 HG_NEW=0000000000000000000000000000000000000000
553 HG_NEW=0000000000000000000000000000000000000000
554 HG_PUSHKEYCOMPAT=1
554 HG_PUSHKEYCOMPAT=1
555 HG_SOURCE=push
555 HG_SOURCE=push
556 HG_TXNID=TXN:$ID$
556 HG_TXNID=TXN:$ID$
557 HG_TXNNAME=push
557 HG_TXNNAME=push
558 HG_URL=file:$TESTTMP/a
558 HG_URL=file:$TESTTMP/a
559
559
560 txnclose hook: HG_BOOKMARK_MOVED=1
560 txnclose hook: HG_BOOKMARK_MOVED=1
561 HG_BUNDLE2=1
561 HG_BUNDLE2=1
562 HG_HOOKNAME=txnclose
562 HG_HOOKNAME=txnclose
563 HG_HOOKTYPE=txnclose
563 HG_HOOKTYPE=txnclose
564 HG_SOURCE=push
564 HG_SOURCE=push
565 HG_TXNID=TXN:$ID$
565 HG_TXNID=TXN:$ID$
566 HG_TXNNAME=push
566 HG_TXNNAME=push
567 HG_URL=file:$TESTTMP/a
567 HG_URL=file:$TESTTMP/a
568
568
569 exporting bookmark foo
569 exporting bookmark foo
570 [1]
570 [1]
571 $ cd ../a
571 $ cd ../a
572
572
573 listkeys hook
573 listkeys hook
574
574
575 $ cat >> .hg/hgrc <<EOF
575 $ cat >> .hg/hgrc <<EOF
576 > listkeys = sh -c "printenv.py --line listkeys"
576 > listkeys = sh -c "printenv.py --line listkeys"
577 > EOF
577 > EOF
578 $ hg bookmark -r null bar
578 $ hg bookmark -r null bar
579 pretxnopen hook: HG_HOOKNAME=pretxnopen
579 pretxnopen hook: HG_HOOKNAME=pretxnopen
580 HG_HOOKTYPE=pretxnopen
580 HG_HOOKTYPE=pretxnopen
581 HG_TXNID=TXN:$ID$
581 HG_TXNID=TXN:$ID$
582 HG_TXNNAME=bookmark
582 HG_TXNNAME=bookmark
583
583
584 pretxnclose hook: HG_BOOKMARK_MOVED=1
584 pretxnclose hook: HG_BOOKMARK_MOVED=1
585 HG_HOOKNAME=pretxnclose
585 HG_HOOKNAME=pretxnclose
586 HG_HOOKTYPE=pretxnclose
586 HG_HOOKTYPE=pretxnclose
587 HG_PENDING=$TESTTMP/a
587 HG_PENDING=$TESTTMP/a
588 HG_TXNID=TXN:$ID$
588 HG_TXNID=TXN:$ID$
589 HG_TXNNAME=bookmark
589 HG_TXNNAME=bookmark
590
590
591 txnclose hook: HG_BOOKMARK_MOVED=1
591 txnclose hook: HG_BOOKMARK_MOVED=1
592 HG_HOOKNAME=txnclose
592 HG_HOOKNAME=txnclose
593 HG_HOOKTYPE=txnclose
593 HG_HOOKTYPE=txnclose
594 HG_TXNID=TXN:$ID$
594 HG_TXNID=TXN:$ID$
595 HG_TXNNAME=bookmark
595 HG_TXNNAME=bookmark
596
596
597 $ cd ../b
597 $ cd ../b
598 $ hg pull -B bar ../a
598 $ hg pull -B bar ../a
599 pulling from ../a
599 pulling from ../a
600 listkeys hook: HG_HOOKNAME=listkeys
600 listkeys hook: HG_HOOKNAME=listkeys
601 HG_HOOKTYPE=listkeys
601 HG_HOOKTYPE=listkeys
602 HG_NAMESPACE=bookmarks
602 HG_NAMESPACE=bookmarks
603 HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
603 HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
604
604
605 no changes found
605 no changes found
606 adding remote bookmark bar
606 adding remote bookmark bar
607 $ cd ../a
607 $ cd ../a
608
608
609 test that prepushkey can prevent incoming keys
609 test that prepushkey can prevent incoming keys
610
610
611 $ cat >> .hg/hgrc <<EOF
611 $ cat >> .hg/hgrc <<EOF
612 > prepushkey = sh -c "printenv.py --line prepushkey.forbid 1"
612 > prepushkey = sh -c "printenv.py --line prepushkey.forbid 1"
613 > EOF
613 > EOF
614 $ cd ../b
614 $ cd ../b
615 $ hg bookmark -r null baz
615 $ hg bookmark -r null baz
616 $ hg push -B baz ../a
616 $ hg push -B baz ../a
617 pushing to ../a
617 pushing to ../a
618 searching for changes
618 searching for changes
619 listkeys hook: HG_HOOKNAME=listkeys
619 listkeys hook: HG_HOOKNAME=listkeys
620 HG_HOOKTYPE=listkeys
620 HG_HOOKTYPE=listkeys
621 HG_NAMESPACE=phases
621 HG_NAMESPACE=phases
622 HG_VALUES={'cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b': '1', 'publishing': 'True'}
622 HG_VALUES={'cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b': '1', 'publishing': 'True'}
623
623
624 listkeys hook: HG_HOOKNAME=listkeys
624 listkeys hook: HG_HOOKNAME=listkeys
625 HG_HOOKTYPE=listkeys
625 HG_HOOKTYPE=listkeys
626 HG_NAMESPACE=bookmarks
626 HG_NAMESPACE=bookmarks
627 HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
627 HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
628
628
629 no changes found
629 no changes found
630 pretxnopen hook: HG_HOOKNAME=pretxnopen
630 pretxnopen hook: HG_HOOKNAME=pretxnopen
631 HG_HOOKTYPE=pretxnopen
631 HG_HOOKTYPE=pretxnopen
632 HG_TXNID=TXN:$ID$
632 HG_TXNID=TXN:$ID$
633 HG_TXNNAME=push
633 HG_TXNNAME=push
634
634
635 prepushkey.forbid hook: HG_BUNDLE2=1
635 prepushkey.forbid hook: HG_BUNDLE2=1
636 HG_HOOKNAME=prepushkey
636 HG_HOOKNAME=prepushkey
637 HG_HOOKTYPE=prepushkey
637 HG_HOOKTYPE=prepushkey
638 HG_KEY=baz
638 HG_KEY=baz
639 HG_NAMESPACE=bookmarks
639 HG_NAMESPACE=bookmarks
640 HG_NEW=0000000000000000000000000000000000000000
640 HG_NEW=0000000000000000000000000000000000000000
641 HG_PUSHKEYCOMPAT=1
641 HG_PUSHKEYCOMPAT=1
642 HG_SOURCE=push
642 HG_SOURCE=push
643 HG_TXNID=TXN:$ID$
643 HG_TXNID=TXN:$ID$
644 HG_TXNNAME=push
644 HG_TXNNAME=push
645 HG_URL=file:$TESTTMP/a
645 HG_URL=file:$TESTTMP/a
646
646
647 txnabort Python hook: bundle2,changes,source,txnid,txnname,url
648 txnabort hook: HG_BUNDLE2=1
649 HG_HOOKNAME=txnabort.1
650 HG_HOOKTYPE=txnabort
651 HG_SOURCE=push
652 HG_TXNID=TXN:$ID$
653 HG_TXNNAME=push
654 HG_URL=file:$TESTTMP/a
655
647 abort: prepushkey hook exited with status 1
656 abort: prepushkey hook exited with status 1
648 [40]
657 [40]
649 $ cd ../a
658 $ cd ../a
650
659
651 test that prelistkeys can prevent listing keys
660 test that prelistkeys can prevent listing keys
652
661
653 $ cat >> .hg/hgrc <<EOF
662 $ cat >> .hg/hgrc <<EOF
654 > prelistkeys = sh -c "printenv.py --line prelistkeys.forbid 1"
663 > prelistkeys = sh -c "printenv.py --line prelistkeys.forbid 1"
655 > EOF
664 > EOF
656 $ hg bookmark -r null quux
665 $ hg bookmark -r null quux
657 pretxnopen hook: HG_HOOKNAME=pretxnopen
666 pretxnopen hook: HG_HOOKNAME=pretxnopen
658 HG_HOOKTYPE=pretxnopen
667 HG_HOOKTYPE=pretxnopen
659 HG_TXNID=TXN:$ID$
668 HG_TXNID=TXN:$ID$
660 HG_TXNNAME=bookmark
669 HG_TXNNAME=bookmark
661
670
662 pretxnclose hook: HG_BOOKMARK_MOVED=1
671 pretxnclose hook: HG_BOOKMARK_MOVED=1
663 HG_HOOKNAME=pretxnclose
672 HG_HOOKNAME=pretxnclose
664 HG_HOOKTYPE=pretxnclose
673 HG_HOOKTYPE=pretxnclose
665 HG_PENDING=$TESTTMP/a
674 HG_PENDING=$TESTTMP/a
666 HG_TXNID=TXN:$ID$
675 HG_TXNID=TXN:$ID$
667 HG_TXNNAME=bookmark
676 HG_TXNNAME=bookmark
668
677
669 txnclose hook: HG_BOOKMARK_MOVED=1
678 txnclose hook: HG_BOOKMARK_MOVED=1
670 HG_HOOKNAME=txnclose
679 HG_HOOKNAME=txnclose
671 HG_HOOKTYPE=txnclose
680 HG_HOOKTYPE=txnclose
672 HG_TXNID=TXN:$ID$
681 HG_TXNID=TXN:$ID$
673 HG_TXNNAME=bookmark
682 HG_TXNNAME=bookmark
674
683
675 $ cd ../b
684 $ cd ../b
676 $ hg pull -B quux ../a
685 $ hg pull -B quux ../a
677 pulling from ../a
686 pulling from ../a
678 prelistkeys.forbid hook: HG_HOOKNAME=prelistkeys
687 prelistkeys.forbid hook: HG_HOOKNAME=prelistkeys
679 HG_HOOKTYPE=prelistkeys
688 HG_HOOKTYPE=prelistkeys
680 HG_NAMESPACE=bookmarks
689 HG_NAMESPACE=bookmarks
681
690
682 abort: prelistkeys hook exited with status 1
691 abort: prelistkeys hook exited with status 1
683 [40]
692 [40]
684 $ cd ../a
693 $ cd ../a
685 $ rm .hg/hgrc
694 $ rm .hg/hgrc
686
695
687 prechangegroup hook can prevent incoming changes
696 prechangegroup hook can prevent incoming changes
688
697
689 $ cd ../b
698 $ cd ../b
690 $ hg -q tip
699 $ hg -q tip
691 3:07f3376c1e65
700 3:07f3376c1e65
692 $ cat > .hg/hgrc <<EOF
701 $ cat > .hg/hgrc <<EOF
693 > [hooks]
702 > [hooks]
694 > prechangegroup.forbid = sh -c "printenv.py --line prechangegroup.forbid 1"
703 > prechangegroup.forbid = sh -c "printenv.py --line prechangegroup.forbid 1"
695 > EOF
704 > EOF
696 $ hg pull ../a
705 $ hg pull ../a
697 pulling from ../a
706 pulling from ../a
698 searching for changes
707 searching for changes
699 prechangegroup.forbid hook: HG_HOOKNAME=prechangegroup.forbid
708 prechangegroup.forbid hook: HG_HOOKNAME=prechangegroup.forbid
700 HG_HOOKTYPE=prechangegroup
709 HG_HOOKTYPE=prechangegroup
701 HG_SOURCE=pull
710 HG_SOURCE=pull
702 HG_TXNID=TXN:$ID$
711 HG_TXNID=TXN:$ID$
703 HG_TXNNAME=pull
712 HG_TXNNAME=pull
704 file:/*/$TESTTMP/a (glob)
713 file:/*/$TESTTMP/a (glob)
705 HG_URL=file:$TESTTMP/a
714 HG_URL=file:$TESTTMP/a
706
715
707 abort: prechangegroup.forbid hook exited with status 1
716 abort: prechangegroup.forbid hook exited with status 1
708 [40]
717 [40]
709
718
710 pretxnchangegroup hook can see incoming changes, can roll back txn,
719 pretxnchangegroup hook can see incoming changes, can roll back txn,
711 incoming changes no longer there after
720 incoming changes no longer there after
712
721
713 $ cat > .hg/hgrc <<EOF
722 $ cat > .hg/hgrc <<EOF
714 > [hooks]
723 > [hooks]
715 > pretxnchangegroup.forbid0 = hg tip -q
724 > pretxnchangegroup.forbid0 = hg tip -q
716 > pretxnchangegroup.forbid1 = sh -c "printenv.py --line pretxnchangegroup.forbid 1"
725 > pretxnchangegroup.forbid1 = sh -c "printenv.py --line pretxnchangegroup.forbid 1"
717 > EOF
726 > EOF
718 $ hg pull ../a
727 $ hg pull ../a
719 pulling from ../a
728 pulling from ../a
720 searching for changes
729 searching for changes
721 adding changesets
730 adding changesets
722 adding manifests
731 adding manifests
723 adding file changes
732 adding file changes
724 4:539e4b31b6dc
733 4:539e4b31b6dc
725 pretxnchangegroup.forbid hook: HG_HOOKNAME=pretxnchangegroup.forbid1
734 pretxnchangegroup.forbid hook: HG_HOOKNAME=pretxnchangegroup.forbid1
726 HG_HOOKTYPE=pretxnchangegroup
735 HG_HOOKTYPE=pretxnchangegroup
727 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
736 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
728 HG_NODE_LAST=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
737 HG_NODE_LAST=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
729 HG_PENDING=$TESTTMP/b
738 HG_PENDING=$TESTTMP/b
730 HG_SOURCE=pull
739 HG_SOURCE=pull
731 HG_TXNID=TXN:$ID$
740 HG_TXNID=TXN:$ID$
732 HG_TXNNAME=pull
741 HG_TXNNAME=pull
733 file:/*/$TESTTMP/a (glob)
742 file:/*/$TESTTMP/a (glob)
734 HG_URL=file:$TESTTMP/a
743 HG_URL=file:$TESTTMP/a
735
744
736 transaction abort!
745 transaction abort!
737 rollback completed
746 rollback completed
738 abort: pretxnchangegroup.forbid1 hook exited with status 1
747 abort: pretxnchangegroup.forbid1 hook exited with status 1
739 [40]
748 [40]
740 $ hg -q tip
749 $ hg -q tip
741 3:07f3376c1e65
750 3:07f3376c1e65
742
751
743 outgoing hooks can see env vars
752 outgoing hooks can see env vars
744
753
745 $ rm .hg/hgrc
754 $ rm .hg/hgrc
746 $ cat > ../a/.hg/hgrc <<EOF
755 $ cat > ../a/.hg/hgrc <<EOF
747 > [hooks]
756 > [hooks]
748 > preoutgoing = sh -c "printenv.py --line preoutgoing"
757 > preoutgoing = sh -c "printenv.py --line preoutgoing"
749 > outgoing = sh -c "printenv.py --line outgoing"
758 > outgoing = sh -c "printenv.py --line outgoing"
750 > EOF
759 > EOF
751 $ hg pull ../a
760 $ hg pull ../a
752 pulling from ../a
761 pulling from ../a
753 searching for changes
762 searching for changes
754 preoutgoing hook: HG_HOOKNAME=preoutgoing
763 preoutgoing hook: HG_HOOKNAME=preoutgoing
755 HG_HOOKTYPE=preoutgoing
764 HG_HOOKTYPE=preoutgoing
756 HG_SOURCE=pull
765 HG_SOURCE=pull
757
766
758 outgoing hook: HG_HOOKNAME=outgoing
767 outgoing hook: HG_HOOKNAME=outgoing
759 HG_HOOKTYPE=outgoing
768 HG_HOOKTYPE=outgoing
760 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
769 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
761 HG_SOURCE=pull
770 HG_SOURCE=pull
762
771
763 adding changesets
772 adding changesets
764 adding manifests
773 adding manifests
765 adding file changes
774 adding file changes
766 adding remote bookmark quux
775 adding remote bookmark quux
767 added 1 changesets with 1 changes to 1 files
776 added 1 changesets with 1 changes to 1 files
768 new changesets 539e4b31b6dc
777 new changesets 539e4b31b6dc
769 (run 'hg update' to get a working copy)
778 (run 'hg update' to get a working copy)
770 $ hg rollback
779 $ hg rollback
771 repository tip rolled back to revision 3 (undo pull)
780 repository tip rolled back to revision 3 (undo pull)
772
781
773 preoutgoing hook can prevent outgoing changes
782 preoutgoing hook can prevent outgoing changes
774
783
775 $ cat >> ../a/.hg/hgrc <<EOF
784 $ cat >> ../a/.hg/hgrc <<EOF
776 > preoutgoing.forbid = sh -c "printenv.py --line preoutgoing.forbid 1"
785 > preoutgoing.forbid = sh -c "printenv.py --line preoutgoing.forbid 1"
777 > EOF
786 > EOF
778 $ hg pull ../a
787 $ hg pull ../a
779 pulling from ../a
788 pulling from ../a
780 searching for changes
789 searching for changes
781 preoutgoing hook: HG_HOOKNAME=preoutgoing
790 preoutgoing hook: HG_HOOKNAME=preoutgoing
782 HG_HOOKTYPE=preoutgoing
791 HG_HOOKTYPE=preoutgoing
783 HG_SOURCE=pull
792 HG_SOURCE=pull
784
793
785 preoutgoing.forbid hook: HG_HOOKNAME=preoutgoing.forbid
794 preoutgoing.forbid hook: HG_HOOKNAME=preoutgoing.forbid
786 HG_HOOKTYPE=preoutgoing
795 HG_HOOKTYPE=preoutgoing
787 HG_SOURCE=pull
796 HG_SOURCE=pull
788
797
789 abort: preoutgoing.forbid hook exited with status 1
798 abort: preoutgoing.forbid hook exited with status 1
790 [40]
799 [40]
791
800
792 outgoing hooks work for local clones
801 outgoing hooks work for local clones
793
802
794 $ cd ..
803 $ cd ..
795 $ cat > a/.hg/hgrc <<EOF
804 $ cat > a/.hg/hgrc <<EOF
796 > [hooks]
805 > [hooks]
797 > preoutgoing = sh -c "printenv.py --line preoutgoing"
806 > preoutgoing = sh -c "printenv.py --line preoutgoing"
798 > outgoing = sh -c "printenv.py --line outgoing"
807 > outgoing = sh -c "printenv.py --line outgoing"
799 > EOF
808 > EOF
800 $ hg clone a c
809 $ hg clone a c
801 preoutgoing hook: HG_HOOKNAME=preoutgoing
810 preoutgoing hook: HG_HOOKNAME=preoutgoing
802 HG_HOOKTYPE=preoutgoing
811 HG_HOOKTYPE=preoutgoing
803 HG_SOURCE=clone
812 HG_SOURCE=clone
804
813
805 outgoing hook: HG_HOOKNAME=outgoing
814 outgoing hook: HG_HOOKNAME=outgoing
806 HG_HOOKTYPE=outgoing
815 HG_HOOKTYPE=outgoing
807 HG_NODE=0000000000000000000000000000000000000000
816 HG_NODE=0000000000000000000000000000000000000000
808 HG_SOURCE=clone
817 HG_SOURCE=clone
809
818
810 updating to branch default
819 updating to branch default
811 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
820 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
812 $ rm -rf c
821 $ rm -rf c
813
822
814 preoutgoing hook can prevent outgoing changes for local clones
823 preoutgoing hook can prevent outgoing changes for local clones
815
824
816 $ cat >> a/.hg/hgrc <<EOF
825 $ cat >> a/.hg/hgrc <<EOF
817 > preoutgoing.forbid = sh -c "printenv.py --line preoutgoing.forbid 1"
826 > preoutgoing.forbid = sh -c "printenv.py --line preoutgoing.forbid 1"
818 > EOF
827 > EOF
819 $ hg clone a zzz
828 $ hg clone a zzz
820 preoutgoing hook: HG_HOOKNAME=preoutgoing
829 preoutgoing hook: HG_HOOKNAME=preoutgoing
821 HG_HOOKTYPE=preoutgoing
830 HG_HOOKTYPE=preoutgoing
822 HG_SOURCE=clone
831 HG_SOURCE=clone
823
832
824 preoutgoing.forbid hook: HG_HOOKNAME=preoutgoing.forbid
833 preoutgoing.forbid hook: HG_HOOKNAME=preoutgoing.forbid
825 HG_HOOKTYPE=preoutgoing
834 HG_HOOKTYPE=preoutgoing
826 HG_SOURCE=clone
835 HG_SOURCE=clone
827
836
828 abort: preoutgoing.forbid hook exited with status 1
837 abort: preoutgoing.forbid hook exited with status 1
829 [40]
838 [40]
830
839
831 $ cd "$TESTTMP/b"
840 $ cd "$TESTTMP/b"
832
841
833 $ cat > hooktests.py <<EOF
842 $ cat > hooktests.py <<EOF
834 > from mercurial import (
843 > from mercurial import (
835 > error,
844 > error,
836 > pycompat,
845 > pycompat,
837 > )
846 > )
838 >
847 >
839 > uncallable = 0
848 > uncallable = 0
840 >
849 >
841 > def printargs(ui, args):
850 > def printargs(ui, args):
842 > a = list(pycompat.byteskwargs(args).items())
851 > a = list(pycompat.byteskwargs(args).items())
843 > a.sort()
852 > a.sort()
844 > ui.write(b'hook args:\n')
853 > ui.write(b'hook args:\n')
845 > for k, v in a:
854 > for k, v in a:
846 > ui.write(b' %s %s\n' % (k, v))
855 > ui.write(b' %s %s\n' % (k, v))
847 >
856 >
848 > def passhook(ui, repo, **args):
857 > def passhook(ui, repo, **args):
849 > printargs(ui, args)
858 > printargs(ui, args)
850 >
859 >
851 > def failhook(ui, repo, **args):
860 > def failhook(ui, repo, **args):
852 > printargs(ui, args)
861 > printargs(ui, args)
853 > return True
862 > return True
854 >
863 >
855 > class LocalException(Exception):
864 > class LocalException(Exception):
856 > pass
865 > pass
857 >
866 >
858 > def raisehook(**args):
867 > def raisehook(**args):
859 > raise LocalException('exception from hook')
868 > raise LocalException('exception from hook')
860 >
869 >
861 > def aborthook(**args):
870 > def aborthook(**args):
862 > raise error.Abort(b'raise abort from hook')
871 > raise error.Abort(b'raise abort from hook')
863 >
872 >
864 > def brokenhook(**args):
873 > def brokenhook(**args):
865 > return 1 + {}
874 > return 1 + {}
866 >
875 >
867 > def verbosehook(ui, **args):
876 > def verbosehook(ui, **args):
868 > ui.note(b'verbose output from hook\n')
877 > ui.note(b'verbose output from hook\n')
869 >
878 >
870 > def printtags(ui, repo, **args):
879 > def printtags(ui, repo, **args):
871 > ui.write(b'[%s]\n' % b', '.join(sorted(repo.tags())))
880 > ui.write(b'[%s]\n' % b', '.join(sorted(repo.tags())))
872 >
881 >
873 > class container(object):
882 > class container(object):
874 > unreachable = 1
883 > unreachable = 1
875 > EOF
884 > EOF
876
885
877 $ cat > syntaxerror.py << NO_CHECK_EOF
886 $ cat > syntaxerror.py << NO_CHECK_EOF
878 > (foo
887 > (foo
879 > NO_CHECK_EOF
888 > NO_CHECK_EOF
880
889
881 test python hooks
890 test python hooks
882
891
883 #if windows
892 #if windows
884 $ PYTHONPATH="$TESTTMP/b;$PYTHONPATH"
893 $ PYTHONPATH="$TESTTMP/b;$PYTHONPATH"
885 #else
894 #else
886 $ PYTHONPATH="$TESTTMP/b:$PYTHONPATH"
895 $ PYTHONPATH="$TESTTMP/b:$PYTHONPATH"
887 #endif
896 #endif
888 $ export PYTHONPATH
897 $ export PYTHONPATH
889
898
890 $ echo '[hooks]' > ../a/.hg/hgrc
899 $ echo '[hooks]' > ../a/.hg/hgrc
891 $ echo 'preoutgoing.broken = python:hooktests.brokenhook' >> ../a/.hg/hgrc
900 $ echo 'preoutgoing.broken = python:hooktests.brokenhook' >> ../a/.hg/hgrc
892 $ hg pull ../a 2>&1 | grep 'raised an exception'
901 $ hg pull ../a 2>&1 | grep 'raised an exception'
893 error: preoutgoing.broken hook raised an exception: unsupported operand type(s) for +: 'int' and 'dict'
902 error: preoutgoing.broken hook raised an exception: unsupported operand type(s) for +: 'int' and 'dict'
894
903
895 $ echo '[hooks]' > ../a/.hg/hgrc
904 $ echo '[hooks]' > ../a/.hg/hgrc
896 $ echo 'preoutgoing.raise = python:hooktests.raisehook' >> ../a/.hg/hgrc
905 $ echo 'preoutgoing.raise = python:hooktests.raisehook' >> ../a/.hg/hgrc
897 $ hg pull ../a 2>&1 | grep 'raised an exception'
906 $ hg pull ../a 2>&1 | grep 'raised an exception'
898 error: preoutgoing.raise hook raised an exception: exception from hook
907 error: preoutgoing.raise hook raised an exception: exception from hook
899
908
900 $ echo '[hooks]' > ../a/.hg/hgrc
909 $ echo '[hooks]' > ../a/.hg/hgrc
901 $ echo 'preoutgoing.abort = python:hooktests.aborthook' >> ../a/.hg/hgrc
910 $ echo 'preoutgoing.abort = python:hooktests.aborthook' >> ../a/.hg/hgrc
902 $ hg pull ../a
911 $ hg pull ../a
903 pulling from ../a
912 pulling from ../a
904 searching for changes
913 searching for changes
905 error: preoutgoing.abort hook failed: raise abort from hook
914 error: preoutgoing.abort hook failed: raise abort from hook
906 abort: raise abort from hook
915 abort: raise abort from hook
907 [255]
916 [255]
908
917
909 $ echo '[hooks]' > ../a/.hg/hgrc
918 $ echo '[hooks]' > ../a/.hg/hgrc
910 $ echo 'preoutgoing.fail = python:hooktests.failhook' >> ../a/.hg/hgrc
919 $ echo 'preoutgoing.fail = python:hooktests.failhook' >> ../a/.hg/hgrc
911 $ hg pull ../a
920 $ hg pull ../a
912 pulling from ../a
921 pulling from ../a
913 searching for changes
922 searching for changes
914 hook args:
923 hook args:
915 hooktype preoutgoing
924 hooktype preoutgoing
916 source pull
925 source pull
917 abort: preoutgoing.fail hook failed
926 abort: preoutgoing.fail hook failed
918 [40]
927 [40]
919
928
920 $ echo '[hooks]' > ../a/.hg/hgrc
929 $ echo '[hooks]' > ../a/.hg/hgrc
921 $ echo 'preoutgoing.uncallable = python:hooktests.uncallable' >> ../a/.hg/hgrc
930 $ echo 'preoutgoing.uncallable = python:hooktests.uncallable' >> ../a/.hg/hgrc
922 $ hg pull ../a
931 $ hg pull ../a
923 pulling from ../a
932 pulling from ../a
924 searching for changes
933 searching for changes
925 abort: preoutgoing.uncallable hook is invalid: "hooktests.uncallable" is not callable
934 abort: preoutgoing.uncallable hook is invalid: "hooktests.uncallable" is not callable
926 [255]
935 [255]
927
936
928 $ echo '[hooks]' > ../a/.hg/hgrc
937 $ echo '[hooks]' > ../a/.hg/hgrc
929 $ echo 'preoutgoing.nohook = python:hooktests.nohook' >> ../a/.hg/hgrc
938 $ echo 'preoutgoing.nohook = python:hooktests.nohook' >> ../a/.hg/hgrc
930 $ hg pull ../a
939 $ hg pull ../a
931 pulling from ../a
940 pulling from ../a
932 searching for changes
941 searching for changes
933 abort: preoutgoing.nohook hook is invalid: "hooktests.nohook" is not defined
942 abort: preoutgoing.nohook hook is invalid: "hooktests.nohook" is not defined
934 [255]
943 [255]
935
944
936 $ echo '[hooks]' > ../a/.hg/hgrc
945 $ echo '[hooks]' > ../a/.hg/hgrc
937 $ echo 'preoutgoing.nomodule = python:nomodule' >> ../a/.hg/hgrc
946 $ echo 'preoutgoing.nomodule = python:nomodule' >> ../a/.hg/hgrc
938 $ hg pull ../a
947 $ hg pull ../a
939 pulling from ../a
948 pulling from ../a
940 searching for changes
949 searching for changes
941 abort: preoutgoing.nomodule hook is invalid: "nomodule" not in a module
950 abort: preoutgoing.nomodule hook is invalid: "nomodule" not in a module
942 [255]
951 [255]
943
952
944 $ echo '[hooks]' > ../a/.hg/hgrc
953 $ echo '[hooks]' > ../a/.hg/hgrc
945 $ echo 'preoutgoing.badmodule = python:nomodule.nowhere' >> ../a/.hg/hgrc
954 $ echo 'preoutgoing.badmodule = python:nomodule.nowhere' >> ../a/.hg/hgrc
946 $ hg pull ../a
955 $ hg pull ../a
947 pulling from ../a
956 pulling from ../a
948 searching for changes
957 searching for changes
949 abort: preoutgoing.badmodule hook is invalid: import of "nomodule" failed
958 abort: preoutgoing.badmodule hook is invalid: import of "nomodule" failed
950 (run with --traceback for stack trace)
959 (run with --traceback for stack trace)
951 [255]
960 [255]
952
961
953 $ echo '[hooks]' > ../a/.hg/hgrc
962 $ echo '[hooks]' > ../a/.hg/hgrc
954 $ echo 'preoutgoing.unreachable = python:hooktests.container.unreachable' >> ../a/.hg/hgrc
963 $ echo 'preoutgoing.unreachable = python:hooktests.container.unreachable' >> ../a/.hg/hgrc
955 $ hg pull ../a
964 $ hg pull ../a
956 pulling from ../a
965 pulling from ../a
957 searching for changes
966 searching for changes
958 abort: preoutgoing.unreachable hook is invalid: import of "hooktests.container" failed
967 abort: preoutgoing.unreachable hook is invalid: import of "hooktests.container" failed
959 (run with --traceback for stack trace)
968 (run with --traceback for stack trace)
960 [255]
969 [255]
961
970
962 $ echo '[hooks]' > ../a/.hg/hgrc
971 $ echo '[hooks]' > ../a/.hg/hgrc
963 $ echo 'preoutgoing.syntaxerror = python:syntaxerror.syntaxerror' >> ../a/.hg/hgrc
972 $ echo 'preoutgoing.syntaxerror = python:syntaxerror.syntaxerror' >> ../a/.hg/hgrc
964 $ hg pull ../a
973 $ hg pull ../a
965 pulling from ../a
974 pulling from ../a
966 searching for changes
975 searching for changes
967 abort: preoutgoing.syntaxerror hook is invalid: import of "syntaxerror" failed
976 abort: preoutgoing.syntaxerror hook is invalid: import of "syntaxerror" failed
968 (run with --traceback for stack trace)
977 (run with --traceback for stack trace)
969 [255]
978 [255]
970
979
971 $ hg pull ../a --traceback 2>&1 | egrep 'pulling|searching|^exception|Traceback|SyntaxError|ImportError|ModuleNotFoundError|HookLoadError|abort'
980 $ hg pull ../a --traceback 2>&1 | egrep 'pulling|searching|^exception|Traceback|SyntaxError|ImportError|ModuleNotFoundError|HookLoadError|abort'
972 pulling from ../a
981 pulling from ../a
973 searching for changes
982 searching for changes
974 exception from first failed import attempt:
983 exception from first failed import attempt:
975 Traceback (most recent call last):
984 Traceback (most recent call last):
976 SyntaxError: * (glob)
985 SyntaxError: * (glob)
977 exception from second failed import attempt:
986 exception from second failed import attempt:
978 Traceback (most recent call last):
987 Traceback (most recent call last):
979 SyntaxError: * (glob)
988 SyntaxError: * (glob)
980 Traceback (most recent call last):
989 Traceback (most recent call last):
981 ImportError: No module named 'hgext_syntaxerror' (no-py36 !)
990 ImportError: No module named 'hgext_syntaxerror' (no-py36 !)
982 ModuleNotFoundError: No module named 'hgext_syntaxerror' (py36 !)
991 ModuleNotFoundError: No module named 'hgext_syntaxerror' (py36 !)
983 Traceback (most recent call last):
992 Traceback (most recent call last):
984 SyntaxError: * (glob)
993 SyntaxError: * (glob)
985 Traceback (most recent call last):
994 Traceback (most recent call last):
986 ImportError: No module named 'hgext_syntaxerror' (no-py36 !)
995 ImportError: No module named 'hgext_syntaxerror' (no-py36 !)
987 ModuleNotFoundError: No module named 'hgext_syntaxerror' (py36 !)
996 ModuleNotFoundError: No module named 'hgext_syntaxerror' (py36 !)
988 Traceback (most recent call last):
997 Traceback (most recent call last):
989 raise error.HookLoadError( (py38 !)
998 raise error.HookLoadError( (py38 !)
990 mercurial.error.HookLoadError: preoutgoing.syntaxerror hook is invalid: import of "syntaxerror" failed
999 mercurial.error.HookLoadError: preoutgoing.syntaxerror hook is invalid: import of "syntaxerror" failed
991 abort: preoutgoing.syntaxerror hook is invalid: import of "syntaxerror" failed
1000 abort: preoutgoing.syntaxerror hook is invalid: import of "syntaxerror" failed
992
1001
993 $ echo '[hooks]' > ../a/.hg/hgrc
1002 $ echo '[hooks]' > ../a/.hg/hgrc
994 $ echo 'preoutgoing.pass = python:hooktests.passhook' >> ../a/.hg/hgrc
1003 $ echo 'preoutgoing.pass = python:hooktests.passhook' >> ../a/.hg/hgrc
995 $ hg pull ../a
1004 $ hg pull ../a
996 pulling from ../a
1005 pulling from ../a
997 searching for changes
1006 searching for changes
998 hook args:
1007 hook args:
999 hooktype preoutgoing
1008 hooktype preoutgoing
1000 source pull
1009 source pull
1001 adding changesets
1010 adding changesets
1002 adding manifests
1011 adding manifests
1003 adding file changes
1012 adding file changes
1004 adding remote bookmark quux
1013 adding remote bookmark quux
1005 added 1 changesets with 1 changes to 1 files
1014 added 1 changesets with 1 changes to 1 files
1006 new changesets 539e4b31b6dc
1015 new changesets 539e4b31b6dc
1007 (run 'hg update' to get a working copy)
1016 (run 'hg update' to get a working copy)
1008
1017
1009 post- python hooks that fail to *run* don't cause an abort
1018 post- python hooks that fail to *run* don't cause an abort
1010 $ rm ../a/.hg/hgrc
1019 $ rm ../a/.hg/hgrc
1011 $ echo '[hooks]' > .hg/hgrc
1020 $ echo '[hooks]' > .hg/hgrc
1012 $ echo 'post-pull.broken = python:hooktests.brokenhook' >> .hg/hgrc
1021 $ echo 'post-pull.broken = python:hooktests.brokenhook' >> .hg/hgrc
1013 $ hg pull ../a
1022 $ hg pull ../a
1014 pulling from ../a
1023 pulling from ../a
1015 searching for changes
1024 searching for changes
1016 no changes found
1025 no changes found
1017 error: post-pull.broken hook raised an exception: unsupported operand type(s) for +: 'int' and 'dict'
1026 error: post-pull.broken hook raised an exception: unsupported operand type(s) for +: 'int' and 'dict'
1018 (run with --traceback for stack trace)
1027 (run with --traceback for stack trace)
1019
1028
1020 but post- python hooks that fail to *load* do
1029 but post- python hooks that fail to *load* do
1021 $ echo '[hooks]' > .hg/hgrc
1030 $ echo '[hooks]' > .hg/hgrc
1022 $ echo 'post-pull.nomodule = python:nomodule' >> .hg/hgrc
1031 $ echo 'post-pull.nomodule = python:nomodule' >> .hg/hgrc
1023 $ hg pull ../a
1032 $ hg pull ../a
1024 pulling from ../a
1033 pulling from ../a
1025 searching for changes
1034 searching for changes
1026 no changes found
1035 no changes found
1027 abort: post-pull.nomodule hook is invalid: "nomodule" not in a module
1036 abort: post-pull.nomodule hook is invalid: "nomodule" not in a module
1028 [255]
1037 [255]
1029
1038
1030 $ echo '[hooks]' > .hg/hgrc
1039 $ echo '[hooks]' > .hg/hgrc
1031 $ echo 'post-pull.badmodule = python:nomodule.nowhere' >> .hg/hgrc
1040 $ echo 'post-pull.badmodule = python:nomodule.nowhere' >> .hg/hgrc
1032 $ hg pull ../a
1041 $ hg pull ../a
1033 pulling from ../a
1042 pulling from ../a
1034 searching for changes
1043 searching for changes
1035 no changes found
1044 no changes found
1036 abort: post-pull.badmodule hook is invalid: import of "nomodule" failed
1045 abort: post-pull.badmodule hook is invalid: import of "nomodule" failed
1037 (run with --traceback for stack trace)
1046 (run with --traceback for stack trace)
1038 [255]
1047 [255]
1039
1048
1040 $ echo '[hooks]' > .hg/hgrc
1049 $ echo '[hooks]' > .hg/hgrc
1041 $ echo 'post-pull.nohook = python:hooktests.nohook' >> .hg/hgrc
1050 $ echo 'post-pull.nohook = python:hooktests.nohook' >> .hg/hgrc
1042 $ hg pull ../a
1051 $ hg pull ../a
1043 pulling from ../a
1052 pulling from ../a
1044 searching for changes
1053 searching for changes
1045 no changes found
1054 no changes found
1046 abort: post-pull.nohook hook is invalid: "hooktests.nohook" is not defined
1055 abort: post-pull.nohook hook is invalid: "hooktests.nohook" is not defined
1047 [255]
1056 [255]
1048
1057
1049 make sure --traceback works
1058 make sure --traceback works
1050
1059
1051 $ echo '[hooks]' > .hg/hgrc
1060 $ echo '[hooks]' > .hg/hgrc
1052 $ echo 'commit.abort = python:hooktests.aborthook' >> .hg/hgrc
1061 $ echo 'commit.abort = python:hooktests.aborthook' >> .hg/hgrc
1053
1062
1054 $ echo aa > a
1063 $ echo aa > a
1055 $ hg --traceback commit -d '0 0' -ma 2>&1 | grep '^Traceback'
1064 $ hg --traceback commit -d '0 0' -ma 2>&1 | grep '^Traceback'
1056 Traceback (most recent call last):
1065 Traceback (most recent call last):
1057
1066
1058 $ cd ..
1067 $ cd ..
1059 $ hg init c
1068 $ hg init c
1060 $ cd c
1069 $ cd c
1061
1070
1062 $ cat > hookext.py <<EOF
1071 $ cat > hookext.py <<EOF
1063 > def autohook(ui, **args):
1072 > def autohook(ui, **args):
1064 > ui.write(b'Automatically installed hook\n')
1073 > ui.write(b'Automatically installed hook\n')
1065 >
1074 >
1066 > def reposetup(ui, repo):
1075 > def reposetup(ui, repo):
1067 > repo.ui.setconfig(b"hooks", b"commit.auto", autohook)
1076 > repo.ui.setconfig(b"hooks", b"commit.auto", autohook)
1068 > EOF
1077 > EOF
1069 $ echo '[extensions]' >> .hg/hgrc
1078 $ echo '[extensions]' >> .hg/hgrc
1070 $ echo 'hookext = hookext.py' >> .hg/hgrc
1079 $ echo 'hookext = hookext.py' >> .hg/hgrc
1071
1080
1072 $ touch foo
1081 $ touch foo
1073 $ hg add foo
1082 $ hg add foo
1074 $ hg ci -d '0 0' -m 'add foo'
1083 $ hg ci -d '0 0' -m 'add foo'
1075 Automatically installed hook
1084 Automatically installed hook
1076 $ echo >> foo
1085 $ echo >> foo
1077 $ hg ci --debug -d '0 0' -m 'change foo'
1086 $ hg ci --debug -d '0 0' -m 'change foo'
1078 committing files:
1087 committing files:
1079 foo
1088 foo
1080 committing manifest
1089 committing manifest
1081 committing changelog
1090 committing changelog
1082 updating the branch cache
1091 updating the branch cache
1083 committed changeset 1:52998019f6252a2b893452765fcb0a47351a5708
1092 committed changeset 1:52998019f6252a2b893452765fcb0a47351a5708
1084 calling hook commit.auto: hgext_hookext.autohook
1093 calling hook commit.auto: hgext_hookext.autohook
1085 Automatically installed hook
1094 Automatically installed hook
1086
1095
1087 $ hg showconfig hooks
1096 $ hg showconfig hooks
1088 hooks.commit.auto=<function autohook at *> (glob)
1097 hooks.commit.auto=<function autohook at *> (glob)
1089
1098
1090 test python hook configured with python:[file]:[hook] syntax
1099 test python hook configured with python:[file]:[hook] syntax
1091
1100
1092 $ cd ..
1101 $ cd ..
1093 $ mkdir d
1102 $ mkdir d
1094 $ cd d
1103 $ cd d
1095 $ hg init repo
1104 $ hg init repo
1096 $ mkdir hooks
1105 $ mkdir hooks
1097
1106
1098 $ cd hooks
1107 $ cd hooks
1099 $ cat > testhooks.py <<EOF
1108 $ cat > testhooks.py <<EOF
1100 > def testhook(ui, **args):
1109 > def testhook(ui, **args):
1101 > ui.write(b'hook works\n')
1110 > ui.write(b'hook works\n')
1102 > EOF
1111 > EOF
1103 $ echo '[hooks]' > ../repo/.hg/hgrc
1112 $ echo '[hooks]' > ../repo/.hg/hgrc
1104 $ echo "pre-commit.test = python:`pwd`/testhooks.py:testhook" >> ../repo/.hg/hgrc
1113 $ echo "pre-commit.test = python:`pwd`/testhooks.py:testhook" >> ../repo/.hg/hgrc
1105
1114
1106 $ cd ../repo
1115 $ cd ../repo
1107 $ hg commit -d '0 0'
1116 $ hg commit -d '0 0'
1108 hook works
1117 hook works
1109 nothing changed
1118 nothing changed
1110 [1]
1119 [1]
1111
1120
1112 $ echo '[hooks]' > .hg/hgrc
1121 $ echo '[hooks]' > .hg/hgrc
1113 $ echo "update.ne = python:`pwd`/nonexistent.py:testhook" >> .hg/hgrc
1122 $ echo "update.ne = python:`pwd`/nonexistent.py:testhook" >> .hg/hgrc
1114 $ echo "pre-identify.npmd = python:`pwd`/:no_python_module_dir" >> .hg/hgrc
1123 $ echo "pre-identify.npmd = python:`pwd`/:no_python_module_dir" >> .hg/hgrc
1115
1124
1116 $ hg up null
1125 $ hg up null
1117 loading update.ne hook failed:
1126 loading update.ne hook failed:
1118 abort: $ENOENT$: '$TESTTMP/d/repo/nonexistent.py'
1127 abort: $ENOENT$: '$TESTTMP/d/repo/nonexistent.py'
1119 [255]
1128 [255]
1120
1129
1121 $ hg id
1130 $ hg id
1122 loading pre-identify.npmd hook failed:
1131 loading pre-identify.npmd hook failed:
1123 abort: No module named 'repo'
1132 abort: No module named 'repo'
1124 [255]
1133 [255]
1125
1134
1126 $ cd ../../b
1135 $ cd ../../b
1127
1136
1128 make sure --traceback works on hook import failure
1137 make sure --traceback works on hook import failure
1129
1138
1130 $ cat > importfail.py <<EOF
1139 $ cat > importfail.py <<EOF
1131 > import somebogusmodule
1140 > import somebogusmodule
1132 > # dereference something in the module to force demandimport to load it
1141 > # dereference something in the module to force demandimport to load it
1133 > somebogusmodule.whatever
1142 > somebogusmodule.whatever
1134 > EOF
1143 > EOF
1135
1144
1136 $ echo '[hooks]' > .hg/hgrc
1145 $ echo '[hooks]' > .hg/hgrc
1137 $ echo 'precommit.importfail = python:importfail.whatever' >> .hg/hgrc
1146 $ echo 'precommit.importfail = python:importfail.whatever' >> .hg/hgrc
1138
1147
1139 $ echo a >> a
1148 $ echo a >> a
1140 $ hg --traceback commit -ma 2>&1 | egrep '^exception|ImportError|ModuleNotFoundError|Traceback|HookLoadError|abort'
1149 $ hg --traceback commit -ma 2>&1 | egrep '^exception|ImportError|ModuleNotFoundError|Traceback|HookLoadError|abort'
1141 exception from first failed import attempt:
1150 exception from first failed import attempt:
1142 Traceback (most recent call last):
1151 Traceback (most recent call last):
1143 ImportError: No module named 'somebogusmodule' (no-py36 !)
1152 ImportError: No module named 'somebogusmodule' (no-py36 !)
1144 ModuleNotFoundError: No module named 'somebogusmodule' (py36 !)
1153 ModuleNotFoundError: No module named 'somebogusmodule' (py36 !)
1145 exception from second failed import attempt:
1154 exception from second failed import attempt:
1146 Traceback (most recent call last):
1155 Traceback (most recent call last):
1147 ImportError: No module named 'somebogusmodule' (no-py36 !)
1156 ImportError: No module named 'somebogusmodule' (no-py36 !)
1148 ModuleNotFoundError: No module named 'somebogusmodule' (py36 !)
1157 ModuleNotFoundError: No module named 'somebogusmodule' (py36 !)
1149 Traceback (most recent call last):
1158 Traceback (most recent call last):
1150 ImportError: No module named 'hgext_importfail' (no-py36 !)
1159 ImportError: No module named 'hgext_importfail' (no-py36 !)
1151 ModuleNotFoundError: No module named 'hgext_importfail' (py36 !)
1160 ModuleNotFoundError: No module named 'hgext_importfail' (py36 !)
1152 Traceback (most recent call last):
1161 Traceback (most recent call last):
1153 ImportError: No module named 'somebogusmodule' (no-py36 !)
1162 ImportError: No module named 'somebogusmodule' (no-py36 !)
1154 ModuleNotFoundError: No module named 'somebogusmodule' (py36 !)
1163 ModuleNotFoundError: No module named 'somebogusmodule' (py36 !)
1155 Traceback (most recent call last):
1164 Traceback (most recent call last):
1156 ImportError: No module named 'hgext_importfail' (no-py36 !)
1165 ImportError: No module named 'hgext_importfail' (no-py36 !)
1157 ModuleNotFoundError: No module named 'hgext_importfail' (py36 !)
1166 ModuleNotFoundError: No module named 'hgext_importfail' (py36 !)
1158 Traceback (most recent call last):
1167 Traceback (most recent call last):
1159 raise error.HookLoadError( (py38 !)
1168 raise error.HookLoadError( (py38 !)
1160 mercurial.error.HookLoadError: precommit.importfail hook is invalid: import of "importfail" failed
1169 mercurial.error.HookLoadError: precommit.importfail hook is invalid: import of "importfail" failed
1161 abort: precommit.importfail hook is invalid: import of "importfail" failed
1170 abort: precommit.importfail hook is invalid: import of "importfail" failed
1162
1171
1163 Issue1827: Hooks Update & Commit not completely post operation
1172 Issue1827: Hooks Update & Commit not completely post operation
1164
1173
1165 commit and update hooks should run after command completion. The largefiles
1174 commit and update hooks should run after command completion. The largefiles
1166 use demonstrates a recursive wlock, showing the hook doesn't run until the
1175 use demonstrates a recursive wlock, showing the hook doesn't run until the
1167 final release (and dirstate flush).
1176 final release (and dirstate flush).
1168
1177
1169 $ echo '[hooks]' > .hg/hgrc
1178 $ echo '[hooks]' > .hg/hgrc
1170 $ echo 'commit = hg id' >> .hg/hgrc
1179 $ echo 'commit = hg id' >> .hg/hgrc
1171 $ echo 'update = hg id' >> .hg/hgrc
1180 $ echo 'update = hg id' >> .hg/hgrc
1172 $ echo bb > a
1181 $ echo bb > a
1173 $ hg ci -ma
1182 $ hg ci -ma
1174 223eafe2750c tip
1183 223eafe2750c tip
1175 $ hg up 0 --config extensions.largefiles=
1184 $ hg up 0 --config extensions.largefiles=
1176 The fsmonitor extension is incompatible with the largefiles extension and has been disabled. (fsmonitor !)
1185 The fsmonitor extension is incompatible with the largefiles extension and has been disabled. (fsmonitor !)
1177 cb9a9f314b8b
1186 cb9a9f314b8b
1178 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1187 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1179
1188
1180 make sure --verbose (and --quiet/--debug etc.) are propagated to the local ui
1189 make sure --verbose (and --quiet/--debug etc.) are propagated to the local ui
1181 that is passed to pre/post hooks
1190 that is passed to pre/post hooks
1182
1191
1183 $ echo '[hooks]' > .hg/hgrc
1192 $ echo '[hooks]' > .hg/hgrc
1184 $ echo 'pre-identify = python:hooktests.verbosehook' >> .hg/hgrc
1193 $ echo 'pre-identify = python:hooktests.verbosehook' >> .hg/hgrc
1185 $ hg id
1194 $ hg id
1186 cb9a9f314b8b
1195 cb9a9f314b8b
1187 $ hg id --verbose
1196 $ hg id --verbose
1188 calling hook pre-identify: hooktests.verbosehook
1197 calling hook pre-identify: hooktests.verbosehook
1189 verbose output from hook
1198 verbose output from hook
1190 cb9a9f314b8b
1199 cb9a9f314b8b
1191
1200
1192 Ensure hooks can be prioritized
1201 Ensure hooks can be prioritized
1193
1202
1194 $ echo '[hooks]' > .hg/hgrc
1203 $ echo '[hooks]' > .hg/hgrc
1195 $ echo 'pre-identify.a = python:hooktests.verbosehook' >> .hg/hgrc
1204 $ echo 'pre-identify.a = python:hooktests.verbosehook' >> .hg/hgrc
1196 $ echo 'pre-identify.b = python:hooktests.verbosehook' >> .hg/hgrc
1205 $ echo 'pre-identify.b = python:hooktests.verbosehook' >> .hg/hgrc
1197 $ echo 'priority.pre-identify.b = 1' >> .hg/hgrc
1206 $ echo 'priority.pre-identify.b = 1' >> .hg/hgrc
1198 $ echo 'pre-identify.c = python:hooktests.verbosehook' >> .hg/hgrc
1207 $ echo 'pre-identify.c = python:hooktests.verbosehook' >> .hg/hgrc
1199 $ hg id --verbose
1208 $ hg id --verbose
1200 calling hook pre-identify.b: hooktests.verbosehook
1209 calling hook pre-identify.b: hooktests.verbosehook
1201 verbose output from hook
1210 verbose output from hook
1202 calling hook pre-identify.a: hooktests.verbosehook
1211 calling hook pre-identify.a: hooktests.verbosehook
1203 verbose output from hook
1212 verbose output from hook
1204 calling hook pre-identify.c: hooktests.verbosehook
1213 calling hook pre-identify.c: hooktests.verbosehook
1205 verbose output from hook
1214 verbose output from hook
1206 cb9a9f314b8b
1215 cb9a9f314b8b
1207
1216
1208 new tags must be visible in pretxncommit (issue3210)
1217 new tags must be visible in pretxncommit (issue3210)
1209
1218
1210 $ echo 'pretxncommit.printtags = python:hooktests.printtags' >> .hg/hgrc
1219 $ echo 'pretxncommit.printtags = python:hooktests.printtags' >> .hg/hgrc
1211 $ hg tag -f foo
1220 $ hg tag -f foo
1212 [a, foo, tip]
1221 [a, foo, tip]
1213
1222
1214 post-init hooks must not crash (issue4983)
1223 post-init hooks must not crash (issue4983)
1215 This also creates the `to` repo for the next test block.
1224 This also creates the `to` repo for the next test block.
1216
1225
1217 $ cd ..
1226 $ cd ..
1218 $ cat << EOF >> hgrc-with-post-init-hook
1227 $ cat << EOF >> hgrc-with-post-init-hook
1219 > [hooks]
1228 > [hooks]
1220 > post-init = sh -c "printenv.py --line post-init"
1229 > post-init = sh -c "printenv.py --line post-init"
1221 > EOF
1230 > EOF
1222 $ HGRCPATH=hgrc-with-post-init-hook hg init to
1231 $ HGRCPATH=hgrc-with-post-init-hook hg init to
1223 post-init hook: HG_ARGS=init to
1232 post-init hook: HG_ARGS=init to
1224 HG_HOOKNAME=post-init
1233 HG_HOOKNAME=post-init
1225 HG_HOOKTYPE=post-init
1234 HG_HOOKTYPE=post-init
1226 HG_OPTS={'insecure': None, 'remotecmd': '', 'ssh': ''}
1235 HG_OPTS={'insecure': None, 'remotecmd': '', 'ssh': ''}
1227 HG_PATS=['to']
1236 HG_PATS=['to']
1228 HG_RESULT=0
1237 HG_RESULT=0
1229
1238
1230
1239
1231 new commits must be visible in pretxnchangegroup (issue3428)
1240 new commits must be visible in pretxnchangegroup (issue3428)
1232
1241
1233 $ echo '[hooks]' >> to/.hg/hgrc
1242 $ echo '[hooks]' >> to/.hg/hgrc
1234 $ echo 'prechangegroup = hg --traceback tip' >> to/.hg/hgrc
1243 $ echo 'prechangegroup = hg --traceback tip' >> to/.hg/hgrc
1235 $ echo 'pretxnchangegroup = hg --traceback tip' >> to/.hg/hgrc
1244 $ echo 'pretxnchangegroup = hg --traceback tip' >> to/.hg/hgrc
1236 $ echo a >> to/a
1245 $ echo a >> to/a
1237 $ hg --cwd to ci -Ama
1246 $ hg --cwd to ci -Ama
1238 adding a
1247 adding a
1239 $ hg clone to from
1248 $ hg clone to from
1240 updating to branch default
1249 updating to branch default
1241 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1250 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1242 $ echo aa >> from/a
1251 $ echo aa >> from/a
1243 $ hg --cwd from ci -mb
1252 $ hg --cwd from ci -mb
1244 $ hg --cwd from push
1253 $ hg --cwd from push
1245 pushing to $TESTTMP/to
1254 pushing to $TESTTMP/to
1246 searching for changes
1255 searching for changes
1247 changeset: 0:cb9a9f314b8b
1256 changeset: 0:cb9a9f314b8b
1248 tag: tip
1257 tag: tip
1249 user: test
1258 user: test
1250 date: Thu Jan 01 00:00:00 1970 +0000
1259 date: Thu Jan 01 00:00:00 1970 +0000
1251 summary: a
1260 summary: a
1252
1261
1253 adding changesets
1262 adding changesets
1254 adding manifests
1263 adding manifests
1255 adding file changes
1264 adding file changes
1256 changeset: 1:9836a07b9b9d
1265 changeset: 1:9836a07b9b9d
1257 tag: tip
1266 tag: tip
1258 user: test
1267 user: test
1259 date: Thu Jan 01 00:00:00 1970 +0000
1268 date: Thu Jan 01 00:00:00 1970 +0000
1260 summary: b
1269 summary: b
1261
1270
1262 added 1 changesets with 1 changes to 1 files
1271 added 1 changesets with 1 changes to 1 files
1263
1272
1264 pretxnclose hook failure should abort the transaction
1273 pretxnclose hook failure should abort the transaction
1265
1274
1266 $ hg init txnfailure
1275 $ hg init txnfailure
1267 $ cd txnfailure
1276 $ cd txnfailure
1268 $ touch a && hg commit -Aqm a
1277 $ touch a && hg commit -Aqm a
1269 $ cat >> .hg/hgrc <<EOF
1278 $ cat >> .hg/hgrc <<EOF
1270 > [hooks]
1279 > [hooks]
1271 > pretxnclose.error = exit 1
1280 > pretxnclose.error = exit 1
1272 > EOF
1281 > EOF
1273 $ hg strip -r 0 --config extensions.strip=
1282 $ hg strip -r 0 --config extensions.strip=
1274 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1283 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1275 saved backup bundle to * (glob)
1284 saved backup bundle to * (glob)
1276 transaction abort!
1285 transaction abort!
1277 rollback completed
1286 rollback completed
1278 strip failed, backup bundle stored in * (glob)
1287 strip failed, backup bundle stored in * (glob)
1279 abort: pretxnclose.error hook exited with status 1
1288 abort: pretxnclose.error hook exited with status 1
1280 [40]
1289 [40]
1281 $ hg recover
1290 $ hg recover
1282 no interrupted transaction available
1291 no interrupted transaction available
1283 [1]
1292 [1]
1284 $ cd ..
1293 $ cd ..
1285
1294
1286 check whether HG_PENDING makes pending changes only in related
1295 check whether HG_PENDING makes pending changes only in related
1287 repositories visible to an external hook.
1296 repositories visible to an external hook.
1288
1297
1289 (emulate a transaction running concurrently by copied
1298 (emulate a transaction running concurrently by copied
1290 .hg/store/00changelog.i.a in subsequent test)
1299 .hg/store/00changelog.i.a in subsequent test)
1291
1300
1292 $ cat > $TESTTMP/savepending.sh <<EOF
1301 $ cat > $TESTTMP/savepending.sh <<EOF
1293 > cp .hg/store/00changelog.i.a .hg/store/00changelog.i.a.saved
1302 > cp .hg/store/00changelog.i.a .hg/store/00changelog.i.a.saved
1294 > exit 1 # to avoid adding new revision for subsequent tests
1303 > exit 1 # to avoid adding new revision for subsequent tests
1295 > EOF
1304 > EOF
1296 $ cd a
1305 $ cd a
1297 $ hg tip -q
1306 $ hg tip -q
1298 4:539e4b31b6dc
1307 4:539e4b31b6dc
1299 $ hg --config hooks.pretxnclose="sh $TESTTMP/savepending.sh" commit -m "invisible"
1308 $ hg --config hooks.pretxnclose="sh $TESTTMP/savepending.sh" commit -m "invisible"
1300 transaction abort!
1309 transaction abort!
1301 rollback completed
1310 rollback completed
1302 abort: pretxnclose hook exited with status 1
1311 abort: pretxnclose hook exited with status 1
1303 [40]
1312 [40]
1304 $ cp .hg/store/00changelog.i.a.saved .hg/store/00changelog.i.a
1313 $ cp .hg/store/00changelog.i.a.saved .hg/store/00changelog.i.a
1305
1314
1306 (check (in)visibility of new changeset while transaction running in
1315 (check (in)visibility of new changeset while transaction running in
1307 repo)
1316 repo)
1308
1317
1309 $ cat > $TESTTMP/checkpending.sh <<EOF
1318 $ cat > $TESTTMP/checkpending.sh <<EOF
1310 > echo '@a'
1319 > echo '@a'
1311 > hg -R "$TESTTMP/a" tip -q
1320 > hg -R "$TESTTMP/a" tip -q
1312 > echo '@a/nested'
1321 > echo '@a/nested'
1313 > hg -R "$TESTTMP/a/nested" tip -q
1322 > hg -R "$TESTTMP/a/nested" tip -q
1314 > exit 1 # to avoid adding new revision for subsequent tests
1323 > exit 1 # to avoid adding new revision for subsequent tests
1315 > EOF
1324 > EOF
1316 $ hg init nested
1325 $ hg init nested
1317 $ cd nested
1326 $ cd nested
1318 $ echo a > a
1327 $ echo a > a
1319 $ hg add a
1328 $ hg add a
1320 $ hg --config hooks.pretxnclose="sh $TESTTMP/checkpending.sh" commit -m '#0'
1329 $ hg --config hooks.pretxnclose="sh $TESTTMP/checkpending.sh" commit -m '#0'
1321 @a
1330 @a
1322 4:539e4b31b6dc
1331 4:539e4b31b6dc
1323 @a/nested
1332 @a/nested
1324 0:bf5e395ced2c
1333 0:bf5e395ced2c
1325 transaction abort!
1334 transaction abort!
1326 rollback completed
1335 rollback completed
1327 abort: pretxnclose hook exited with status 1
1336 abort: pretxnclose hook exited with status 1
1328 [40]
1337 [40]
1329
1338
1330 Hook from untrusted hgrc are reported as failure
1339 Hook from untrusted hgrc are reported as failure
1331 ================================================
1340 ================================================
1332
1341
1333 $ cat << EOF > $TESTTMP/untrusted.py
1342 $ cat << EOF > $TESTTMP/untrusted.py
1334 > from mercurial import scmutil, util
1343 > from mercurial import scmutil, util
1335 > def uisetup(ui):
1344 > def uisetup(ui):
1336 > class untrustedui(ui.__class__):
1345 > class untrustedui(ui.__class__):
1337 > def _trusted(self, fp, f):
1346 > def _trusted(self, fp, f):
1338 > if util.normpath(fp.name).endswith(b'untrusted/.hg/hgrc'):
1347 > if util.normpath(fp.name).endswith(b'untrusted/.hg/hgrc'):
1339 > return False
1348 > return False
1340 > return super(untrustedui, self)._trusted(fp, f)
1349 > return super(untrustedui, self)._trusted(fp, f)
1341 > ui.__class__ = untrustedui
1350 > ui.__class__ = untrustedui
1342 > EOF
1351 > EOF
1343 $ cat << EOF >> $HGRCPATH
1352 $ cat << EOF >> $HGRCPATH
1344 > [extensions]
1353 > [extensions]
1345 > untrusted=$TESTTMP/untrusted.py
1354 > untrusted=$TESTTMP/untrusted.py
1346 > EOF
1355 > EOF
1347 $ hg init untrusted
1356 $ hg init untrusted
1348 $ cd untrusted
1357 $ cd untrusted
1349
1358
1350 Non-blocking hook
1359 Non-blocking hook
1351 -----------------
1360 -----------------
1352
1361
1353 $ cat << EOF >> .hg/hgrc
1362 $ cat << EOF >> .hg/hgrc
1354 > [hooks]
1363 > [hooks]
1355 > txnclose.testing=echo txnclose hook called
1364 > txnclose.testing=echo txnclose hook called
1356 > EOF
1365 > EOF
1357 $ touch a && hg commit -Aqm a
1366 $ touch a && hg commit -Aqm a
1358 warning: untrusted hook txnclose.testing not executed
1367 warning: untrusted hook txnclose.testing not executed
1359 $ hg log
1368 $ hg log
1360 changeset: 0:3903775176ed
1369 changeset: 0:3903775176ed
1361 tag: tip
1370 tag: tip
1362 user: test
1371 user: test
1363 date: Thu Jan 01 00:00:00 1970 +0000
1372 date: Thu Jan 01 00:00:00 1970 +0000
1364 summary: a
1373 summary: a
1365
1374
1366
1375
1367 Non-blocking hook
1376 Non-blocking hook
1368 -----------------
1377 -----------------
1369
1378
1370 $ cat << EOF >> .hg/hgrc
1379 $ cat << EOF >> .hg/hgrc
1371 > [hooks]
1380 > [hooks]
1372 > pretxnclose.testing=echo pre-txnclose hook called
1381 > pretxnclose.testing=echo pre-txnclose hook called
1373 > EOF
1382 > EOF
1374 $ touch b && hg commit -Aqm a
1383 $ touch b && hg commit -Aqm a
1375 transaction abort!
1384 transaction abort!
1376 rollback completed
1385 rollback completed
1377 abort: untrusted hook pretxnclose.testing not executed
1386 abort: untrusted hook pretxnclose.testing not executed
1378 (see 'hg help config.trusted')
1387 (see 'hg help config.trusted')
1379 [40]
1388 [40]
1380 $ hg log
1389 $ hg log
1381 changeset: 0:3903775176ed
1390 changeset: 0:3903775176ed
1382 tag: tip
1391 tag: tip
1383 user: test
1392 user: test
1384 date: Thu Jan 01 00:00:00 1970 +0000
1393 date: Thu Jan 01 00:00:00 1970 +0000
1385 summary: a
1394 summary: a
1386
1395
1387
1396
1388 unsetup the test
1397 unsetup the test
1389 ----------------
1398 ----------------
1390
1399
1391 # touch the file to unconfuse chg with a diffrent mtime
1400 # touch the file to unconfuse chg with a diffrent mtime
1392 $ sleep 1
1401 $ sleep 1
1393 $ touch $TESTTMP/untrusted.py
1402 $ touch $TESTTMP/untrusted.py
1394 $ cat << EOF >> $HGRCPATH
1403 $ cat << EOF >> $HGRCPATH
1395 > [extensions]
1404 > [extensions]
1396 > untrusted=!
1405 > untrusted=!
1397 > EOF
1406 > EOF
1398
1407
1399 HGPLAIN setting in hooks
1408 HGPLAIN setting in hooks
1400 ========================
1409 ========================
1401
1410
1402 $ cat << EOF >> .hg/hgrc
1411 $ cat << EOF >> .hg/hgrc
1403 > [hooks]
1412 > [hooks]
1404 > pre-version.testing-default=sh -c "echo '### default ###' plain: \${HGPLAIN:-'<unset>'}"
1413 > pre-version.testing-default=sh -c "echo '### default ###' plain: \${HGPLAIN:-'<unset>'}"
1405 > pre-version.testing-yes=sh -c "echo '### yes #######' plain: \${HGPLAIN:-'<unset>'}"
1414 > pre-version.testing-yes=sh -c "echo '### yes #######' plain: \${HGPLAIN:-'<unset>'}"
1406 > pre-version.testing-yes:run-with-plain=yes
1415 > pre-version.testing-yes:run-with-plain=yes
1407 > pre-version.testing-no=sh -c "echo '### no ########' plain: \${HGPLAIN:-'<unset>'}"
1416 > pre-version.testing-no=sh -c "echo '### no ########' plain: \${HGPLAIN:-'<unset>'}"
1408 > pre-version.testing-no:run-with-plain=no
1417 > pre-version.testing-no:run-with-plain=no
1409 > pre-version.testing-auto=sh -c "echo '### auto ######' plain: \${HGPLAIN:-'<unset>'}"
1418 > pre-version.testing-auto=sh -c "echo '### auto ######' plain: \${HGPLAIN:-'<unset>'}"
1410 > pre-version.testing-auto:run-with-plain=auto
1419 > pre-version.testing-auto:run-with-plain=auto
1411 > EOF
1420 > EOF
1412
1421
1413 $ (unset HGPLAIN; hg version --quiet)
1422 $ (unset HGPLAIN; hg version --quiet)
1414 ### default ### plain: 1
1423 ### default ### plain: 1
1415 ### yes ####### plain: 1
1424 ### yes ####### plain: 1
1416 ### no ######## plain: <unset>
1425 ### no ######## plain: <unset>
1417 ### auto ###### plain: <unset>
1426 ### auto ###### plain: <unset>
1418 Mercurial Distributed SCM (*) (glob)
1427 Mercurial Distributed SCM (*) (glob)
1419
1428
1420 $ HGPLAIN=1 hg version --quiet
1429 $ HGPLAIN=1 hg version --quiet
1421 ### default ### plain: 1
1430 ### default ### plain: 1
1422 ### yes ####### plain: 1
1431 ### yes ####### plain: 1
1423 ### no ######## plain: <unset>
1432 ### no ######## plain: <unset>
1424 ### auto ###### plain: 1
1433 ### auto ###### plain: 1
1425 Mercurial Distributed SCM (*) (glob)
1434 Mercurial Distributed SCM (*) (glob)
General Comments 0
You need to be logged in to leave comments. Login now