##// END OF EJS Templates
merge with stable
merge with stable

File last commit:

r24283:ef22cfff default
r24415:1cfded2f merge default
Show More
transaction.py
547 lines | 19.5 KiB | text/x-python | PythonLexer
Mads Kiilerich
fix trivial spelling errors
r17424 # transaction.py - simple journaling scheme for mercurial
mpm@selenic.com
Add back links from file revisions to changeset revisions...
r0 #
# This transaction scheme is intended to gracefully handle program
# errors and interruptions. More serious failures like system crashes
# can be recovered with an fsck-like tool. As the whole repository is
# effectively log-structured, this should amount to simply truncating
# anything that isn't referenced in the changelog.
#
Vadim Gelfer
update copyrights.
r2859 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
mpm@selenic.com
Add back links from file revisions to changeset revisions...
r0 #
Martin Geisler
updated license to be explicit about GPL version 2
r8225 # This software may be used and distributed according to the terms of the
Matt Mackall
Update license to GPLv2+
r10263 # GNU General Public License version 2 or any later version.
mpm@selenic.com
Add back links from file revisions to changeset revisions...
r0
Matt Mackall
Simplify i18n imports
r3891 from i18n import _
Matt Mackall
transaction: drop extra import caught by pyflakes
r20886 import errno
Durham Goode
transaction: add support for non-append files...
r20882 import error, util
Henrik Stuart
transaction: ensure finished transactions are not reused...
r8289
Pierre-Yves David
transaction: set backupentries version to proper value...
r23313 version = 2
Durham Goode
transactions: add version number to journal.backupfiles...
r23064
Henrik Stuart
transaction: ensure finished transactions are not reused...
r8289 def active(func):
def _active(self, *args, **kwds):
if self.count == 0:
raise error.Abort(_(
'cannot use transaction when it is already committed/aborted'))
return func(self, *args, **kwds)
return _active
mpm@selenic.com
Add back links from file revisions to changeset revisions...
r0
Pierre-Yves David
transaction: use the location value when doing backup...
r23311 def _playback(journal, report, opener, vfsmap, entries, backupentries,
unlink=True):
Mads Kiilerich
cleanup: name unused variables using convention of leading _...
r22204 for f, o, _ignore in entries:
Henrik Stuart
transaction: refactor transaction.abort and rollback to use the same code...
r8294 if o or not unlink:
try:
Dan Villiom Podlaski Christiansen
explicitly close files...
r13400 fp = opener(f, 'a')
fp.truncate(o)
fp.close()
Benoit Boissinot
transaction: more specific exceptions, os.unlink can raise OSError
r9686 except IOError:
Henrik Stuart
transaction: refactor transaction.abort and rollback to use the same code...
r8294 report(_("failed to truncate %s\n") % f)
raise
else:
try:
FUJIWARA Katsunori
transaction: unlink target file via vfs...
r20084 opener.unlink(f)
Benoit Boissinot
transaction: more specific exceptions, os.unlink can raise OSError
r9686 except (IOError, OSError), inst:
Henrik Stuart
transaction: refactor transaction.abort and rollback to use the same code...
r8294 if inst.errno != errno.ENOENT:
raise
Durham Goode
transaction: add support for non-append files...
r20882
backupfiles = []
Pierre-Yves David
transaction: change the on disk format for backupentries...
r23309 for l, f, b, c in backupentries:
Pierre-Yves David
transaction: support cache file in backupentries...
r23312 if l not in vfsmap and c:
report("couldn't handle %s: unknown cache location %s\n"
% (b, l))
Pierre-Yves David
transaction: use the location value when doing backup...
r23311 vfs = vfsmap[l]
Pierre-Yves David
transaction: support cache file in backupentries...
r23312 try:
if f and b:
filepath = vfs.join(f)
backuppath = vfs.join(b)
try:
util.copyfile(backuppath, filepath)
backupfiles.append(b)
except IOError:
report(_("failed to recover %s\n") % f)
else:
target = f or b
try:
vfs.unlink(target)
except (IOError, OSError), inst:
if inst.errno != errno.ENOENT:
raise
except (IOError, OSError, util.Abort), inst:
if not c:
Pierre-Yves David
transaction: handle missing file in backupentries (instead of using entries)...
r23278 raise
Durham Goode
transaction: add support for non-append files...
r20882
FUJIWARA Katsunori
transaction: take journal file path relative to vfs to use file API via vfs
r20087 opener.unlink(journal)
Durham Goode
transaction: add support for non-append files...
r20882 backuppath = "%s.backupfiles" % journal
if opener.exists(backuppath):
opener.unlink(backuppath)
Pierre-Yves David
transaction: support cache file in backupentries...
r23312 try:
for f in backupfiles:
if opener.exists(f):
opener.unlink(f)
except (IOError, OSError, util.Abort), inst:
# only pure backup file remains, it is sage to ignore any error
pass
Henrik Stuart
transaction: refactor transaction.abort and rollback to use the same code...
r8294
Eric Hopper
Convert all classes to new-style classes by deriving them from object.
r1559 class transaction(object):
Pierre-Yves David
transaction: pass the name of the "undo" journal to the transaction...
r23903 def __init__(self, report, opener, vfsmap, journalname, undoname=None,
Pierre-Yves David
transaction: add a validation stage...
r24283 after=None, createmode=None, validator=None):
Durham Goode
transaction: add onclose/onabort hook for pre-close logic...
r20881 """Begin a new transaction
Begins a new transaction that allows rolling back writes in the event of
an exception.
* `after`: called after the transaction has been committed
* `createmode`: the mode of the journal file that will be created
"""
mason@suse.com
Automatic nesting into running transactions in the same repository....
r1806 self.count = 1
Ronny Pfannschmidt
make transactions work on non-refcounted python implementations
r11230 self.usages = 1
mpm@selenic.com
Remove all remaining print statements...
r582 self.report = report
Pierre-Yves David
transaction: pass a vfs map to the transaction...
r23310 # a vfs to the store content
mpm@selenic.com
Add back links from file revisions to changeset revisions...
r0 self.opener = opener
Pierre-Yves David
transaction: pass a vfs map to the transaction...
r23310 # a map to access file in various {location -> vfs}
vfsmap = vfsmap.copy()
vfsmap[''] = opener # set default value
self._vfsmap = vfsmap
mpm@selenic.com
Beginnings of transaction undo support
r95 self.after = after
mpm@selenic.com
Add back links from file revisions to changeset revisions...
r0 self.entries = []
Pierre-Yves David
transaction: mark backup-related attributes private...
r23249 self.map = {}
Pierre-Yves David
transaction: clarify the name of 'journal' argument for transaction...
r23901 self.journal = journalname
Pierre-Yves David
transaction: pass the name of the "undo" journal to the transaction...
r23903 self.undoname = undoname
Pierre-Yves David
transaction: gather backupjournal logic together in the __init__...
r23279 self._queue = []
Pierre-Yves David
transaction: add a validation stage...
r24283 # A callback to validate transaction content before closing it.
# should raise exception is anything is wrong.
# target user is repository hooks.
if validator is None:
validator = lambda tr: None
self.validator = validator
Pierre-Yves David
transaction: gather backupjournal logic together in the __init__...
r23279 # a dict of arguments to be passed to hooks
self.hookargs = {}
self.file = opener.open(self.journal, "w")
Pierre-Yves David
transaction: change the on disk format for backupentries...
r23309 # a list of ('location', 'path', 'backuppath', cache) entries.
Pierre-Yves David
transaction: use the location value when doing backup...
r23311 # - if 'backuppath' is empty, no file existed at backup time
# - if 'path' is empty, this is a temporary transaction file
# - if 'location' is not empty, the path is outside main opener reach.
# use 'location' value as a key in a vfsmap to find the right 'vfs'
# (cache is currently unused)
Pierre-Yves David
transaction: mark backup-related attributes private...
r23249 self._backupentries = []
self._backupmap = {}
Pierre-Yves David
transaction: clarify the name of 'journal' argument for transaction...
r23901 self._backupjournal = "%s.backupfiles" % self.journal
Pierre-Yves David
transaction: mark backup-related attributes private...
r23249 self._backupsfile = opener.open(self._backupjournal, 'w')
self._backupsfile.write('%d\n' % version)
Pierre-Yves David
transaction: gather backupjournal logic together in the __init__...
r23279
Alexis S. L. Carvalho
make the journal/undo files from transactions inherit the mode from .hg/store
r6065 if createmode is not None:
FUJIWARA Katsunori
transaction: take journal file path relative to vfs to use file API via vfs
r20087 opener.chmod(self.journal, createmode & 0666)
Pierre-Yves David
transaction: mark backup-related attributes private...
r23249 opener.chmod(self._backupjournal, createmode & 0666)
mpm@selenic.com
Add back links from file revisions to changeset revisions...
r0
Pierre-Yves David
transaction: add a file generation mechanism...
r22078 # hold file generations to be performed on commit
self._filegenerators = {}
Mads Kiilerich
spelling: fixes from proofreading of spell checker issues
r23543 # hold callback to write pending data for hooks
Pierre-Yves David
transaction: add 'writepending' logic...
r23202 self._pendingcallback = {}
# True is any pending data have been written ever
self._anypending = False
Pierre-Yves David
transaction: allow registering a finalization callback...
r23204 # holds callback to call when writing the transaction
self._finalizecallback = {}
Mads Kiilerich
spelling: fixes from proofreading of spell checker issues
r23543 # hold callback for post transaction close
Pierre-Yves David
transaction: allow registering a post-close callback...
r23220 self._postclosecallback = {}
Gregory Szorc
transaction: support for callbacks during abort...
r23764 # holds callbacks to call during abort
self._abortcallback = {}
Pierre-Yves David
transaction: add a file generation mechanism...
r22078
mpm@selenic.com
Add back links from file revisions to changeset revisions...
r0 def __del__(self):
mpm@selenic.com
transaction: __del__ should do nothing if the journal already exists...
r558 if self.journal:
Sune Foldager
transaction: always remove empty journal on abort...
r9693 self._abort()
mpm@selenic.com
Add back links from file revisions to changeset revisions...
r0
Henrik Stuart
transaction: ensure finished transactions are not reused...
r8289 @active
Henrik Stuart
transaction: add atomic groups to transaction logic...
r8363 def startgroup(self):
Pierre-Yves David
transaction: document startgroup and endgroup...
r23250 """delay registration of file entry
This is used by strip to delay vision of strip offset. The transaction
sees either none or all of the strip actions to be done."""
Pierre-Yves David
transaction: drop backupentries logic from startgroup and endgroup...
r23251 self._queue.append([])
Henrik Stuart
transaction: add atomic groups to transaction logic...
r8363
@active
def endgroup(self):
Pierre-Yves David
transaction: document startgroup and endgroup...
r23250 """apply delayed registration of file entry.
This is used by strip to delay vision of strip offset. The transaction
sees either none or all of the strip actions to be done."""
Henrik Stuart
transaction: add atomic groups to transaction logic...
r8363 q = self._queue.pop()
Pierre-Yves David
transaction: factorise append-only file registration...
r23253 for f, o, data in q:
self._addentry(f, o, data)
Henrik Stuart
transaction: add atomic groups to transaction logic...
r8363
@active
Chris Mason
...
r2084 def add(self, file, offset, data=None):
Pierre-Yves David
transaction: document `tr.add`
r23252 """record the state of an append-only file before update"""
Pierre-Yves David
transaction: mark backup-related attributes private...
r23249 if file in self.map or file in self._backupmap:
Matt Mackall
many, many trivial check-code fixups
r10282 return
Henrik Stuart
transaction: add atomic groups to transaction logic...
r8363 if self._queue:
Pierre-Yves David
transaction: drop backupentries logic from startgroup and endgroup...
r23251 self._queue[-1].append((file, offset, data))
Henrik Stuart
transaction: add atomic groups to transaction logic...
r8363 return
Pierre-Yves David
transaction: factorise append-only file registration...
r23253 self._addentry(file, offset, data)
def _addentry(self, file, offset, data):
"""add a append-only entry to memory and on-disk state"""
if file in self.map or file in self._backupmap:
return
Chris Mason
...
r2084 self.entries.append((file, offset, data))
self.map[file] = len(self.entries) - 1
mpm@selenic.com
Add back links from file revisions to changeset revisions...
r0 # add enough data to the journal to do the truncate
self.file.write("%s\0%d\n" % (file, offset))
self.file.flush()
Henrik Stuart
transaction: ensure finished transactions are not reused...
r8289 @active
Pierre-Yves David
transaction: use 'location' instead of 'vfs' in the addbackup method...
r23316 def addbackup(self, file, hardlink=True, location=''):
Durham Goode
transaction: add support for non-append files...
r20882 """Adds a backup of the file to the transaction
Calling addbackup() creates a hardlink backup of the specified file
that is used to recover the file in the event of the transaction
aborting.
* `file`: the file path, relative to .hg/store
* `hardlink`: use a hardlink to quickly create the backup
"""
Pierre-Yves David
transaction: drop backupentries logic from startgroup and endgroup...
r23251 if self._queue:
msg = 'cannot use transaction.addbackup inside "group"'
raise RuntimeError(msg)
Durham Goode
transaction: add support for non-append files...
r20882
Pierre-Yves David
transaction: mark backup-related attributes private...
r23249 if file in self.map or file in self._backupmap:
Durham Goode
transaction: add support for non-append files...
r20882 return
Pierre-Yves David
vfs: add a 'split' method...
r23582 vfs = self._vfsmap[location]
dirname, filename = vfs.split(file)
Pierre-Yves David
addbackup: handle file in subdirectory...
r23315 backupfilename = "%s.backup.%s" % (self.journal, filename)
Pierre-Yves David
vfs: add a 'reljoin' function for joining relative paths...
r23581 backupfile = vfs.reljoin(dirname, backupfilename)
Pierre-Yves David
transaction: allow generating file outside of store...
r22663 if vfs.exists(file):
filepath = vfs.join(file)
Pierre-Yves David
addbackup: use the vfs for the backup destination too...
r23314 backuppath = vfs.join(backupfile)
Pierre-Yves David
transaction: use 'util.copyfile' for creating backup...
r23900 util.copyfile(filepath, backuppath, hardlink=hardlink)
Durham Goode
transaction: add support for non-append files...
r20882 else:
Pierre-Yves David
transaction: handle missing file in backupentries (instead of using entries)...
r23278 backupfile = ''
Durham Goode
transaction: add support for non-append files...
r20882
Pierre-Yves David
transaction: use 'location' instead of 'vfs' in the addbackup method...
r23316 self._addbackupentry((location, file, backupfile, False))
Pierre-Yves David
transaction: extract backupentry registration in a dedicated function...
r23283
def _addbackupentry(self, entry):
"""register a new backup entry and write it to disk"""
self._backupentries.append(entry)
Pierre-Yves David
transaction: mark backup-related attributes private...
r23249 self._backupmap[file] = len(self._backupentries) - 1
Pierre-Yves David
transaction: change the on disk format for backupentries...
r23309 self._backupsfile.write("%s\0%s\0%s\0%d\n" % entry)
Pierre-Yves David
transaction: mark backup-related attributes private...
r23249 self._backupsfile.flush()
Durham Goode
transaction: add support for non-append files...
r20882
@active
Pierre-Yves David
transaction: accept a 'location' argument for registertmp...
r23354 def registertmp(self, tmpfile, location=''):
Pierre-Yves David
transaction: allow registering a temporary transaction file...
r23291 """register a temporary transaction file
Matt Mackall
transaction: fix some docstring grammar
r23355 Such files will be deleted when the transaction exits (on both
failure and success).
Pierre-Yves David
transaction: allow registering a temporary transaction file...
r23291 """
Pierre-Yves David
transaction: accept a 'location' argument for registertmp...
r23354 self._addbackupentry((location, '', tmpfile, False))
Pierre-Yves David
transaction: allow registering a temporary transaction file...
r23291
@active
Pierre-Yves David
transaction: use 'location' instead of 'vfs' objects for file generation...
r23317 def addfilegenerator(self, genid, filenames, genfunc, order=0,
location=''):
Pierre-Yves David
transaction: add a file generation mechanism...
r22078 """add a function to generates some files at transaction commit
The `genfunc` argument is a function capable of generating proper
content of each entry in the `filename` tuple.
At transaction close time, `genfunc` will be called with one file
object argument per entries in `filenames`.
The transaction itself is responsible for the backup, creation and
final write of such file.
The `genid` argument is used to ensure the same set of file is only
generated once. Call to `addfilegenerator` for a `genid` already
present will overwrite the old entry.
The `order` argument may be used to control the order in which multiple
generator will be executed.
Pierre-Yves David
transaction: use 'location' instead of 'vfs' objects for file generation...
r23317
The `location` arguments may be used to indicate the files are located
outside of the the standard directory for transaction. It should match
Mads Kiilerich
spelling: fixes from proofreading of spell checker issues
r23543 one of the key of the `transaction.vfsmap` dictionary.
Pierre-Yves David
transaction: add a file generation mechanism...
r22078 """
Pierre-Yves David
transaction: allow generating file outside of store...
r22663 # For now, we are unable to do proper backup and restore of custom vfs
# but for bookmarks that are handled outside this mechanism.
Pierre-Yves David
transaction: use 'location' instead of 'vfs' objects for file generation...
r23317 self._filegenerators[genid] = (order, filenames, genfunc, location)
Pierre-Yves David
transaction: add a file generation mechanism...
r22078
Pierre-Yves David
transaction: allow generating files with a suffix...
r23356 def _generatefiles(self, suffix=''):
Pierre-Yves David
transaction: extract file generation into its own function...
r23102 # write files registered for generation
Pierre-Yves David
transaction: have _generatefile return a boolean...
r23357 any = False
Pierre-Yves David
transaction: extract file generation into its own function...
r23102 for entry in sorted(self._filegenerators.values()):
Pierre-Yves David
transaction: have _generatefile return a boolean...
r23357 any = True
Pierre-Yves David
transaction: use 'location' instead of 'vfs' objects for file generation...
r23317 order, filenames, genfunc, location = entry
vfs = self._vfsmap[location]
Pierre-Yves David
transaction: extract file generation into its own function...
r23102 files = []
try:
for name in filenames:
Pierre-Yves David
transaction: allow generating files with a suffix...
r23356 name += suffix
if suffix:
self.registertmp(name, location=location)
else:
self.addbackup(name, location=location)
Pierre-Yves David
transaction: extract file generation into its own function...
r23102 files.append(vfs(name, 'w', atomictemp=True))
genfunc(*files)
finally:
for f in files:
f.close()
Pierre-Yves David
transaction: have _generatefile return a boolean...
r23357 return any
Pierre-Yves David
transaction: extract file generation into its own function...
r23102
Pierre-Yves David
transaction: add a file generation mechanism...
r22078 @active
Chris Mason
...
r2084 def find(self, file):
if file in self.map:
return self.entries[self.map[file]]
Pierre-Yves David
transaction: mark backup-related attributes private...
r23249 if file in self._backupmap:
return self._backupentries[self._backupmap[file]]
Chris Mason
...
r2084 return None
Henrik Stuart
transaction: ensure finished transactions are not reused...
r8289 @active
Chris Mason
...
r2084 def replace(self, file, offset, data=None):
Henrik Stuart
transaction: add atomic groups to transaction logic...
r8363 '''
replace can only replace already committed entries
that are not pending in the queue
'''
Chris Mason
...
r2084 if file not in self.map:
raise KeyError(file)
index = self.map[file]
self.entries[index] = (file, offset, data)
self.file.write("%s\0%d\n" % (file, offset))
self.file.flush()
Henrik Stuart
transaction: ensure finished transactions are not reused...
r8289 @active
mason@suse.com
Automatic nesting into running transactions in the same repository....
r1806 def nest(self):
self.count += 1
Ronny Pfannschmidt
make transactions work on non-refcounted python implementations
r11230 self.usages += 1
mason@suse.com
Automatic nesting into running transactions in the same repository....
r1806 return self
Ronny Pfannschmidt
make transactions work on non-refcounted python implementations
r11230 def release(self):
if self.count > 0:
self.usages -= 1
Patrick Mezard
cleanup: typos
r11685 # if the transaction scopes are left without being closed, fail
Ronny Pfannschmidt
make transactions work on non-refcounted python implementations
r11230 if self.count > 0 and self.usages == 0:
self._abort()
mason@suse.com
Automatic nesting into running transactions in the same repository....
r1806 def running(self):
return self.count > 0
Pierre-Yves David
transaction: add 'writepending' logic...
r23202 def addpending(self, category, callback):
"""add a callback to be called when the transaction is pending
Pierre-Yves David
transaction: pass the transaction to 'pending' callback...
r23280 The transaction will be given as callback's first argument.
Pierre-Yves David
transaction: add 'writepending' logic...
r23202 Category is a unique identifier to allow overwriting an old callback
with a newer callback.
"""
self._pendingcallback[category] = callback
@active
def writepending(self):
'''write pending file to temporary version
This is used to allow hooks to view a transaction before commit'''
categories = sorted(self._pendingcallback)
for cat in categories:
# remove callback since the data will have been flushed
Pierre-Yves David
transaction: pass the transaction to 'pending' callback...
r23280 any = self._pendingcallback.pop(cat)(self)
Pierre-Yves David
transaction: add 'writepending' logic...
r23202 self._anypending = self._anypending or any
Pierre-Yves David
transaction: write pending generated files...
r23358 self._anypending |= self._generatefiles(suffix='.pending')
Pierre-Yves David
transaction: add 'writepending' logic...
r23202 return self._anypending
Henrik Stuart
transaction: ensure finished transactions are not reused...
r8289 @active
Pierre-Yves David
transaction: allow registering a finalization callback...
r23204 def addfinalize(self, category, callback):
"""add a callback to be called when the transaction is closed
Pierre-Yves David
transaction: pass the transaction to 'finalize' callback...
r23281 The transaction will be given as callback's first argument.
Pierre-Yves David
transaction: allow registering a finalization callback...
r23204 Category is a unique identifier to allow overwriting old callbacks with
newer callbacks.
"""
self._finalizecallback[category] = callback
@active
Pierre-Yves David
transaction: allow registering a post-close callback...
r23220 def addpostclose(self, category, callback):
"""add a callback to be called after the transaction is closed
Pierre-Yves David
transaction: pass the transaction to 'postclose' callback...
r23282 The transaction will be given as callback's first argument.
Pierre-Yves David
transaction: allow registering a post-close callback...
r23220 Category is a unique identifier to allow overwriting an old callback
with a newer callback.
"""
self._postclosecallback[category] = callback
@active
Gregory Szorc
transaction: support for callbacks during abort...
r23764 def addabort(self, category, callback):
"""add a callback to be called when the transaction is aborted.
The transaction will be given as the first argument to the callback.
Category is a unique identifier to allow overwriting an old callback
with a newer callback.
"""
self._abortcallback[category] = callback
@active
mpm@selenic.com
Add back links from file revisions to changeset revisions...
r0 def close(self):
Greg Ward
transaction: document close(), abort() methods
r9220 '''commit the transaction'''
Pierre-Yves David
transaction: always generate file on close...
r23290 if self.count == 1:
Pierre-Yves David
transaction: add a validation stage...
r24283 self.validator(self) # will raise exception if needed
Pierre-Yves David
transaction: only generate file when we actually close the transaction...
r23103 self._generatefiles()
Pierre-Yves David
transaction: allow registering a finalization callback...
r23204 categories = sorted(self._finalizecallback)
for cat in categories:
Pierre-Yves David
transaction: pass the transaction to 'finalize' callback...
r23281 self._finalizecallback[cat](self)
Durham Goode
transaction: add onclose/onabort hook for pre-close logic...
r20881
mason@suse.com
Automatic nesting into running transactions in the same repository....
r1806 self.count -= 1
if self.count != 0:
return
mpm@selenic.com
Add back links from file revisions to changeset revisions...
r0 self.file.close()
Pierre-Yves David
transaction: mark backup-related attributes private...
r23249 self._backupsfile.close()
Pierre-Yves David
transaction: allow registering a temporary transaction file...
r23291 # cleanup temporary files
Pierre-Yves David
transaction: support cache file in backupentries...
r23312 for l, f, b, c in self._backupentries:
if l not in self._vfsmap and c:
self.report("couldn't remote %s: unknown cache location %s\n"
% (b, l))
continue
Pierre-Yves David
transaction: use the location value when doing backup...
r23311 vfs = self._vfsmap[l]
if not f and b and vfs.exists(b):
Pierre-Yves David
transaction: support cache file in backupentries...
r23312 try:
vfs.unlink(b)
except (IOError, OSError, util.Abort), inst:
if not c:
raise
# Abort may be raise by read only opener
self.report("couldn't remote %s: %s\n"
% (vfs.join(b), inst))
mpm@selenic.com
Add back links from file revisions to changeset revisions...
r0 self.entries = []
Pierre-Yves David
transaction: include backup file in the "undo" transaction...
r23904 self._writeundo()
mpm@selenic.com
Beginnings of transaction undo support
r95 if self.after:
mpm@selenic.com
Fix undo after aborted commit bug...
r785 self.after()
FUJIWARA Katsunori
transaction: take journal file path relative to vfs to use file API via vfs
r20087 if self.opener.isfile(self.journal):
self.opener.unlink(self.journal)
Pierre-Yves David
transaction: mark backup-related attributes private...
r23249 if self.opener.isfile(self._backupjournal):
self.opener.unlink(self._backupjournal)
Pierre-Yves David
transaction: use the right location when cleaning up backup file (issue4479)...
r23727 for l, _f, b, c in self._backupentries:
Pierre-Yves David
transaction: support cache file in backupentries...
r23312 if l not in self._vfsmap and c:
self.report("couldn't remote %s: unknown cache location"
"%s\n" % (b, l))
continue
Pierre-Yves David
transaction: use the location value when doing backup...
r23311 vfs = self._vfsmap[l]
if b and vfs.exists(b):
Pierre-Yves David
transaction: support cache file in backupentries...
r23312 try:
vfs.unlink(b)
except (IOError, OSError, util.Abort), inst:
if not c:
raise
# Abort may be raise by read only opener
self.report("couldn't remote %s: %s\n"
% (vfs.join(b), inst))
Pierre-Yves David
transaction: mark backup-related attributes private...
r23249 self._backupentries = []
mpm@selenic.com
transaction: nullify journal after close()...
r573 self.journal = None
Pierre-Yves David
transaction: allow registering a post-close callback...
r23220 # run post close action
categories = sorted(self._postclosecallback)
for cat in categories:
Pierre-Yves David
transaction: pass the transaction to 'postclose' callback...
r23282 self._postclosecallback[cat](self)
mpm@selenic.com
Add back links from file revisions to changeset revisions...
r0
Henrik Stuart
transaction: ensure finished transactions are not reused...
r8289 @active
mpm@selenic.com
Add back links from file revisions to changeset revisions...
r0 def abort(self):
Greg Ward
transaction: document close(), abort() methods
r9220 '''abort the transaction (generally called on error, or when the
transaction is not explicitly committed before going out of
scope)'''
Henrik Stuart
transaction: ensure finished transactions are not reused...
r8289 self._abort()
Pierre-Yves David
transaction: include backup file in the "undo" transaction...
r23904 def _writeundo(self):
"""write transaction data for possible future undo call"""
if self.undoname is None:
return
undobackupfile = self.opener.open("%s.backupfiles" % self.undoname, 'w')
undobackupfile.write('%d\n' % version)
for l, f, b, c in self._backupentries:
if not f: # temporary file
continue
if not b:
u = ''
else:
if l not in self._vfsmap and c:
self.report("couldn't remote %s: unknown cache location"
"%s\n" % (b, l))
continue
vfs = self._vfsmap[l]
base, name = vfs.split(b)
assert name.startswith(self.journal), name
uname = name.replace(self.journal, self.undoname, 1)
u = vfs.reljoin(base, uname)
util.copyfile(vfs.join(b), vfs.join(u), hardlink=True)
undobackupfile.write("%s\0%s\0%s\0%d\n" % (l, f, u, c))
undobackupfile.close()
Henrik Stuart
transaction: ensure finished transactions are not reused...
r8289 def _abort(self):
Henrik Stuart
transaction: reset transaction on abort...
r8290 self.count = 0
Ronny Pfannschmidt
make transactions work on non-refcounted python implementations
r11230 self.usages = 0
Henrik Stuart
transaction: reset transaction on abort...
r8290 self.file.close()
Pierre-Yves David
transaction: mark backup-related attributes private...
r23249 self._backupsfile.close()
Henrik Stuart
transaction: reset transaction on abort...
r8290
Benoit Boissinot
transaction: initialize self.journal to None after deletion...
r10228 try:
Pierre-Yves David
transaction: mark backup-related attributes private...
r23249 if not self.entries and not self._backupentries:
Benoit Boissinot
transaction: initialize self.journal to None after deletion...
r10228 if self.journal:
FUJIWARA Katsunori
transaction: take journal file path relative to vfs to use file API via vfs
r20087 self.opener.unlink(self.journal)
Pierre-Yves David
transaction: mark backup-related attributes private...
r23249 if self._backupjournal:
self.opener.unlink(self._backupjournal)
Benoit Boissinot
transaction: initialize self.journal to None after deletion...
r10228 return
mpm@selenic.com
Add back links from file revisions to changeset revisions...
r0
Benoit Boissinot
transaction: initialize self.journal to None after deletion...
r10228 self.report(_("transaction abort!\n"))
mpm@selenic.com
Add back links from file revisions to changeset revisions...
r0
mpm@selenic.com
Warn if we fail to truncate something
r108 try:
Gregory Szorc
transaction: support for callbacks during abort...
r23764 for cat in sorted(self._abortcallback):
self._abortcallback[cat](self)
Pierre-Yves David
transaction: use the location value when doing backup...
r23311 _playback(self.journal, self.report, self.opener, self._vfsmap,
Pierre-Yves David
transaction: mark backup-related attributes private...
r23249 self.entries, self._backupentries, False)
Henrik Stuart
transaction: refactor transaction.abort and rollback to use the same code...
r8294 self.report(_("rollback completed\n"))
Brodie Rao
cleanup: replace naked excepts with except Exception: ...
r16689 except Exception:
Henrik Stuart
transaction: refactor transaction.abort and rollback to use the same code...
r8294 self.report(_("rollback failed - please run hg recover\n"))
finally:
self.journal = None
Henrik Stuart
transaction: reset transaction on abort...
r8290
Pierre-Yves David
transaction: use the location value when doing backup...
r23311 def rollback(opener, vfsmap, file, report):
Durham Goode
transaction: add support for non-append files...
r20882 """Rolls back the transaction contained in the given file
Reads the entries in the specified file, and the corresponding
'*.backupfiles' file, to recover from an incomplete transaction.
* `file`: a file containing a list of entries, specifying where
to truncate each file. The file should contain a list of
file\0offset pairs, delimited by newlines. The corresponding
'*.backupfiles' file should contain a list of file\0backupfile
pairs, delimited by \0.
"""
Henrik Stuart
transaction: refactor transaction.abort and rollback to use the same code...
r8294 entries = []
Durham Goode
transaction: add support for non-append files...
r20882 backupentries = []
Henrik Stuart
transaction: refactor transaction.abort and rollback to use the same code...
r8294
FUJIWARA Katsunori
transaction: take journal file path relative to vfs to use file API via vfs
r20087 fp = opener.open(file)
Dan Villiom Podlaski Christiansen
explicitly close files...
r13400 lines = fp.readlines()
fp.close()
for l in lines:
Matt Mackall
journal: report parsing errors on recover/rollback (issue4172)
r20524 try:
f, o = l.split('\0')
entries.append((f, int(o), None))
except ValueError:
report(_("couldn't read journal entry %r!\n") % l)
mpm@selenic.com
Add back links from file revisions to changeset revisions...
r0
Durham Goode
transaction: add support for non-append files...
r20882 backupjournal = "%s.backupfiles" % file
if opener.exists(backupjournal):
fp = opener.open(backupjournal)
Durham Goode
transactions: change backupfiles format to use newlines...
r23065 lines = fp.readlines()
if lines:
ver = lines[0][:-1]
Durham Goode
transactions: add version number to journal.backupfiles...
r23064 if ver == str(version):
Durham Goode
transactions: change backupfiles format to use newlines...
r23065 for line in lines[1:]:
if line:
# Shave off the trailing newline
line = line[:-1]
Pierre-Yves David
transaction: change the on disk format for backupentries...
r23309 l, f, b, c = line.split('\0')
backupentries.append((l, f, b, bool(c)))
Durham Goode
transactions: add version number to journal.backupfiles...
r23064 else:
Pierre-Yves David
transaction: change the on disk format for backupentries...
r23309 report(_("journal was created by a different version of "
Durham Goode
transactions: add version number to journal.backupfiles...
r23064 "Mercurial"))
Durham Goode
transaction: add support for non-append files...
r20882
Pierre-Yves David
transaction: use the location value when doing backup...
r23311 _playback(file, report, opener, vfsmap, entries, backupentries)