63 try: |
69 try: |
64 vfs.unlink(target) |
70 vfs.unlink(target) |
65 except (IOError, OSError) as inst: |
71 except (IOError, OSError) as inst: |
66 if inst.errno != errno.ENOENT: |
72 if inst.errno != errno.ENOENT: |
67 raise |
73 raise |
68 except (IOError, OSError, util.Abort) as inst: |
74 except (IOError, OSError, error.Abort) as inst: |
69 if not c: |
75 if not c: |
70 raise |
76 raise |
71 |
77 |
72 opener.unlink(journal) |
|
73 backuppath = "%s.backupfiles" % journal |
78 backuppath = "%s.backupfiles" % journal |
74 if opener.exists(backuppath): |
79 if opener.exists(backuppath): |
75 opener.unlink(backuppath) |
80 opener.unlink(backuppath) |
|
81 opener.unlink(journal) |
76 try: |
82 try: |
77 for f in backupfiles: |
83 for f in backupfiles: |
78 if opener.exists(f): |
84 if opener.exists(f): |
79 opener.unlink(f) |
85 opener.unlink(f) |
80 except (IOError, OSError, util.Abort) as inst: |
86 except (IOError, OSError, error.Abort) as inst: |
81 # only pure backup file remains, it is sage to ignore any error |
87 # only pure backup file remains, it is sage to ignore any error |
82 pass |
88 pass |
83 |
89 |
84 class transaction(object): |
90 class transaction(object): |
85 def __init__(self, report, opener, vfsmap, journalname, undoname=None, |
91 def __init__(self, report, opener, vfsmap, journalname, undoname=None, |
86 after=None, createmode=None, validator=None): |
92 after=None, createmode=None, validator=None, releasefn=None): |
87 """Begin a new transaction |
93 """Begin a new transaction |
88 |
94 |
89 Begins a new transaction that allows rolling back writes in the event of |
95 Begins a new transaction that allows rolling back writes in the event of |
90 an exception. |
96 an exception. |
91 |
97 |
92 * `after`: called after the transaction has been committed |
98 * `after`: called after the transaction has been committed |
93 * `createmode`: the mode of the journal file that will be created |
99 * `createmode`: the mode of the journal file that will be created |
|
100 * `releasefn`: called after releasing (with transaction and result) |
94 """ |
101 """ |
95 self.count = 1 |
102 self.count = 1 |
96 self.usages = 1 |
103 self.usages = 1 |
97 self.report = report |
104 self.report = report |
98 # a vfs to the store content |
105 # a vfs to the store content |
111 # should raise exception is anything is wrong. |
118 # should raise exception is anything is wrong. |
112 # target user is repository hooks. |
119 # target user is repository hooks. |
113 if validator is None: |
120 if validator is None: |
114 validator = lambda tr: None |
121 validator = lambda tr: None |
115 self.validator = validator |
122 self.validator = validator |
|
123 # A callback to do something just after releasing transaction. |
|
124 if releasefn is None: |
|
125 releasefn = lambda tr, success: None |
|
126 self.releasefn = releasefn |
|
127 |
116 # a dict of arguments to be passed to hooks |
128 # a dict of arguments to be passed to hooks |
117 self.hookargs = {} |
129 self.hookargs = {} |
118 self.file = opener.open(self.journal, "w") |
130 self.file = opener.open(self.journal, "w") |
119 |
131 |
120 # a list of ('location', 'path', 'backuppath', cache) entries. |
132 # a list of ('location', 'path', 'backuppath', cache) entries. |
396 self.file.close() |
408 self.file.close() |
397 self._backupsfile.close() |
409 self._backupsfile.close() |
398 # cleanup temporary files |
410 # cleanup temporary files |
399 for l, f, b, c in self._backupentries: |
411 for l, f, b, c in self._backupentries: |
400 if l not in self._vfsmap and c: |
412 if l not in self._vfsmap and c: |
401 self.report("couldn't remote %s: unknown cache location %s\n" |
413 self.report("couldn't remove %s: unknown cache location %s\n" |
402 % (b, l)) |
414 % (b, l)) |
403 continue |
415 continue |
404 vfs = self._vfsmap[l] |
416 vfs = self._vfsmap[l] |
405 if not f and b and vfs.exists(b): |
417 if not f and b and vfs.exists(b): |
406 try: |
418 try: |
407 vfs.unlink(b) |
419 vfs.unlink(b) |
408 except (IOError, OSError, util.Abort) as inst: |
420 except (IOError, OSError, error.Abort) as inst: |
409 if not c: |
421 if not c: |
410 raise |
422 raise |
411 # Abort may be raise by read only opener |
423 # Abort may be raise by read only opener |
412 self.report("couldn't remote %s: %s\n" |
424 self.report("couldn't remove %s: %s\n" |
413 % (vfs.join(b), inst)) |
425 % (vfs.join(b), inst)) |
414 self.entries = [] |
426 self.entries = [] |
415 self._writeundo() |
427 self._writeundo() |
416 if self.after: |
428 if self.after: |
417 self.after() |
429 self.after() |
|
430 if self.opener.isfile(self._backupjournal): |
|
431 self.opener.unlink(self._backupjournal) |
418 if self.opener.isfile(self.journal): |
432 if self.opener.isfile(self.journal): |
419 self.opener.unlink(self.journal) |
433 self.opener.unlink(self.journal) |
420 if self.opener.isfile(self._backupjournal): |
434 if True: |
421 self.opener.unlink(self._backupjournal) |
|
422 for l, _f, b, c in self._backupentries: |
435 for l, _f, b, c in self._backupentries: |
423 if l not in self._vfsmap and c: |
436 if l not in self._vfsmap and c: |
424 self.report("couldn't remote %s: unknown cache location" |
437 self.report("couldn't remove %s: unknown cache location" |
425 "%s\n" % (b, l)) |
438 "%s\n" % (b, l)) |
426 continue |
439 continue |
427 vfs = self._vfsmap[l] |
440 vfs = self._vfsmap[l] |
428 if b and vfs.exists(b): |
441 if b and vfs.exists(b): |
429 try: |
442 try: |
430 vfs.unlink(b) |
443 vfs.unlink(b) |
431 except (IOError, OSError, util.Abort) as inst: |
444 except (IOError, OSError, error.Abort) as inst: |
432 if not c: |
445 if not c: |
433 raise |
446 raise |
434 # Abort may be raise by read only opener |
447 # Abort may be raise by read only opener |
435 self.report("couldn't remote %s: %s\n" |
448 self.report("couldn't remove %s: %s\n" |
436 % (vfs.join(b), inst)) |
449 % (vfs.join(b), inst)) |
437 self._backupentries = [] |
450 self._backupentries = [] |
438 self.journal = None |
451 self.journal = None |
|
452 |
|
453 self.releasefn(self, True) # notify success of closing transaction |
|
454 |
439 # run post close action |
455 # run post close action |
440 categories = sorted(self._postclosecallback) |
456 categories = sorted(self._postclosecallback) |
441 for cat in categories: |
457 for cat in categories: |
442 self._postclosecallback[cat](self) |
458 self._postclosecallback[cat](self) |
443 |
459 |
498 self.report(_("rollback completed\n")) |
514 self.report(_("rollback completed\n")) |
499 except BaseException: |
515 except BaseException: |
500 self.report(_("rollback failed - please run hg recover\n")) |
516 self.report(_("rollback failed - please run hg recover\n")) |
501 finally: |
517 finally: |
502 self.journal = None |
518 self.journal = None |
503 |
519 self.releasefn(self, False) # notify failure of transaction |
504 |
520 |
505 def rollback(opener, vfsmap, file, report): |
521 def rollback(opener, vfsmap, file, report): |
506 """Rolls back the transaction contained in the given file |
522 """Rolls back the transaction contained in the given file |
507 |
523 |
508 Reads the entries in the specified file, and the corresponding |
524 Reads the entries in the specified file, and the corresponding |