diff --git a/infinitepush/__init__.py b/infinitepush/__init__.py --- a/infinitepush/__init__.py +++ b/infinitepush/__init__.py @@ -324,6 +324,7 @@ smartlogmod = extensions.find('smartlog') wrapcommand(smartlogmod.cmdtable, 'smartlog', _smartlog) extensions.afterloaded('smartlog', wrapsmartlog) + backupcommands.extsetup(ui) def _smartlog(orig, ui, repo, **opts): res = orig(ui, repo, **opts) diff --git a/infinitepush/backupcommands.py b/infinitepush/backupcommands.py --- a/infinitepush/backupcommands.py +++ b/infinitepush/backupcommands.py @@ -4,6 +4,11 @@ # GNU General Public License version 2 or any later version. """ [infinitepushbackup] + # Whether to enable automatic backups. If this option is True then a backup + # process will be started after every mercurial command that modifies the + # repo, for example, commit, amend, histedit, rebase etc. + autobackup = False + # path to the directory where pushback logs should be stored logdir = path/to/dir @@ -53,9 +58,12 @@ changegroup, commands, discovery, + dispatch, encoding, error, + extensions, hg, + localrepo, lock as lockmod, phases, registrar, @@ -98,6 +106,13 @@ _backuplockname = 'infinitepushbackup.lock' +def extsetup(ui): + if ui.configbool('infinitepushbackup', 'autobackup', False): + extensions.wrapfunction(dispatch, 'runcommand', + _autobackupruncommandwrapper) + extensions.wrapfunction(localrepo.localrepository, 'transaction', + _transaction) + @command('pushbackup', [('', 'background', None, 'run backup in background')]) def backup(ui, repo, dest=None, **opts): @@ -112,48 +127,7 @@ """ if opts.get('background'): - background_cmd = ['hg', 'pushbackup'] - if dest: - background_cmd.append(dest) - logfile = None - logdir = ui.config('infinitepushbackup', 'logdir') - if logdir: - # make newly created files and dirs non-writable - oldumask = os.umask(0o022) - try: - try: - username = util.shortuser(ui.username()) - except Exception: - username = 'unknown' - - if not _checkcommonlogdir(logdir): - raise WrongPermissionsException(logdir) - - userlogdir = os.path.join(logdir, username) - util.makedirs(userlogdir) - - if not _checkuserlogdir(userlogdir): - raise WrongPermissionsException(userlogdir) - - reporoot = repo.origroot - reponame = os.path.basename(reporoot) - _removeoldlogfiles(userlogdir, reponame) - logfile = _getlogfilename(logdir, username, reponame) - except (OSError, IOError) as e: - ui.debug('infinitepush backup log is disabled: %s\n' % e) - except WrongPermissionsException as e: - ui.debug(('%s directory has incorrect permission, ' + - 'infinitepush backup logging will be disabled\n') % - e.logdir) - finally: - os.umask(oldumask) - - if not logfile: - logfile = os.devnull - - with open(logfile, 'a') as f: - subprocess.Popen(background_cmd, shell=False, stdout=f, - stderr=subprocess.STDOUT) + _dobackgroundbackup(ui, repo, dest) return 0 try: @@ -355,6 +329,34 @@ ui.warn(_('Run `hg pushbackup` to perform a backup. If this fails,\n' 'please report to the Source Control @ FB group.\n')) +def _autobackupruncommandwrapper(orig, lui, repo, cmd, fullargs, *args): + ''' + If this wrapper is enabled then auto backup is started after every command + that modifies a repository. + Since we don't want to start auto backup after read-only commands, + then this wrapper checks if this command opened at least one transaction. + If yes then background backup will be started. + ''' + + # For chg, do not wrap the "serve" runcommand call + if 'CHGINTERNALMARK' in os.environ: + return orig(lui, repo, cmd, fullargs, *args) + + try: + return orig(lui, repo, cmd, fullargs, *args) + finally: + if getattr(repo, 'txnwasopened', False): + _dobackgroundbackup(lui, repo) + +def _transaction(orig, self, *args, **kwargs): + ''' Wrapper that records if a transaction was opened. + + If a transaction was opened then we want to start background backup process. + This hook records the fact that transaction was opened. + ''' + self.txnwasopened = True + return orig(self, *args, **kwargs) + def _backupheads(ui, repo): """Returns the set of heads that should be backed up in this repo.""" maxheadstobackup = ui.configint('infinitepushbackup', @@ -455,6 +457,50 @@ unwrapfunction(changegroup.cg2packer, 'deltaparent', _deltaparent) return 0 +def _dobackgroundbackup(ui, repo, dest=None): + background_cmd = ['hg', 'pushbackup'] + if dest: + background_cmd.append(dest) + logfile = None + logdir = ui.config('infinitepushbackup', 'logdir') + if logdir: + # make newly created files and dirs non-writable + oldumask = os.umask(0o022) + try: + try: + username = util.shortuser(ui.username()) + except Exception: + username = 'unknown' + + if not _checkcommonlogdir(logdir): + raise WrongPermissionsException(logdir) + + userlogdir = os.path.join(logdir, username) + util.makedirs(userlogdir) + + if not _checkuserlogdir(userlogdir): + raise WrongPermissionsException(userlogdir) + + reporoot = repo.origroot + reponame = os.path.basename(reporoot) + _removeoldlogfiles(userlogdir, reponame) + logfile = _getlogfilename(logdir, username, reponame) + except (OSError, IOError) as e: + ui.debug('infinitepush backup log is disabled: %s\n' % e) + except WrongPermissionsException as e: + ui.debug(('%s directory has incorrect permission, ' + + 'infinitepush backup logging will be disabled\n') % + e.logdir) + finally: + os.umask(oldumask) + + if not logfile: + logfile = os.devnull + + with open(logfile, 'a') as f: + subprocess.Popen(background_cmd, shell=False, stdout=f, + stderr=subprocess.STDOUT) + def _dobackupcheck(bkpstate, ui, repo, dest, **opts): remotehexnodes = sorted( set(bkpstate.heads).union(bkpstate.localbookmarks.values())) diff --git a/tests/library-infinitepush.sh b/tests/library-infinitepush.sh --- a/tests/library-infinitepush.sh +++ b/tests/library-infinitepush.sh @@ -96,3 +96,9 @@ sleep 1 hg debugwaitbackup } + +mkcommitautobackup() { + echo $1 > $1 + hg add $1 + hg ci -m $1 --config infinitepushbackup.autobackup=True +} diff --git a/tests/test-infinitepush-backup.t b/tests/test-infinitepush-backup.t --- a/tests/test-infinitepush-backup.t +++ b/tests/test-infinitepush-backup.t @@ -138,8 +138,7 @@ infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/heads/667453c0787e7830fdfb86db0f8c29aa7af2a1ea 667453c0787e7830fdfb86db0f8c29aa7af2a1ea (re) infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/heads/d5609f7fa63352da538eeffbe3ffabed1779aafc d5609f7fa63352da538eeffbe3ffabed1779aafc (re) infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/heads/f79c5017def3b9af9928edbb52cc620c74b4b291 f79c5017def3b9af9928edbb52cc620c74b4b291 (re) - $ mkcommit newcommit - $ hg pushbackup --background + $ mkcommitautobackup newcommit $ waitbgbackup $ scratchbookmarks infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/heads/3a30e220fe42e969e34bbe8001b951a20f31f2e8 3a30e220fe42e969e34bbe8001b951a20f31f2e8 (re)