mercurial/transaction.py
branchstable
changeset 26813 b66e3ca0b90c
parent 26754 e7e1528cf200
child 27662 f7ab50c721ac
equal deleted inserted replaced
26535:d3712209921d 26813:b66e3ca0b90c
     9 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
     9 # Copyright 2005, 2006 Matt Mackall <mpm@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 from i18n import _
    14 from __future__ import absolute_import
       
    15 
    15 import errno
    16 import errno
    16 import error, util
    17 
       
    18 from .i18n import _
       
    19 from . import (
       
    20     error,
       
    21     util,
       
    22 )
    17 
    23 
    18 version = 2
    24 version = 2
    19 
    25 
    20 def active(func):
    26 def active(func):
    21     def _active(self, *args, **kwds):
    27     def _active(self, *args, **kwds):
    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 
   459                 continue
   475                 continue
   460             if not b:
   476             if not b:
   461                 u = ''
   477                 u = ''
   462             else:
   478             else:
   463                 if l not in self._vfsmap and c:
   479                 if l not in self._vfsmap and c:
   464                     self.report("couldn't remote %s: unknown cache location"
   480                     self.report("couldn't remove %s: unknown cache location"
   465                                 "%s\n" % (b, l))
   481                                 "%s\n" % (b, l))
   466                     continue
   482                     continue
   467                 vfs = self._vfsmap[l]
   483                 vfs = self._vfsmap[l]
   468                 base, name = vfs.split(b)
   484                 base, name = vfs.split(b)
   469                 assert name.startswith(self.journal), name
   485                 assert name.startswith(self.journal), name
   480         self.file.close()
   496         self.file.close()
   481         self._backupsfile.close()
   497         self._backupsfile.close()
   482 
   498 
   483         try:
   499         try:
   484             if not self.entries and not self._backupentries:
   500             if not self.entries and not self._backupentries:
       
   501                 if self._backupjournal:
       
   502                     self.opener.unlink(self._backupjournal)
   485                 if self.journal:
   503                 if self.journal:
   486                     self.opener.unlink(self.journal)
   504                     self.opener.unlink(self.journal)
   487                 if self._backupjournal:
       
   488                     self.opener.unlink(self._backupjournal)
       
   489                 return
   505                 return
   490 
   506 
   491             self.report(_("transaction abort!\n"))
   507             self.report(_("transaction abort!\n"))
   492 
   508 
   493             try:
   509             try:
   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