[PATCH 3 of 5] transaction: use "location" when doing backup
Pierre-Yves David
pierre-yves.david at ens-lyon.org
Sat Oct 18 11:44:55 UTC 2014
# HG changeset patch
# User Pierre-Yves David <pierre-yves.david at fb.com>
# Date 1413605075 25200
# Fri Oct 17 21:04:35 2014 -0700
# Node ID f533386d7f84a8e5ad1885d654d5983d81302b79
# Parent 580364627689487b0c13185a28d925f676fa0526
transaction: use "location" when doing backup
Same logic here, we stop passing vfs object as arguments and use the location
identifier to backup the right file. We persist this in the transaction state
file. As this is a format change (3 values per entry instead of 2) we change
the file name to "backupfiles2".
Note that backup of empty file is handled by the "entries" list, not
"backupentries" and will improve that in the next changeset.
Some more compatibility layer between "backupfiles" and "backupfiles2" may come
in the futur.
diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -915,11 +915,12 @@ class localrepository(object):
def recover(self):
lock = self.lock()
try:
if self.svfs.exists("journal"):
self.ui.status(_("rolling back interrupted transaction\n"))
- transaction.rollback(self.sopener, "journal",
+ vfsmap = {'store': self.sopener, 'plain': self.opener}
+ transaction.rollback(self.sopener, vfsmap, "journal",
self.ui.warn)
self.invalidate()
return True
else:
self.ui.warn(_("no interrupted transaction available\n"))
@@ -971,11 +972,12 @@ class localrepository(object):
if dryrun:
return 0
parents = self.dirstate.parents()
self.destroying()
- transaction.rollback(self.sopener, 'undo', ui.warn)
+ vfsmap = {'store': self.sopener, 'plain': self.opener}
+ transaction.rollback(self.sopener, vfsmap, 'undo', ui.warn)
if self.vfs.exists('undo.bookmarks'):
self.vfs.rename('undo.bookmarks', 'bookmarks')
if self.svfs.exists('undo.phaseroots'):
self.svfs.rename('undo.phaseroots', 'phaseroots')
self.invalidate()
diff --git a/mercurial/transaction.py b/mercurial/transaction.py
--- a/mercurial/transaction.py
+++ b/mercurial/transaction.py
@@ -21,11 +21,12 @@ def active(func):
raise error.Abort(_(
'cannot use transaction when it is already committed/aborted'))
return func(self, *args, **kwds)
return _active
-def _playback(journal, report, opener, entries, backupentries, unlink=True):
+def _playback(journal, report, opener, vfsmap, entries, backupentries,\
+ unlink=True):
for f, o, _ignore in entries:
if o or not unlink:
try:
fp = opener(f, 'a')
fp.truncate(o)
@@ -39,22 +40,23 @@ def _playback(journal, report, opener, e
except (IOError, OSError), inst:
if inst.errno != errno.ENOENT:
raise
backupfiles = []
- for f, b, _ignore in backupentries:
- filepath = opener.join(f)
- backuppath = opener.join(b)
+ for l, f, b, _ignore in backupentries:
+ vfs = vfsmap[l]
+ 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)
raise
opener.unlink(journal)
- backuppath = "%s.backupfiles" % journal
+ backuppath = "%s.backupfiles2" % journal
if opener.exists(backuppath):
opener.unlink(backuppath)
for f in backupfiles:
opener.unlink(f)
@@ -90,11 +92,11 @@ class transaction(object):
self.journal = journal
self._queue = []
# a dict of arguments to be passed to hooks
self.hookargs = {}
- self.backupjournal = "%s.backupfiles" % journal
+ self.backupjournal = "%s.backupfiles2" % journal
self.file = opener.open(self.journal, "w")
self.backupsfile = opener.open(self.backupjournal, 'w')
if createmode is not None:
opener.chmod(self.journal, createmode & 0666)
opener.chmod(self.backupjournal, createmode & 0666)
@@ -119,18 +121,18 @@ class transaction(object):
offsets = []
backups = []
for f, o, _data in q[0]:
offsets.append((f, o))
- for f, b, _data in q[1]:
- backups.append((f, b))
+ for l, f, b, _data in q[1]:
+ backups.append((l, f, b))
d = ''.join(['%s\0%d\n' % (f, o) for f, o in offsets])
self.file.write(d)
self.file.flush()
- d = ''.join(['%s\0%s\0' % (f, b) for f, b in backups])
+ d = ''.join(['%s\0%s\0%s\0' % entry for entry in backups])
self.backupsfile.write(d)
self.backupsfile.flush()
@active
def add(self, file, offset, data=None):
@@ -145,11 +147,11 @@ class transaction(object):
# add enough data to the journal to do the truncate
self.file.write("%s\0%d\n" % (file, offset))
self.file.flush()
@active
- def addbackup(self, file, hardlink=True, vfs=None):
+ def addbackup(self, file, hardlink=True, location='store'):
"""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.
@@ -159,12 +161,11 @@ class transaction(object):
"""
if file in self.map or file in self.backupmap:
return
backupfile = "%s.backup.%s" % (self.journal, file)
- if vfs is None:
- vfs = self.opener
+ vfs = self._vfsmap[location]
if vfs.exists(file):
filepath = vfs.join(file)
backuppath = self.opener.join(backupfile)
util.copyfiles(filepath, backuppath, hardlink=hardlink)
else:
@@ -173,13 +174,13 @@ class transaction(object):
if self._queue:
self._queue[-1][1].append((file, backupfile))
return
- self.backupentries.append((file, backupfile, None))
+ self.backupentries.append((location, file, backupfile, None))
self.backupmap[file] = len(self.backupentries) - 1
- self.backupsfile.write("%s\0%s\0" % (file, backupfile))
+ self.backupsfile.write("%s\0%s\0%s\0" % (location, file, backupfile))
self.backupsfile.flush()
@active
def addfilegenerator(self, genid, filenames, genfunc, order=0,
location='store'):
@@ -278,12 +279,13 @@ class transaction(object):
self.after()
if self.opener.isfile(self.journal):
self.opener.unlink(self.journal)
if self.opener.isfile(self.backupjournal):
self.opener.unlink(self.backupjournal)
- for _f, b, _ignore in self.backupentries:
- self.opener.unlink(b)
+ for l, _f, b, _ignore in self.backupentries:
+ vfs = self._vfsmap[l]
+ vfs.unlink(b)
self.backupentries = []
self.journal = None
@active
def abort(self):
@@ -310,20 +312,20 @@ class transaction(object):
return
self.report(_("transaction abort!\n"))
try:
- _playback(self.journal, self.report, self.opener,
+ _playback(self.journal, self.report, self.opener, self._vfsmap,
self.entries, self.backupentries, False)
self.report(_("rollback completed\n"))
except Exception:
self.report(_("rollback failed - please run hg recover\n"))
finally:
self.journal = None
-def rollback(opener, file, report):
+def rollback(opener, vfsmap, file, report):
"""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.
@@ -350,10 +352,10 @@ def rollback(opener, file, report):
if opener.exists(backupjournal):
fp = opener.open(backupjournal)
data = fp.read()
if len(data) > 0:
parts = data.split('\0')
- for i in xrange(0, len(parts), 2):
- f, b = parts[i:i + 1]
- backupentries.append((f, b, None))
+ for i in xrange(0, len(parts), 3):
+ f, b = parts[i:i + 2]
+ backupentries.append((l, f, b, None))
- _playback(file, report, opener, entries, backupentries)
+ _playback(file, report, opener, vfsmap, entries, backupentries)
More information about the Mercurial-devel
mailing list