Backup.py revision 9a70fc3be3b1e966bf78825cdb8d509963a6f0a1
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson#
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# This program is free software; you can redistribute it and/or modify
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# it under the terms of the GNU General Public License version 2
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# as published by the Free Software Foundation.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson#
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# This program is distributed in the hope that it will be useful,
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# but WITHOUT ANY WARRANTY; without even the implied warranty of
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# GNU General Public License for more details.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson#
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# You should have received a copy of the GNU General Public License
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# along with this program; if not, write to the Free Software
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson#
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson#
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson# Use is subject to license terms.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson#
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelsonWorkspace backup
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelsonBackup format is:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson backupdir/
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson wsname/
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson generation#/
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson dirstate (handled by CdmUncommittedBackup)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson File containing dirstate nodeid (the tip we expect to be at
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson after applying the bundle).
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson bundle (handled by CdmCommittedBackup)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson An Hg bundle containing outgoing committed changes.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson nodes (handled by CdmCommittedBackup)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson A text file listing the full (hex) nodeid of all nodes in
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson bundle, used by need_backup.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson diff (handled by CdmUncommittedBackup)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson A Git-formatted diff containing uncommitted changes.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson renames (handled by CdmUncommittedBackup)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson A list of renames in the working copy that have to be
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson applied manually, rather than by the diff.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson metadata.tar.gz (handled by CdmMetadataBackup)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson $CODEMGR_WS/.hg/hgrc
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson $CODEMGR_WS/.hg/localtags
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson $CODEMGR_WS/.hg/patches (Mq data)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson latest -> generation#
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson Newest backup generation.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelsonAll files in a given backup generation, with the exception of
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelsondirstate, are optional.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelsonimport os, pwd, shutil, traceback, tarfile, time
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelsonfrom mercurial import changegroup, patch, node, util
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelsonfrom cStringIO import StringIO
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelsonclass CdmCommittedBackup(object):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Backup of committed changes'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def __init__(self, backup, ws):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.ws = ws
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.bu = backup
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.files = ('bundle', 'nodes')
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def _outgoing_nodes(self, parent):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Return a list of all outgoing nodes in hex format'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if parent:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson outgoing = self.ws.findoutgoing(parent)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson nodes = self.ws.repo.changelog.nodesbetween(outgoing)[0]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return map(node.hex, nodes)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson else:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return []
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def backup(self):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Backup committed changes'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson parent = self.ws.parent()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if not parent:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.ws.ui.warn('Workspace has no parent, committed changes will '
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson 'not be backed up\n')
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson out = self.ws.findoutgoing(parent)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if not out:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson cg = self.ws.repo.changegroup(out, 'bundle')
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson changegroup.writebundle(cg, self.bu.backupfile('bundle'), 'HG10BZ')
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson outnodes = self._outgoing_nodes(parent)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if outnodes:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson fp = None
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson fp = open(self.bu.backupfile('nodes'), 'w')
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson fp.write('%s\n' % '\n'.join(outnodes))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson except EnvironmentError, e:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson raise util.Abort("couldn't store outgoing nodes: %s" % e)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson finally:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if fp and not fp.closed:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson fp.close()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def restore(self):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Restore committed changes from backup'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson bfile = self.bu.backupfile('bundle')
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if os.path.exists(bfile):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson f = None
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson f = open(bfile, 'r')
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson bundle = changegroup.readbundle(f, bfile)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.ws.repo.addchangegroup(bundle, 'strip',
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson 'bundle:%s' % bfile)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson except EnvironmentError, e:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson raise util.Abort("couldn't restore committed changes: %s\n"
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson " %s" % (bfile, e))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson finally:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if f and not f.closed:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson f.close()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def need_backup(self):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Compare backup of committed changes to workspace'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if os.path.exists(self.bu.backupfile('nodes')):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson f = None
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson f = open(self.bu.backupfile('nodes'))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson bnodes = set([line.rstrip('\r\n')
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson for line in f.readlines()])
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson f.close()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson except EnvironmentError, e:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson raise util.Abort("couldn't open backup node list: %s" % e)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson finally:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if f and not f.closed:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson f.close()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson else:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson bnodes = set()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson outnodes = set(self._outgoing_nodes(self.ws.parent()))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if outnodes != bnodes:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return True
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return False
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def cleanup(self):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Remove backed up committed changes'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson for fname in self.files:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if os.path.exists(self.bu.backupfile(fname)):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson os.unlink(self.bu.backupfile(fname))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelsonclass CdmUncommittedBackup(object):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Backup of uncommitted changes'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def __init__(self, backup, ws):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.ws = ws
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.bu = backup
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def _clobbering_renames(self):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Return a list of pairs of files representing renames/copies
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson that clobber already versioned files. [(oldname newname)...]'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # Note that this doesn't handle uncommitted merges
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # as CdmUncommittedBackup itself doesn't.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson wctx = self.ws.repo.workingctx()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson parent = wctx.parents()[0]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson ret = []
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson for fname in wctx.added() + wctx.modified():
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson rn = wctx.filectx(fname).renamed()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if rn and fname in parent:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson ret.append((rn[0], fname))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return ret
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def backup(self):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Backup uncommitted changes'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if self.ws.merged():
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson raise util.Abort("Unable to backup an uncommitted merge.\n"
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson "Please complete your merge and commit")
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson dirstate = node.hex(self.ws.repo.changectx().node())
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson fp = None
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson fp = open(self.bu.backupfile('dirstate'), 'w')
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson fp.write(dirstate + '\n')
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson except EnvironmentError, e:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson raise util.Abort("couldn't save working copy parent: %s" % e)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson finally:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if fp and not fp.closed:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson fp.close()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson fp = open(self.bu.backupfile('renames'), 'w')
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson for cons in self._clobbering_renames():
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson fp.write("%s %s\n" % cons)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson except EnvironmentError, e:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson raise util.Abort("couldn't save clobbering copies: %s" % e)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson finally:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if fp and not fp.closed:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson fp.close()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson fp = open(self.bu.backupfile('diff'), 'w')
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson patch.diff(self.ws.repo, fp=fp,
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson opts=patch.diffopts(self.ws.ui, opts={'git': True}))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson except EnvironmentError, e:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson raise util.Abort("couldn't save working copy diff: %s" % e)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson finally:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if fp and not fp.closed:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson fp.close()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def _dirstate(self):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Return the current working copy node'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson fp = None
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson fp = open(self.bu.backupfile('dirstate'))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson dirstate = fp.readline().strip()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return dirstate
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson except EnvironmentError, e:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson raise util.Abort("couldn't read saved parent: %s" % e)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson finally:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if fp and not fp.closed:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson fp.close()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def restore(self):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Restore uncommitted changes'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson diff = self.bu.backupfile('diff')
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson dirstate = self._dirstate()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.ws.clean(rev=dirstate)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson except util.Abort, e:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson raise util.Abort("couldn't update to saved node: %s" % e)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if not os.path.exists(diff):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # There's a race here whereby if the patch (or part thereof)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # is applied within the same second as the clean above (such
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # that mtime doesn't change) and if the size of that file
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # does not change, Hg may not see the change.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # We sleep a full second to avoid this, as sleeping merely
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # until the next second begins would require very close clock
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # synchronization on network filesystems.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson time.sleep(1)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson files = {}
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson fuzz = patch.patch(diff, self.ws.ui, strip=1,
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson cwd=self.ws.repo.root, files=files)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if fuzz:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson raise util.Abort('working copy diff applied with fuzz')
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson except Exception, e:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson raise util.Abort("couldn't apply working copy diff: %s\n"
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson " %s" % (diff, e))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson finally:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson patch.updatedir(self.ws.ui, self.ws.repo, files)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if not os.path.exists(self.bu.backupfile('renames')):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # We need to re-apply name changes where the new name
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # (rename/copy destination) is an already versioned file, as
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # Hg would otherwise ignore them.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson fp = open(self.bu.backupfile('renames'))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson for line in fp:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson source, dest = line.strip().split()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.ws.repo.copy(source, dest)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson except EnvironmentError, e:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson raise util.Abort('unable to open renames file: %s' % e)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson except ValueError:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson raise util.Abort('corrupt renames file: %s' %
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.bu.backupfile('renames'))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def need_backup(self):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Compare backup of uncommitted changes to workspace'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if self._dirstate() != node.hex(self.ws.repo.changectx().node()):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return True
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson curdiff = StringIO()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson diff = self.bu.backupfile('diff')
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson fd = None
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson patch.diff(self.ws.repo, fp=curdiff,
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson opts=patch.diffopts(self.ws.ui, opts={'git': True}))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if os.path.exists(diff):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson fd = open(diff)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson backdiff = fd.read()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson except EnvironmentError, e:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson raise util.Abort("couldn't open backup diff %s\n"
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson " %s" % (diff, e))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson finally:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if fd and not fd.closed:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson fd.close()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson else:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson backdiff = ''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if backdiff != curdiff.getvalue():
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return True
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson currrenamed = self._clobbering_renames()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson bakrenamed = None
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if os.path.exists(self.bu.backupfile('renames')):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson fd = open(self.bu.backupfile('renames'))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson bakrenamed = [line.strip().split(' ') for line in fd]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson except EnvironmentError, e:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson raise util.Abort("couldn't open renames file %s: %s\n" %
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson (self.bu.backupfile('renames'), e))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson finally:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if fd and not fd.closed:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson fd.close()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if currrenamed != bakrenamed:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return True
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return False
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def cleanup(self):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Remove backed up uncommitted changes'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson for fname in ('dirstate', 'diff', 'renames'):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if os.path.exists(self.bu.backupfile(fname)):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson os.unlink(self.bu.backupfile(fname))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelsonclass CdmMetadataBackup(object):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Backup of workspace metadata'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def __init__(self, backup, ws):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.bu = backup
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.ws = ws
9a70fc3be3b1e966bf78825cdb8d509963a6f0a1Mark J. Nelson self.files = ('hgrc', 'localtags', 'patches', 'cdm')
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def backup(self):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Backup workspace metadata'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson tar = None
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson tar = tarfile.open(self.bu.backupfile('metadata.tar.gz'),
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson 'w:gz')
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson tar.errorlevel = 2
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson except (EnvironmentError, tarfile.TarError), e:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson raise util.Abort("couldn't open %s for writing: %s" %
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson (self.bu.backupfile('metadata.tar.gz'), e))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson for elt in self.files:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson fpath = self.ws.repo.join(elt)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if os.path.exists(fpath):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson tar.add(fpath, elt)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson except (EnvironmentError, tarfile.TarError), e:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # tarfile.TarError doesn't include the tar member or file
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # in question, so we have to do so ourselves.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if isinstance(e, tarfile.TarError):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson error = "%s: %s" % (elt, e)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson else:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson error = str(e)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson raise util.Abort("couldn't backup metadata to %s:\n"
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson " %s" %
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson (self.bu.backupfile('metadata.tar.gz'),
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson error))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson finally:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if tar and not tar.closed:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson tar.close()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def old_restore(self):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Restore workspace metadata from an pre-tar backup'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson for fname in self.files:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson bfile = self.bu.backupfile(fname)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson wfile = self.ws.repo.join(fname)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if os.path.exists(bfile):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson shutil.copy2(bfile, wfile)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson except EnvironmentError, e:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson raise util.Abort("couldn't restore metadata from %s:\n"
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson " %s" % (bfile, e))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def tar_restore(self):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Restore workspace metadata (from a tar-style backup)'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if os.path.exists(self.bu.backupfile('metadata.tar.gz')):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson tar = None
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson tar = tarfile.open(self.bu.backupfile('metadata.tar.gz'))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson tar.errorlevel = 2
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson except (EnvironmentError, tarfile.TarError), e:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson raise util.Abort("couldn't open %s: %s" %
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson (self.bu.backupfile('metadata.tar.gz'), e))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson for elt in tar:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson tar.extract(elt, path=self.ws.repo.path)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson except (EnvironmentError, tarfile.TarError), e:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # Make sure the member name is in the exception message.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if isinstance(e, tarfile.TarError):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson error = "%s: %s" % (elt.name, e)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson else:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson error = str(e)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson raise util.Abort("couldn't restore metadata from %s:\n"
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson " %s" %
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson (self.bu.backupfile('metadata.tar.gz'),
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson error))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson finally:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if tar and not tar.closed:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson tar.close()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def restore(self):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Restore workspace metadata'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if os.path.exists(self.bu.backupfile('hgrc')):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.old_restore()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson else:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.tar_restore()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def need_backup(self):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Compare backed up workspace metadata to workspace'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if os.path.exists(self.bu.backupfile('metadata.tar.gz')):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson tar = tarfile.open(self.bu.backupfile('metadata.tar.gz'))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson tar.errorlevel = 2
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson except (EnvironmentError, tarfile.TarError), e:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson raise util.Abort("couldn't open metadata tarball: %s\n"
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson " %s" %
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson (self.bu.backupfile('metadata.tar.gz'), e))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson for elt in tar:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson fpath = self.ws.repo.join(elt.name)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if not os.path.exists(fpath):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return True # File in tar, not workspace
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if elt.isdir(): # Don't care about directories
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson continue
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if (elt.mtime != os.path.getmtime(fpath) or
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson elt.size != os.path.getsize(fpath)):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return True
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson tarnames = tar.getnames()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson tar.close()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson else:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson tarnames = []
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson for mfile in self.files:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson fpath = self.ws.repo.join(mfile)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if os.path.isdir(fpath):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # Directories in tarfile always end with a '/'
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if not mfile.endswith('/'):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson mfile += '/'
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if mfile not in tarnames:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return True
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson for root, dirs, files in os.walk(fpath, topdown=True):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson for elt in files:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson path = os.path.join(root, elt)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson rpath = self.ws.repo.path
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if not rpath.endswith('/'):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson rpath += '/'
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson path = path.replace(rpath, '', 1)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if path not in tarnames:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return True # In workspace not tar
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson else:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if os.path.exists(fpath) and mfile not in tarnames:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return True
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return False
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def cleanup(self):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Remove backed up workspace metadata'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if os.path.exists(self.bu.backupfile('metadata.tar.gz')):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson os.unlink(self.bu.backupfile('metadata.tar.gz'))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelsonclass CdmBackup(object):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''A backup of a given workspace'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def __init__(self, ui, ws, name):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.ws = ws
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.ui = ui
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.backupdir = self._find_backup_dir(name)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # The order of instances here controls the order the various operations
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # are run.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # There's some inherent dependence, in that on restore we need
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # to restore committed changes prior to uncommitted changes
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # (as the parent revision of any uncommitted changes is quite
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # likely to not exist until committed changes are restored).
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # Metadata restore can happen at any point, but happens last
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # as a matter of convention.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.modules = [x(self, ws) for x in [CdmCommittedBackup,
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson CdmUncommittedBackup,
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson CdmMetadataBackup]]
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if os.path.exists(os.path.join(self.backupdir, 'latest')):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson generation = os.readlink(os.path.join(self.backupdir, 'latest'))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.generation = int(os.path.split(generation)[1])
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson else:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.generation = 0
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def _find_backup_dir(self, name):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Find the path to an appropriate backup directory based on NAME'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson backupdir = None
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson backupbase = None
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if os.path.isabs(name):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return name
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if self.ui.config('cdm', 'backupdir'):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson backupbase = os.path.expanduser(self.ui.config('cdm', 'backupdir'))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson else:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson home = None
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson home = os.getenv('HOME') or pwd.getpwuid(os.getuid()).pw_dir
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson except KeyError:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson pass # Handled anyway
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if not home:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson raise util.Abort('Could not determine your HOME directory to '
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson 'find backup path')
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson backupbase = os.path.join(home, 'cdm.backup')
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson backupdir = os.path.join(backupbase, name)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # If backupdir exists, it must be a directory.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if (os.path.exists(backupdir) and not os.path.isdir(backupdir)):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson raise util.Abort('%s exists but is not a directory' % backupdir)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return backupdir
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def backupfile(self, path):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''return full path to backup file FILE at GEN'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return os.path.join(self.backupdir, str(self.generation), path)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def update_latest(self, gen):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Update latest symlink to point to the current generation'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson linkpath = os.path.join(self.backupdir, 'latest')
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if os.path.lexists(linkpath):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson os.unlink(linkpath)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson os.symlink(str(gen), linkpath)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def create_gen(self, gen):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Create a new backup generation'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson os.makedirs(os.path.join(self.backupdir, str(gen)))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.update_latest(gen)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson except EnvironmentError, e:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson raise util.Abort("Couldn't create backup generation %s: %s" %
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson (os.path.join(self.backupdir, str(gen)), e))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def need_backup(self):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Compare backed up changes to workspace'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # If there's no current backup generation, or the last backup was
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # invalid (lacking the dirstate file), we need a backup regardless
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # of anything else.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if (not self.generation or
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson not os.path.exists(self.backupfile('dirstate'))):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return True
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson for x in self.modules:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if x.need_backup():
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return True
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson return False
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def backup(self):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Take a backup of the current workspace'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if not os.path.exists(self.backupdir):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson os.makedirs(self.backupdir)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson except EnvironmentError, e:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson raise util.Abort('Could not create backup directory %s: %s' %
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson (self.backupdir, e))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.generation += 1
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.create_gen(self.generation)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # Lock the repo, so the backup can be consistent. We need the
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # wlock too to make sure the dirstate parent doesn't change
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # underneath us.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson lock = self.ws.repo.lock()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson wlock = self.ws.repo.lock()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson for x in self.modules:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson x.backup()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson except Exception, e:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if isinstance(e, KeyboardInterrupt):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.ws.ui.warn("Interrupted\n")
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson else:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.ws.ui.warn("Error: %s\n" % e)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # If it's not a 'normal' error, we want to print a stack
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # trace now in case the attempt to remove the partial
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson # backup also fails, and raises a second exception.
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson #
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if (not isinstance(e, (EnvironmentError, util.Abort))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson or self.ws.ui.traceback):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson traceback.print_exc()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson for x in self.modules:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson x.cleanup()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson os.rmdir(os.path.join(self.backupdir, str(self.generation)))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.generation -= 1
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if self.generation != 0:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.update_latest(self.generation)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson else:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson os.unlink(os.path.join(self.backupdir, 'latest'))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson raise util.Abort('Backup failed')
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson def restore(self, gen=None):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '''Restore workspace from backup
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson Restores from backup generation GEN (defaulting to the latest)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson into workspace WS.'''
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson wlock = self.ws.repo.wlock()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson lock = self.ws.repo.lock()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if not os.path.exists(self.backupdir):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson raise util.Abort('Backup directory does not exist: %s' %
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson (self.backupdir))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if gen:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if not os.path.exists(os.path.join(self.backupdir, str(gen))):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson raise util.Abort('Backup generation does not exist: %s' %
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson (os.path.join(self.backupdir, str(gen))))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson self.generation = int(gen)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if not self.generation: # This is ok, 0 is not a valid generation
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson raise util.Abort('Backup has no generations: %s' % self.backupdir)
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson if not os.path.exists(self.backupfile('dirstate')):
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson raise util.Abort('Backup %s/%s is incomplete (dirstate missing)' %
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson (self.backupdir, self.generation))
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson try:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson for x in self.modules:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson x.restore()
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson except util.Abort, e:
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson raise util.Abort('Error restoring workspace:\n'
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson '%s\n'
cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0mjnelson 'Workspace will be partially restored' % e)