diff --git a/infinitepush/README b/infinitepush/README deleted file mode 100644 --- a/infinitepush/README +++ /dev/null @@ -1,25 +0,0 @@ -## What is it? - -This extension adds ability to save certain pushes to a remote blob store -as bundles and to serve commits from remote blob store. -The revisions are stored on disk or in everstore. -The metadata are stored in sql or on disk. - -## Config options - -infinitepush.branchpattern: pattern to detect a scratchbranch, example - 're:scratch/.+' - -infinitepush.indextype: disk or sql for the metadata -infinitepush.reponame: only relevant for sql metadata backend, reponame to put in - sql - -infinitepush.indexpath: only relevant for ondisk metadata backend, the path to - store the index on disk. If not set will be under .hg - in a folder named filebundlestore - -infinitepush.storepath: only relevant for ondisk metadata backend, the path to - store the bundles. If not set, it will be - .hg/filebundlestore - - diff --git a/infinitepush/__init__.py b/infinitepush/__init__.py deleted file mode 100644 --- a/infinitepush/__init__.py +++ /dev/null @@ -1,1366 +0,0 @@ -# Infinite push -# -# Copyright 2016 Facebook, Inc. -# -# This software may be used and distributed according to the terms of the -# GNU General Public License version 2 or any later version. -""" - [infinitepush] - # Server-side and client-side option. Pattern of the infinitepush bookmark - branchpattern = PATTERN - - # Server or client - server = False - - # Server-side option. Possible values: 'disk' or 'sql'. Fails if not set - indextype = disk - - # Server-side option. Used only if indextype=sql. - # Format: 'IP:PORT:DB_NAME:USER:PASSWORD' - sqlhost = IP:PORT:DB_NAME:USER:PASSWORD - - # Server-side option. Used only if indextype=disk. - # Filesystem path to the index store - indexpath = PATH - - # Server-side option. Possible values: 'disk' or 'external' - # Fails if not set - storetype = disk - - # Server-side option. - # Path to the binary that will save bundle to the bundlestore - # Formatted cmd line will be passed to it (see `put_args`) - put_binary = put - - # Serser-side option. Used only if storetype=external. - # Format cmd-line string for put binary. Placeholder: {filename} - put_args = {filename} - - # Server-side option. - # Path to the binary that get bundle from the bundlestore. - # Formatted cmd line will be passed to it (see `get_args`) - get_binary = get - - # Serser-side option. Used only if storetype=external. - # Format cmd-line string for get binary. Placeholders: {filename} {handle} - get_args = {filename} {handle} - - # Server-side option - logfile = FIlE - - # Server-side option - loglevel = DEBUG - - # Server-side option. Used only if indextype=sql. - # Sets mysql wait_timeout option. - waittimeout = 300 - - # Server-side option. Used only if indextype=sql. - # Sets mysql innodb_lock_wait_timeout option. - locktimeout = 120 - - # Server-side option. Used only if indextype=sql. - # Name of the repository - reponame = '' - - # Client-side option. Used by --list-remote option. List of remote scratch - # patterns to list if no patterns are specified. - defaultremotepatterns = ['*'] - - # Server-side option. If bookmark that was pushed matches - # `fillmetadatabranchpattern` then background - # `hg debugfillinfinitepushmetadata` process will save metadata - # in infinitepush index for nodes that are ancestor of the bookmark. - fillmetadatabranchpattern = '' - - # Instructs infinitepush to forward all received bundle2 parts to the - # bundle for storage. Defaults to False. - storeallparts = True - - [remotenames] - # Client-side option - # This option should be set only if remotenames extension is enabled. - # Whether remote bookmarks are tracked by remotenames extension. - bookmarks = True -""" - -from __future__ import absolute_import -import contextlib -import errno -import json -import logging -import os -import random -import re -import socket -import struct -import subprocess -import sys -import tempfile -import time - -from .bundleparts import ( - copiedpart, - getscratchbranchparts, - scratchbookmarksparttype, - scratchbranchparttype, -) -from . import ( - backupcommands, - infinitepushcommands, - common, -) -from collections import defaultdict -from functools import partial -from mercurial import ( - bundle2, - changegroup, - commands, - discovery, - encoding, - error, - exchange, - extensions, - hg, - localrepo, - phases, - pushkey, - util, - wireproto, -) - -from mercurial.extensions import wrapcommand, wrapfunction, unwrapfunction -from mercurial.hg import repository -from mercurial.node import bin, hex -from mercurial.i18n import _ -from mercurial.peer import batchable, future -from mercurial.wireproto import encodelist, decodelist - -pushrebaseparttype = 'b2x:rebase' -experimental = 'experimental' -configbookmark = 'server-bundlestore-bookmark' -configcreate = 'server-bundlestore-create' -configscratchpush = 'infinitepush-scratchpush' -confignonforwardmove = 'non-forward-move' - -cmdtable = infinitepushcommands.cmdtable -revsetpredicate = backupcommands.revsetpredicate -templatekeyword = backupcommands.templatekeyword -_scratchbranchmatcher = lambda x: False -_maybehash = re.compile(r'^[a-f0-9]+$').search - -if util.safehasattr(util, '_hgexecutable'): - # Before 5be286db - _hgexecutable = util.hgexecutable -else: - from mercurial.utils import procutil - _hgexecutable = procutil.hgexecutable - -def _buildexternalbundlestore(ui): - put_args = ui.configlist('infinitepush', 'put_args', []) - put_binary = ui.config('infinitepush', 'put_binary') - if not put_binary: - raise error.Abort('put binary is not specified') - get_args = ui.configlist('infinitepush', 'get_args', []) - get_binary = ui.config('infinitepush', 'get_binary') - if not get_binary: - raise error.Abort('get binary is not specified') - from . import store - return store.externalbundlestore(put_binary, put_args, get_binary, get_args) - -def _buildsqlindex(ui): - sqlhost = ui.config('infinitepush', 'sqlhost') - if not sqlhost: - raise error.Abort(_('please set infinitepush.sqlhost')) - host, port, db, user, password = sqlhost.split(':') - reponame = ui.config('infinitepush', 'reponame') - if not reponame: - raise error.Abort(_('please set infinitepush.reponame')) - - logfile = ui.config('infinitepush', 'logfile', '') - waittimeout = ui.configint('infinitepush', 'waittimeout', 300) - locktimeout = ui.configint('infinitepush', 'locktimeout', 120) - from . import sqlindexapi - return sqlindexapi.sqlindexapi( - reponame, host, port, db, user, password, - logfile, _getloglevel(ui), waittimeout=waittimeout, - locktimeout=locktimeout) - -def _getloglevel(ui): - loglevel = ui.config('infinitepush', 'loglevel', 'DEBUG') - numeric_loglevel = getattr(logging, loglevel.upper(), None) - if not isinstance(numeric_loglevel, int): - raise error.Abort(_('invalid log level %s') % loglevel) - return numeric_loglevel - -def _tryhoist(ui, remotebookmark): - '''returns a bookmarks with hoisted part removed - - Remotenames extension has a 'hoist' config that allows to use remote - bookmarks without specifying remote path. For example, 'hg update master' - works as well as 'hg update remote/master'. We want to allow the same in - infinitepush. - ''' - - if common.isremotebooksenabled(ui): - hoist = ui.config('remotenames', 'hoist') + '/' - if remotebookmark.startswith(hoist): - return remotebookmark[len(hoist):] - return remotebookmark - -class bundlestore(object): - def __init__(self, repo): - self._repo = repo - storetype = self._repo.ui.config('infinitepush', 'storetype', '') - if storetype == 'disk': - from . import store - self.store = store.filebundlestore(self._repo.ui, self._repo) - elif storetype == 'external': - self.store = _buildexternalbundlestore(self._repo.ui) - else: - raise error.Abort( - _('unknown infinitepush store type specified %s') % storetype) - - indextype = self._repo.ui.config('infinitepush', 'indextype', '') - if indextype == 'disk': - from . import fileindexapi - self.index = fileindexapi.fileindexapi(self._repo) - elif indextype == 'sql': - self.index = _buildsqlindex(self._repo.ui) - else: - raise error.Abort( - _('unknown infinitepush index type specified %s') % indextype) - -def _isserver(ui): - return ui.configbool('infinitepush', 'server') - -def reposetup(ui, repo): - if _isserver(ui) and repo.local(): - repo.bundlestore = bundlestore(repo) - -def uisetup(ui): - # remotenames circumvents the default push implementation entirely, so make - # sure we load after it so that we wrap it. - order = extensions._order - order.remove('infinitepush') - order.append('infinitepush') - extensions._order = order - -def extsetup(ui): - # Allow writing backup files outside the normal lock - localrepo.localrepository._wlockfreeprefix.update([ - backupcommands._backupstatefile, - backupcommands._backupgenerationfile, - backupcommands._backuplatestinfofile, - ]) - - commonsetup(ui) - if _isserver(ui): - serverextsetup(ui) - else: - clientextsetup(ui) - -def commonsetup(ui): - wireproto.commands['listkeyspatterns'] = ( - wireprotolistkeyspatterns, 'namespace patterns') - scratchbranchpat = ui.config('infinitepush', 'branchpattern') - if scratchbranchpat: - global _scratchbranchmatcher - kind, pat, _scratchbranchmatcher = util.stringmatcher(scratchbranchpat) - -def serverextsetup(ui): - origpushkeyhandler = bundle2.parthandlermapping['pushkey'] - - def newpushkeyhandler(*args, **kwargs): - bundle2pushkey(origpushkeyhandler, *args, **kwargs) - newpushkeyhandler.params = origpushkeyhandler.params - bundle2.parthandlermapping['pushkey'] = newpushkeyhandler - - orighandlephasehandler = bundle2.parthandlermapping['phase-heads'] - newphaseheadshandler = lambda *args, **kwargs: \ - bundle2handlephases(orighandlephasehandler, *args, **kwargs) - newphaseheadshandler.params = orighandlephasehandler.params - bundle2.parthandlermapping['phase-heads'] = newphaseheadshandler - - wrapfunction(localrepo.localrepository, 'listkeys', localrepolistkeys) - wireproto.commands['lookup'] = ( - _lookupwrap(wireproto.commands['lookup'][0]), 'key') - wrapfunction(exchange, 'getbundlechunks', getbundlechunks) - - wrapfunction(bundle2, 'processparts', processparts) - -def clientextsetup(ui): - entry = wrapcommand(commands.table, 'push', _push) - # Don't add the 'to' arg if it already exists - if not any(a for a in entry[1] if a[1] == 'to'): - entry[1].append(('', 'to', '', _('push revs to this bookmark'))) - - if not any(a for a in entry[1] if a[1] == 'non-forward-move'): - entry[1].append(('', 'non-forward-move', None, - _('allows moving a remote bookmark to an ' - 'arbitrary place'))) - - if not any(a for a in entry[1] if a[1] == 'create'): - entry[1].append( - ('', 'create', None, _('create a new remote bookmark'))) - - entry[1].append( - ('', 'bundle-store', None, - _('force push to go to bundle store (EXPERIMENTAL)'))) - - bookcmd = extensions.wrapcommand(commands.table, 'bookmarks', exbookmarks) - bookcmd[1].append( - ('', 'list-remote', None, - 'list remote bookmarks. ' - 'Positional arguments are interpreted as wildcard patterns. ' - 'Only allowed wildcard is \'*\' in the end of the pattern. ' - 'If no positional arguments are specified then it will list ' - 'the most "important" remote bookmarks. ' - 'Otherwise it will list remote bookmarks ' - 'that match at least one pattern ' - '')) - bookcmd[1].append( - ('', 'remote-path', '', - 'name of the remote path to list the bookmarks')) - - wrapcommand(commands.table, 'pull', _pull) - wrapcommand(commands.table, 'update', _update) - - wrapfunction(discovery, 'checkheads', _checkheads) - wrapfunction(bundle2, '_addpartsfromopts', _addpartsfromopts) - - wireproto.wirepeer.listkeyspatterns = listkeyspatterns - - # Move infinitepush part before pushrebase part - # to avoid generation of both parts. - partorder = exchange.b2partsgenorder - index = partorder.index('changeset') - if pushrebaseparttype in partorder: - index = min(index, partorder.index(pushrebaseparttype)) - partorder.insert( - index, partorder.pop(partorder.index(scratchbranchparttype))) - - def wrapsmartlog(loaded): - if not loaded: - return - 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) - backupcommands.smartlogsummary(ui, repo) - return res - -def _showbookmarks(ui, bookmarks, **opts): - # Copy-paste from commands.py - fm = ui.formatter('bookmarks', opts) - for bmark, n in sorted(bookmarks.iteritems()): - fm.startitem() - if not ui.quiet: - fm.plain(' ') - fm.write('bookmark', '%s', bmark) - pad = ' ' * (25 - encoding.colwidth(bmark)) - fm.condwrite(not ui.quiet, 'node', pad + ' %s', n) - fm.plain('\n') - fm.end() - -def exbookmarks(orig, ui, repo, *names, **opts): - pattern = opts.get('list_remote') - delete = opts.get('delete') - remotepath = opts.get('remote_path') - path = ui.paths.getpath(remotepath or None, default=('default')) - if pattern: - destpath = path.pushloc or path.loc - other = hg.peer(repo, opts, destpath) - if not names: - raise error.Abort( - '--list-remote requires a bookmark pattern', - hint='use "hg book" to get a list of your local bookmarks') - else: - fetchedbookmarks = other.listkeyspatterns('bookmarks', - patterns=names) - _showbookmarks(ui, fetchedbookmarks, **opts) - return - elif delete and 'remotenames' in extensions._extensions: - existing_local_bms = set(repo._bookmarks.keys()) - scratch_bms = [] - other_bms = [] - for name in names: - if _scratchbranchmatcher(name) and name not in existing_local_bms: - scratch_bms.append(name) - else: - other_bms.append(name) - - if len(scratch_bms) > 0: - if remotepath == '': - remotepath = 'default' - _deleteinfinitepushbookmarks(ui, - repo, - remotepath, - scratch_bms) - - if len(other_bms) > 0 or len(scratch_bms) == 0: - return orig(ui, repo, *other_bms, **opts) - else: - return orig(ui, repo, *names, **opts) - -def _checkheads(orig, pushop): - if pushop.ui.configbool(experimental, configscratchpush, False): - return - return orig(pushop) - -def _addpartsfromopts(orig, ui, repo, bundler, *args, **kwargs): - """ adds a stream level part to bundle2 storing whether this is an - infinitepush bundle or not """ - if ui.configbool('infinitepush', 'bundle-stream', False): - bundler.addparam('infinitepush', True) - return orig(ui, repo, bundler, *args, **kwargs) - -def wireprotolistkeyspatterns(repo, proto, namespace, patterns): - patterns = decodelist(patterns) - d = repo.listkeys(encoding.tolocal(namespace), patterns).iteritems() - return pushkey.encodekeys(d) - -def localrepolistkeys(orig, self, namespace, patterns=None): - if namespace == 'bookmarks' and patterns: - index = self.bundlestore.index - results = {} - bookmarks = orig(self, namespace) - for pattern in patterns: - results.update(index.getbookmarks(pattern)) - if pattern.endswith('*'): - pattern = 're:^' + pattern[:-1] + '.*' - kind, pat, matcher = util.stringmatcher(pattern) - for bookmark, node in bookmarks.iteritems(): - if matcher(bookmark): - results[bookmark] = node - return results - else: - return orig(self, namespace) - -@batchable -def listkeyspatterns(self, namespace, patterns): - if not self.capable('pushkey'): - yield {}, None - f = future() - self.ui.debug('preparing listkeys for "%s" with pattern "%s"\n' % - (namespace, patterns)) - yield { - 'namespace': encoding.fromlocal(namespace), - 'patterns': encodelist(patterns) - }, f - d = f.value - self.ui.debug('received listkey for "%s": %i bytes\n' - % (namespace, len(d))) - yield pushkey.decodekeys(d) - -def _readbundlerevs(bundlerepo): - return list(bundlerepo.revs('bundle()')) - -def _includefilelogstobundle(bundlecaps, bundlerepo, bundlerevs, ui): - '''Tells remotefilelog to include all changed files to the changegroup - - By default remotefilelog doesn't include file content to the changegroup. - But we need to include it if we are fetching from bundlestore. - ''' - changedfiles = set() - cl = bundlerepo.changelog - for r in bundlerevs: - # [3] means changed files - changedfiles.update(cl.read(r)[3]) - if not changedfiles: - return bundlecaps - - changedfiles = '\0'.join(changedfiles) - newcaps = [] - appended = False - for cap in (bundlecaps or []): - if cap.startswith('excludepattern='): - newcaps.append('\0'.join((cap, changedfiles))) - appended = True - else: - newcaps.append(cap) - if not appended: - # Not found excludepattern cap. Just append it - newcaps.append('excludepattern=' + changedfiles) - - return newcaps - -def _rebundle(bundlerepo, bundleroots, unknownhead): - ''' - Bundle may include more revision then user requested. For example, - if user asks for revision but bundle also consists its descendants. - This function will filter out all revision that user is not requested. - ''' - parts = [] - - version = '02' - outgoing = discovery.outgoing(bundlerepo, commonheads=bundleroots, - missingheads=[unknownhead]) - cgstream = changegroup.makestream(bundlerepo, outgoing, version, 'pull') - cgstream = util.chunkbuffer(cgstream).read() - cgpart = bundle2.bundlepart('changegroup', data=cgstream) - cgpart.addparam('version', version) - parts.append(cgpart) - - try: - treemod = extensions.find('treemanifest') - except KeyError: - pass - else: - if treemod._cansendtrees(bundlerepo, outgoing.missing): - treepart = treemod.createtreepackpart(bundlerepo, outgoing, - treemod.TREEGROUP_PARTTYPE2) - parts.append(treepart) - - return parts - -def _getbundleroots(oldrepo, bundlerepo, bundlerevs): - cl = bundlerepo.changelog - bundleroots = [] - for rev in bundlerevs: - node = cl.node(rev) - parents = cl.parents(node) - for parent in parents: - # include all revs that exist in the main repo - # to make sure that bundle may apply client-side - if parent in oldrepo: - bundleroots.append(parent) - return bundleroots - -def _needsrebundling(head, bundlerepo): - bundleheads = list(bundlerepo.revs('heads(bundle())')) - return not (len(bundleheads) == 1 and - bundlerepo[bundleheads[0]].node() == head) - -def _generateoutputparts(head, bundlerepo, bundleroots, bundlefile): - '''generates bundle that will be send to the user - - returns tuple with raw bundle string and bundle type - ''' - parts = [] - if not _needsrebundling(head, bundlerepo): - with util.posixfile(bundlefile, "rb") as f: - unbundler = exchange.readbundle(bundlerepo.ui, f, bundlefile) - if isinstance(unbundler, changegroup.cg1unpacker): - part = bundle2.bundlepart('changegroup', - data=unbundler._stream.read()) - part.addparam('version', '01') - parts.append(part) - elif isinstance(unbundler, bundle2.unbundle20): - haschangegroup = False - for part in unbundler.iterparts(): - if part.type == 'changegroup': - haschangegroup = True - newpart = bundle2.bundlepart(part.type, data=part.read()) - for key, value in part.params.iteritems(): - newpart.addparam(key, value) - parts.append(newpart) - - if not haschangegroup: - raise error.Abort( - 'unexpected bundle without changegroup part, ' + - 'head: %s' % hex(head), - hint='report to administrator') - else: - raise error.Abort('unknown bundle type') - else: - parts = _rebundle(bundlerepo, bundleroots, head) - - return parts - -def getbundlechunks(orig, repo, source, heads=None, bundlecaps=None, **kwargs): - heads = heads or [] - # newheads are parents of roots of scratch bundles that were requested - newphases = {} - scratchbundles = [] - newheads = [] - scratchheads = [] - nodestobundle = {} - allbundlestocleanup = [] - try: - for head in heads: - if head not in repo.changelog.nodemap: - if head not in nodestobundle: - newbundlefile = common.downloadbundle(repo, head) - bundlepath = "bundle:%s+%s" % (repo.root, newbundlefile) - bundlerepo = repository(repo.ui, bundlepath) - - allbundlestocleanup.append((bundlerepo, newbundlefile)) - bundlerevs = set(_readbundlerevs(bundlerepo)) - bundlecaps = _includefilelogstobundle( - bundlecaps, bundlerepo, bundlerevs, repo.ui) - cl = bundlerepo.changelog - bundleroots = _getbundleroots(repo, bundlerepo, bundlerevs) - for rev in bundlerevs: - node = cl.node(rev) - newphases[hex(node)] = str(phases.draft) - nodestobundle[node] = (bundlerepo, bundleroots, - newbundlefile) - - scratchbundles.append( - _generateoutputparts(head, *nodestobundle[head])) - newheads.extend(bundleroots) - scratchheads.append(head) - finally: - for bundlerepo, bundlefile in allbundlestocleanup: - bundlerepo.close() - try: - os.unlink(bundlefile) - except (IOError, OSError): - # if we can't cleanup the file then just ignore the error, - # no need to fail - pass - - pullfrombundlestore = bool(scratchbundles) - wrappedchangegrouppart = False - wrappedlistkeys = False - oldchangegrouppart = exchange.getbundle2partsmapping['changegroup'] - try: - def _changegrouppart(bundler, *args, **kwargs): - # Order is important here. First add non-scratch part - # and only then add parts with scratch bundles because - # non-scratch part contains parents of roots of scratch bundles. - result = oldchangegrouppart(bundler, *args, **kwargs) - for bundle in scratchbundles: - for part in bundle: - bundler.addpart(part) - return result - - exchange.getbundle2partsmapping['changegroup'] = _changegrouppart - wrappedchangegrouppart = True - - def _listkeys(orig, self, namespace): - origvalues = orig(self, namespace) - if namespace == 'phases' and pullfrombundlestore: - if origvalues.get('publishing') == 'True': - # Make repo non-publishing to preserve draft phase - del origvalues['publishing'] - origvalues.update(newphases) - return origvalues - - wrapfunction(localrepo.localrepository, 'listkeys', _listkeys) - wrappedlistkeys = True - heads = list((set(newheads) | set(heads)) - set(scratchheads)) - result = orig(repo, source, heads=heads, - bundlecaps=bundlecaps, **kwargs) - finally: - if wrappedchangegrouppart: - exchange.getbundle2partsmapping['changegroup'] = oldchangegrouppart - if wrappedlistkeys: - unwrapfunction(localrepo.localrepository, 'listkeys', _listkeys) - return result - -def _lookupwrap(orig): - def _lookup(repo, proto, key): - localkey = encoding.tolocal(key) - - if isinstance(localkey, str) and _scratchbranchmatcher(localkey): - scratchnode = repo.bundlestore.index.getnode(localkey) - if scratchnode: - return "%s %s\n" % (1, scratchnode) - else: - return "%s %s\n" % (0, 'scratch branch %s not found' % localkey) - else: - try: - r = hex(repo.lookup(localkey)) - return "%s %s\n" % (1, r) - except Exception as inst: - if repo.bundlestore.index.getbundle(localkey): - return "%s %s\n" % (1, localkey) - else: - r = str(inst) - return "%s %s\n" % (0, r) - return _lookup - -def _decodebookmarks(stream): - sizeofjsonsize = struct.calcsize('>i') - size = struct.unpack('>i', stream.read(sizeofjsonsize))[0] - unicodedict = json.loads(stream.read(size)) - # python json module always returns unicode strings. We need to convert - # it back to bytes string - result = {} - for bookmark, node in unicodedict.iteritems(): - bookmark = bookmark.encode('ascii') - node = node.encode('ascii') - result[bookmark] = node - return result - -def _update(orig, ui, repo, node=None, rev=None, **opts): - if rev and node: - raise error.Abort(_("please specify just one revision")) - - if not opts.get('date') and (rev or node) not in repo: - mayberemote = rev or node - mayberemote = _tryhoist(ui, mayberemote) - dopull = False - kwargs = {} - if _scratchbranchmatcher(mayberemote): - dopull = True - kwargs['bookmark'] = [mayberemote] - elif len(mayberemote) == 40 and _maybehash(mayberemote): - dopull = True - kwargs['rev'] = [mayberemote] - - if dopull: - ui.warn( - _("'%s' does not exist locally - looking for it " + - "remotely...\n") % mayberemote) - # Try pulling node from remote repo - try: - cmdname = '^pull' - pullcmd = commands.table[cmdname][0] - pullopts = dict(opt[1:3] for opt in commands.table[cmdname][1]) - pullopts.update(kwargs) - pullcmd(ui, repo, **pullopts) - except Exception: - ui.warn(_('pull failed: %s\n') % sys.exc_info()[1]) - else: - ui.warn(_("'%s' found remotely\n") % mayberemote) - return orig(ui, repo, node, rev, **opts) - -def _pull(orig, ui, repo, source="default", **opts): - # Copy paste from `pull` command - source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch')) - - scratchbookmarks = {} - unfi = repo.unfiltered() - unknownnodes = [] - for rev in opts.get('rev', []): - if rev not in unfi: - unknownnodes.append(rev) - if opts.get('bookmark'): - bookmarks = [] - revs = opts.get('rev') or [] - for bookmark in opts.get('bookmark'): - if _scratchbranchmatcher(bookmark): - # rev is not known yet - # it will be fetched with listkeyspatterns next - scratchbookmarks[bookmark] = 'REVTOFETCH' - else: - bookmarks.append(bookmark) - - if scratchbookmarks: - other = hg.peer(repo, opts, source) - fetchedbookmarks = other.listkeyspatterns( - 'bookmarks', patterns=scratchbookmarks) - for bookmark in scratchbookmarks: - if bookmark not in fetchedbookmarks: - raise error.Abort('remote bookmark %s not found!' % - bookmark) - scratchbookmarks[bookmark] = fetchedbookmarks[bookmark] - revs.append(fetchedbookmarks[bookmark]) - opts['bookmark'] = bookmarks - opts['rev'] = revs - - try: - inhibitmod = extensions.find('inhibit') - except KeyError: - # Ignore if inhibit is not enabled - pass - else: - # Pulling revisions that were filtered results in a error. - # Let's inhibit them - unfi = repo.unfiltered() - for rev in opts.get('rev', []): - try: - repo[rev] - except error.FilteredRepoLookupError: - node = unfi[rev].node() - inhibitmod.revive([repo.unfiltered()[node]]) - except error.RepoLookupError: - pass - - if scratchbookmarks or unknownnodes: - # Set anyincoming to True - wrapfunction(discovery, 'findcommonincoming', _findcommonincoming) - try: - # Remote scratch bookmarks will be deleted because remotenames doesn't - # know about them. Let's save it before pull and restore after - remotescratchbookmarks = _readscratchremotebookmarks(ui, repo, source) - result = orig(ui, repo, source, **opts) - # TODO(stash): race condition is possible - # if scratch bookmarks was updated right after orig. - # But that's unlikely and shouldn't be harmful. - if common.isremotebooksenabled(ui): - remotescratchbookmarks.update(scratchbookmarks) - _saveremotebookmarks(repo, remotescratchbookmarks, source) - else: - _savelocalbookmarks(repo, scratchbookmarks) - return result - finally: - if scratchbookmarks: - unwrapfunction(discovery, 'findcommonincoming') - -def _readscratchremotebookmarks(ui, repo, other): - if common.isremotebooksenabled(ui): - remotenamesext = extensions.find('remotenames') - remotepath = remotenamesext.activepath(repo.ui, other) - result = {} - # Let's refresh remotenames to make sure we have it up to date - # Seems that `repo.names['remotebookmarks']` may return stale bookmarks - # and it results in deleting scratch bookmarks. Our best guess how to - # fix it is to use `clearnames()` - repo._remotenames.clearnames() - for remotebookmark in repo.names['remotebookmarks'].listnames(repo): - path, bookname = remotenamesext.splitremotename(remotebookmark) - if path == remotepath and _scratchbranchmatcher(bookname): - nodes = repo.names['remotebookmarks'].nodes(repo, - remotebookmark) - if nodes: - result[bookname] = hex(nodes[0]) - return result - else: - return {} - -def _saveremotebookmarks(repo, newbookmarks, remote): - remotenamesext = extensions.find('remotenames') - remotepath = remotenamesext.activepath(repo.ui, remote) - branches = defaultdict(list) - bookmarks = {} - remotenames = remotenamesext.readremotenames(repo) - for hexnode, nametype, remote, rname in remotenames: - if remote != remotepath: - continue - if nametype == 'bookmarks': - if rname in newbookmarks: - # It's possible if we have a normal bookmark that matches - # scratch branch pattern. In this case just use the current - # bookmark node - del newbookmarks[rname] - bookmarks[rname] = hexnode - elif nametype == 'branches': - # saveremotenames expects 20 byte binary nodes for branches - branches[rname].append(bin(hexnode)) - - for bookmark, hexnode in newbookmarks.iteritems(): - bookmarks[bookmark] = hexnode - remotenamesext.saveremotenames(repo, remotepath, branches, bookmarks) - -def _savelocalbookmarks(repo, bookmarks): - if not bookmarks: - return - with repo.wlock(), repo.lock(), repo.transaction('bookmark') as tr: - changes = [] - for scratchbook, node in bookmarks.iteritems(): - changectx = repo[node] - changes.append((scratchbook, changectx.node())) - repo._bookmarks.applychanges(repo, tr, changes) - -def _findcommonincoming(orig, *args, **kwargs): - common, inc, remoteheads = orig(*args, **kwargs) - return common, True, remoteheads - -def _push(orig, ui, repo, dest=None, *args, **opts): - bookmark = opts.get('to') or '' - create = opts.get('create') or False - - oldphasemove = None - overrides = {(experimental, configbookmark): bookmark, - (experimental, configcreate): create} - - with ui.configoverride(overrides, 'infinitepush'): - scratchpush = opts.get('bundle_store') - if _scratchbranchmatcher(bookmark): - # Hack to fix interaction with remotenames. Remotenames push - # '--to' bookmark to the server but we don't want to push scratch - # bookmark to the server. Let's delete '--to' and '--create' and - # also set allow_anon to True (because if --to is not set - # remotenames will think that we are pushing anonymoush head) - if 'to' in opts: - del opts['to'] - if 'create' in opts: - del opts['create'] - opts['allow_anon'] = True - scratchpush = True - # bundle2 can be sent back after push (for example, bundle2 - # containing `pushkey` part to update bookmarks) - ui.setconfig(experimental, 'bundle2.pushback', True) - - ui.setconfig(experimental, confignonforwardmove, - opts.get('non_forward_move'), '--non-forward-move') - if scratchpush: - ui.setconfig(experimental, configscratchpush, True) - oldphasemove = wrapfunction(exchange, - '_localphasemove', - _phasemove) - # Copy-paste from `push` command - path = ui.paths.getpath(dest, default=('default-push', 'default')) - if not path: - raise error.Abort(_('default repository not configured!'), - hint=_("see 'hg help config.paths'")) - destpath = path.pushloc or path.loc - if destpath.startswith('svn+') and scratchpush: - raise error.Abort('infinite push does not work with svn repo', - hint='did you forget to `hg push default`?') - # Remote scratch bookmarks will be deleted because remotenames doesn't - # know about them. Let's save it before push and restore after - remotescratchbookmarks = _readscratchremotebookmarks(ui, repo, destpath) - result = orig(ui, repo, dest, *args, **opts) - if common.isremotebooksenabled(ui): - if bookmark and scratchpush: - other = hg.peer(repo, opts, destpath) - fetchedbookmarks = other.listkeyspatterns('bookmarks', - patterns=[bookmark]) - remotescratchbookmarks.update(fetchedbookmarks) - _saveremotebookmarks(repo, remotescratchbookmarks, destpath) - if oldphasemove: - exchange._localphasemove = oldphasemove - return result - -def _deleteinfinitepushbookmarks(ui, repo, path, names): - """Prune remote names by removing the bookmarks we don't want anymore, - then writing the result back to disk - """ - remotenamesext = extensions.find('remotenames') - - # remotename format is: - # (node, nametype ("branches" or "bookmarks"), remote, name) - nametype_idx = 1 - remote_idx = 2 - name_idx = 3 - remotenames = [remotename for remotename in \ - remotenamesext.readremotenames(repo) \ - if remotename[remote_idx] == path] - remote_bm_names = [remotename[name_idx] for remotename in \ - remotenames if remotename[nametype_idx] == "bookmarks"] - - for name in names: - if name not in remote_bm_names: - raise error.Abort(_("infinitepush bookmark '{}' does not exist " - "in path '{}'").format(name, path)) - - bookmarks = {} - branches = defaultdict(list) - for node, nametype, remote, name in remotenames: - if nametype == "bookmarks" and name not in names: - bookmarks[name] = node - elif nametype == "branches": - # saveremotenames wants binary nodes for branches - branches[name].append(bin(node)) - - remotenamesext.saveremotenames(repo, path, branches, bookmarks) - -def _phasemove(orig, pushop, nodes, phase=phases.public): - """prevent commits from being marked public - - Since these are going to a scratch branch, they aren't really being - published.""" - - if phase != phases.public: - orig(pushop, nodes, phase) - -@exchange.b2partsgenerator(scratchbranchparttype) -def partgen(pushop, bundler): - bookmark = pushop.ui.config(experimental, configbookmark) - create = pushop.ui.configbool(experimental, configcreate) - scratchpush = pushop.ui.configbool(experimental, configscratchpush) - if 'changesets' in pushop.stepsdone or not scratchpush: - return - - if scratchbranchparttype not in bundle2.bundle2caps(pushop.remote): - return - - pushop.stepsdone.add('changesets') - pushop.stepsdone.add('treepack') - if not pushop.outgoing.missing: - pushop.ui.status(_('no changes found\n')) - pushop.cgresult = 0 - return - - # This parameter tells the server that the following bundle is an - # infinitepush. This let's it switch the part processing to our infinitepush - # code path. - bundler.addparam("infinitepush", "True") - - nonforwardmove = pushop.force or pushop.ui.configbool(experimental, - confignonforwardmove) - scratchparts = getscratchbranchparts(pushop.repo, - pushop.remote, - pushop.outgoing, - nonforwardmove, - pushop.ui, - bookmark, - create) - - for scratchpart in scratchparts: - bundler.addpart(scratchpart) - - def handlereply(op): - # server either succeeds or aborts; no code to read - pushop.cgresult = 1 - - return handlereply - -bundle2.capabilities[scratchbranchparttype] = () -bundle2.capabilities[scratchbookmarksparttype] = () - -def _getrevs(bundle, oldnode, force, bookmark): - 'extracts and validates the revs to be imported' - revs = [bundle[r] for r in bundle.revs('sort(bundle())')] - - # new bookmark - if oldnode is None: - return revs - - # Fast forward update - if oldnode in bundle and list(bundle.set('bundle() & %s::', oldnode)): - return revs - - # Forced non-fast forward update - if force: - return revs - else: - raise error.Abort(_('non-forward push'), - hint=_('use --non-forward-move to override')) - -@contextlib.contextmanager -def logservicecall(logger, service, **kwargs): - start = time.time() - logger(service, eventtype='start', **kwargs) - try: - yield - logger(service, eventtype='success', - elapsedms=(time.time() - start) * 1000, **kwargs) - except Exception as e: - logger(service, eventtype='failure', - elapsedms=(time.time() - start) * 1000, errormsg=str(e), - **kwargs) - raise - -def _getorcreateinfinitepushlogger(op): - logger = op.records['infinitepushlogger'] - if not logger: - ui = op.repo.ui - try: - username = util.getuser() - except Exception: - username = 'unknown' - # Generate random request id to be able to find all logged entries - # for the same request. Since requestid is pseudo-generated it may - # not be unique, but we assume that (hostname, username, requestid) - # is unique. - random.seed() - requestid = random.randint(0, 2000000000) - hostname = socket.gethostname() - logger = partial(ui.log, 'infinitepush', user=username, - requestid=requestid, hostname=hostname, - reponame=ui.config('infinitepush', 'reponame')) - op.records.add('infinitepushlogger', logger) - else: - logger = logger[0] - return logger - -def processparts(orig, repo, op, unbundler): - if unbundler.params.get('infinitepush') != 'True': - return orig(repo, op, unbundler) - - handleallparts = repo.ui.configbool('infinitepush', 'storeallparts') - - partforwardingwhitelist = [] - try: - treemfmod = extensions.find('treemanifest') - partforwardingwhitelist.append(treemfmod.TREEGROUP_PARTTYPE2) - except KeyError: - pass - - bundler = bundle2.bundle20(repo.ui) - cgparams = None - scratchbookpart = None - with bundle2.partiterator(repo, op, unbundler) as parts: - for part in parts: - bundlepart = None - if part.type == 'replycaps': - # This configures the current operation to allow reply parts. - bundle2._processpart(op, part) - elif part.type == scratchbranchparttype: - # Scratch branch parts need to be converted to normal - # changegroup parts, and the extra parameters stored for later - # when we upload to the store. Eventually those parameters will - # be put on the actual bundle instead of this part, then we can - # send a vanilla changegroup instead of the scratchbranch part. - cgversion = part.params.get('cgversion', '01') - bundlepart = bundle2.bundlepart('changegroup', data=part.read()) - bundlepart.addparam('version', cgversion) - cgparams = part.params - - # If we're not dumping all parts into the new bundle, we need to - # alert the future pushkey and phase-heads handler to skip - # the part. - if not handleallparts: - op.records.add(scratchbranchparttype + '_skippushkey', True) - op.records.add(scratchbranchparttype + '_skipphaseheads', - True) - elif part.type == scratchbookmarksparttype: - # Save this for later processing. Details below. - # - # Upstream https://phab.mercurial-scm.org/D1389 and its - # follow-ups stop part.seek support to reduce memory usage - # (https://bz.mercurial-scm.org/5691). So we need to copy - # the part so it can be consumed later. - scratchbookpart = copiedpart(part) - else: - if handleallparts or part.type in partforwardingwhitelist: - # Ideally we would not process any parts, and instead just - # forward them to the bundle for storage, but since this - # differs from previous behavior, we need to put it behind a - # config flag for incremental rollout. - bundlepart = bundle2.bundlepart(part.type, data=part.read()) - for key, value in part.params.iteritems(): - bundlepart.addparam(key, value) - - # Certain parts require a response - if part.type == 'pushkey': - if op.reply is not None: - rpart = op.reply.newpart('reply:pushkey') - rpart.addparam('in-reply-to', str(part.id), - mandatory=False) - rpart.addparam('return', '1', mandatory=False) - else: - bundle2._processpart(op, part) - - if handleallparts: - op.records.add(part.type, { - 'return': 1, - }) - if bundlepart: - bundler.addpart(bundlepart) - - # If commits were sent, store them - if cgparams: - buf = util.chunkbuffer(bundler.getchunks()) - fd, bundlefile = tempfile.mkstemp() - try: - try: - fp = os.fdopen(fd, 'wb') - fp.write(buf.read()) - finally: - fp.close() - storebundle(op, cgparams, bundlefile) - finally: - try: - os.unlink(bundlefile) - except Exception: - # we would rather see the original exception - pass - - # The scratch bookmark part is sent as part of a push backup. It needs to be - # processed after the main bundle has been stored, so that any commits it - # references are available in the store. - if scratchbookpart: - bundle2._processpart(op, scratchbookpart) - -def storebundle(op, params, bundlefile): - log = _getorcreateinfinitepushlogger(op) - parthandlerstart = time.time() - log(scratchbranchparttype, eventtype='start') - index = op.repo.bundlestore.index - store = op.repo.bundlestore.store - op.records.add(scratchbranchparttype + '_skippushkey', True) - - bundle = None - try: # guards bundle - bundlepath = "bundle:%s+%s" % (op.repo.root, bundlefile) - bundle = repository(op.repo.ui, bundlepath) - - bookmark = params.get('bookmark') - bookprevnode = params.get('bookprevnode', '') - create = params.get('create') - force = params.get('force') - - if bookmark: - oldnode = index.getnode(bookmark) - - if not oldnode and not create: - raise error.Abort("unknown bookmark %s" % bookmark, - hint="use --create if you want to create one") - else: - oldnode = None - bundleheads = bundle.revs('heads(bundle())') - if bookmark and len(bundleheads) > 1: - raise error.Abort( - _('cannot push more than one head to a scratch branch')) - - revs = _getrevs(bundle, oldnode, force, bookmark) - - # Notify the user of what is being pushed - plural = 's' if len(revs) > 1 else '' - op.repo.ui.warn(_("pushing %s commit%s:\n") % (len(revs), plural)) - maxoutput = 10 - for i in range(0, min(len(revs), maxoutput)): - firstline = bundle[revs[i]].description().split('\n')[0][:50] - op.repo.ui.warn((" %s %s\n") % (revs[i], firstline)) - - if len(revs) > maxoutput + 1: - op.repo.ui.warn((" ...\n")) - firstline = bundle[revs[-1]].description().split('\n')[0][:50] - op.repo.ui.warn((" %s %s\n") % (revs[-1], firstline)) - - nodesctx = [bundle[rev] for rev in revs] - inindex = lambda rev: bool(index.getbundle(bundle[rev].hex())) - if bundleheads: - newheadscount = sum(not inindex(rev) for rev in bundleheads) - else: - newheadscount = 0 - # If there's a bookmark specified, there should be only one head, - # so we choose the last node, which will be that head. - # If a bug or malicious client allows there to be a bookmark - # with multiple heads, we will place the bookmark on the last head. - bookmarknode = nodesctx[-1].hex() if nodesctx else None - key = None - if newheadscount: - with open(bundlefile, 'r') as f: - bundledata = f.read() - with logservicecall(log, 'bundlestore', - bundlesize=len(bundledata)): - bundlesizelimit = 100 * 1024 * 1024 # 100 MB - if len(bundledata) > bundlesizelimit: - error_msg = ('bundle is too big: %d bytes. ' + - 'max allowed size is 100 MB') - raise error.Abort(error_msg % (len(bundledata),)) - key = store.write(bundledata) - - with logservicecall(log, 'index', newheadscount=newheadscount), index: - if key: - index.addbundle(key, nodesctx) - if bookmark: - index.addbookmark(bookmark, bookmarknode) - _maybeaddpushbackpart(op, bookmark, bookmarknode, - bookprevnode, params) - log(scratchbranchparttype, eventtype='success', - elapsedms=(time.time() - parthandlerstart) * 1000) - - fillmetadatabranchpattern = op.repo.ui.config( - 'infinitepush', 'fillmetadatabranchpattern', '') - if bookmark and fillmetadatabranchpattern: - __, __, matcher = util.stringmatcher(fillmetadatabranchpattern) - if matcher(bookmark): - _asyncsavemetadata(op.repo.root, - [ctx.hex() for ctx in nodesctx]) - except Exception as e: - log(scratchbranchparttype, eventtype='failure', - elapsedms=(time.time() - parthandlerstart) * 1000, - errormsg=str(e)) - raise - finally: - if bundle: - bundle.close() - -@bundle2.b2streamparamhandler('infinitepush') -def processinfinitepush(unbundler, param, value): - """ process the bundle2 stream level parameter containing whether this push - is an infinitepush or not. """ - if value and unbundler.ui.configbool('infinitepush', - 'bundle-stream', False): - pass - -@bundle2.parthandler(scratchbranchparttype, - ('bookmark', 'bookprevnode' 'create', 'force', - 'pushbackbookmarks', 'cgversion')) -def bundle2scratchbranch(op, part): - '''unbundle a bundle2 part containing a changegroup to store''' - - bundler = bundle2.bundle20(op.repo.ui) - cgversion = part.params.get('cgversion', '01') - cgpart = bundle2.bundlepart('changegroup', data=part.read()) - cgpart.addparam('version', cgversion) - bundler.addpart(cgpart) - buf = util.chunkbuffer(bundler.getchunks()) - - fd, bundlefile = tempfile.mkstemp() - try: - try: - fp = os.fdopen(fd, 'wb') - fp.write(buf.read()) - finally: - fp.close() - storebundle(op, part.params, bundlefile) - finally: - try: - os.unlink(bundlefile) - except OSError as e: - if e.errno != errno.ENOENT: - raise - - return 1 - -@bundle2.parthandler(scratchbookmarksparttype) -def bundle2scratchbookmarks(op, part): - '''Handler deletes bookmarks first then adds new bookmarks. - ''' - index = op.repo.bundlestore.index - decodedbookmarks = _decodebookmarks(part) - toinsert = {} - todelete = [] - for bookmark, node in decodedbookmarks.iteritems(): - if node: - toinsert[bookmark] = node - else: - todelete.append(bookmark) - log = _getorcreateinfinitepushlogger(op) - with logservicecall(log, scratchbookmarksparttype), index: - if todelete: - index.deletebookmarks(todelete) - if toinsert: - index.addmanybookmarks(toinsert) - -def _maybeaddpushbackpart(op, bookmark, newnode, oldnode, params): - if params.get('pushbackbookmarks'): - if op.reply and 'pushback' in op.reply.capabilities: - params = { - 'namespace': 'bookmarks', - 'key': bookmark, - 'new': newnode, - 'old': oldnode, - } - op.reply.newpart('pushkey', mandatoryparams=params.iteritems()) - -def bundle2pushkey(orig, op, part): - '''Wrapper of bundle2.handlepushkey() - - The only goal is to skip calling the original function if flag is set. - It's set if infinitepush push is happening. - ''' - if op.records[scratchbranchparttype + '_skippushkey']: - if op.reply is not None: - rpart = op.reply.newpart('reply:pushkey') - rpart.addparam('in-reply-to', str(part.id), mandatory=False) - rpart.addparam('return', '1', mandatory=False) - return 1 - - return orig(op, part) - -def bundle2handlephases(orig, op, part): - '''Wrapper of bundle2.handlephases() - - The only goal is to skip calling the original function if flag is set. - It's set if infinitepush push is happening. - ''' - - if op.records[scratchbranchparttype + '_skipphaseheads']: - return - - return orig(op, part) - -def _asyncsavemetadata(root, nodes): - '''starts a separate process that fills metadata for the nodes - - This function creates a separate process and doesn't wait for it's - completion. This was done to avoid slowing down pushes - ''' - - maxnodes = 50 - if len(nodes) > maxnodes: - return - nodesargs = [] - for node in nodes: - nodesargs.append('--node') - nodesargs.append(node) - with open(os.devnull, 'w+b') as devnull: - cmdline = [_hgexecutable(), 'debugfillinfinitepushmetadata', - '-R', root] + nodesargs - # Process will run in background. We don't care about the return code - subprocess.Popen(cmdline, close_fds=True, shell=False, - stdin=devnull, stdout=devnull, stderr=devnull) diff --git a/infinitepush/backupcommands.py b/infinitepush/backupcommands.py deleted file mode 100644 --- a/infinitepush/backupcommands.py +++ /dev/null @@ -1,984 +0,0 @@ -# Copyright 2017 Facebook, Inc. -# -# This software may be used and distributed according to the terms of the -# 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 - - # Backup at most maxheadstobackup heads, other heads are ignored. - # Negative number means backup everything. - maxheadstobackup = -1 - - # Nodes that should not be backed up. Ancestors of these nodes won't be - # backed up either - dontbackupnodes = [] - - # Special option that may be used to trigger re-backuping. For example, - # if there was a bug in infinitepush backups, then changing the value of - # this option will force all clients to make a "clean" backup - backupgeneration = 0 - - # Hostname value to use. If not specified then socket.gethostname() will - # be used - hostname = '' - - # Enable reporting of infinitepush backup status as a summary at the end - # of smartlog. - enablestatus = False - - # Whether or not to save information about the latest successful backup. - # This information includes the local revision number and unix timestamp - # of the last time we successfully made a backup. - savelatestbackupinfo = False -""" - -from __future__ import absolute_import -import errno -import json -import os -import re -import socket -import stat -import subprocess -import time - -from .bundleparts import ( - getscratchbookmarkspart, - getscratchbranchparts, -) -from mercurial import ( - bundle2, - changegroup, - commands, - discovery, - dispatch, - encoding, - error, - extensions, - hg, - localrepo, - lock as lockmod, - phases, - registrar, - scmutil, - util, -) - -from collections import defaultdict, namedtuple -from mercurial import policy -from mercurial.extensions import wrapfunction, unwrapfunction -from mercurial.node import bin, hex, nullrev, short -from mercurial.i18n import _ - -from hgext3rd import shareutil - -osutil = policy.importmod(r'osutil') - -cmdtable = {} -command = registrar.command(cmdtable) -revsetpredicate = registrar.revsetpredicate() -templatekeyword = registrar.templatekeyword() - -backupbookmarktuple = namedtuple('backupbookmarktuple', - ['hostname', 'reporoot', 'localbookmark']) - -class backupstate(object): - def __init__(self): - self.heads = set() - self.localbookmarks = {} - - def empty(self): - return not self.heads and not self.localbookmarks - -class WrongPermissionsException(Exception): - def __init__(self, logdir): - self.logdir = logdir - -restoreoptions = [ - ('', 'reporoot', '', 'root of the repo to restore'), - ('', 'user', '', 'user who ran the backup'), - ('', 'hostname', '', 'hostname of the repo to restore'), -] - -_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): - """ - Pushes commits, bookmarks and heads to infinitepush. - New non-extinct commits are saved since the last `hg pushbackup` - or since 0 revision if this backup is the first. - Local bookmarks are saved remotely as: - infinitepush/backups/USERNAME/HOST/REPOROOT/bookmarks/LOCAL_BOOKMARK - Local heads are saved remotely as: - infinitepush/backups/USERNAME/HOST/REPOROOT/heads/HEAD_HASH - """ - - if opts.get('background'): - _dobackgroundbackup(ui, repo, dest) - return 0 - - try: - # Wait at most 30 seconds, because that's the average backup time - timeout = 30 - srcrepo = shareutil.getsrcrepo(repo) - with lockmod.lock(srcrepo.vfs, _backuplockname, timeout=timeout): - return _dobackup(ui, repo, dest, **opts) - except error.LockHeld as e: - if e.errno == errno.ETIMEDOUT: - ui.warn(_('timeout waiting on backup lock\n')) - return 0 - else: - raise - -@command('pullbackup', restoreoptions) -def restore(ui, repo, dest=None, **opts): - """ - Pulls commits from infinitepush that were previously saved with - `hg pushbackup`. - If user has only one backup for the `dest` repo then it will be restored. - But user may have backed up many local repos that points to `dest` repo. - These local repos may reside on different hosts or in different - repo roots. It makes restore ambiguous; `--reporoot` and `--hostname` - options are used to disambiguate. - """ - - other = _getremote(repo, ui, dest, **opts) - - sourcereporoot = opts.get('reporoot') - sourcehostname = opts.get('hostname') - namingmgr = BackupBookmarkNamingManager(ui, repo, opts.get('user')) - allbackupstates = _downloadbackupstate(ui, other, sourcereporoot, - sourcehostname, namingmgr) - if len(allbackupstates) == 0: - ui.warn(_('no backups found!')) - return 1 - _checkbackupstates(allbackupstates) - - __, backupstate = allbackupstates.popitem() - pullcmd, pullopts = _getcommandandoptions('^pull') - # pull backuped heads and nodes that are pointed by bookmarks - pullopts['rev'] = list(backupstate.heads | - set(backupstate.localbookmarks.values())) - if dest: - pullopts['source'] = dest - result = pullcmd(ui, repo, **pullopts) - - with repo.wlock(), repo.lock(), repo.transaction('bookmark') as tr: - changes = [] - for book, hexnode in backupstate.localbookmarks.iteritems(): - if hexnode in repo: - changes.append((book, bin(hexnode))) - else: - ui.warn(_('%s not found, not creating %s bookmark') % - (hexnode, book)) - repo._bookmarks.applychanges(repo, tr, changes) - - # manually write local backup state and flag to not autobackup - # just after we restored, which would be pointless - _writelocalbackupstate(repo.vfs, - list(backupstate.heads), - backupstate.localbookmarks) - repo.ignoreautobackup = True - - return result - -@command('getavailablebackups', - [('', 'user', '', _('username, defaults to current user')), - ('', 'json', None, _('print available backups in json format'))]) -def getavailablebackups(ui, repo, dest=None, **opts): - other = _getremote(repo, ui, dest, **opts) - - sourcereporoot = opts.get('reporoot') - sourcehostname = opts.get('hostname') - - namingmgr = BackupBookmarkNamingManager(ui, repo, opts.get('user')) - allbackupstates = _downloadbackupstate(ui, other, sourcereporoot, - sourcehostname, namingmgr) - - if opts.get('json'): - jsondict = defaultdict(list) - for hostname, reporoot in allbackupstates.keys(): - jsondict[hostname].append(reporoot) - # make sure the output is sorted. That's not an efficient way to - # keep list sorted but we don't have that many backups. - jsondict[hostname].sort() - ui.write('%s\n' % json.dumps(jsondict)) - else: - if not allbackupstates: - ui.write(_('no backups available for %s\n') % namingmgr.username) - - ui.write(_('user %s has %d available backups:\n') % - (namingmgr.username, len(allbackupstates))) - - for hostname, reporoot in sorted(allbackupstates.keys()): - ui.write(_('%s on %s\n') % (reporoot, hostname)) - -@command('debugcheckbackup', - [('', 'all', None, _('check all backups that user have')), - ] + restoreoptions) -def checkbackup(ui, repo, dest=None, **opts): - """ - Checks that all the nodes that backup needs are available in bundlestore - This command can check either specific backup (see restoreoptions) or all - backups for the user - """ - - sourcereporoot = opts.get('reporoot') - sourcehostname = opts.get('hostname') - - other = _getremote(repo, ui, dest, **opts) - namingmgr = BackupBookmarkNamingManager(ui, repo, opts.get('user')) - allbackupstates = _downloadbackupstate(ui, other, sourcereporoot, - sourcehostname, namingmgr) - if not opts.get('all'): - _checkbackupstates(allbackupstates) - - ret = 0 - while allbackupstates: - key, bkpstate = allbackupstates.popitem() - ui.status(_('checking %s on %s\n') % (key[1], key[0])) - if not _dobackupcheck(bkpstate, ui, repo, dest, **opts): - ret = 255 - return ret - -@command('debugwaitbackup', [('', 'timeout', '', 'timeout value')]) -def waitbackup(ui, repo, timeout): - try: - if timeout: - timeout = int(timeout) - else: - timeout = -1 - except ValueError: - raise error.Abort('timeout should be integer') - - try: - repo = shareutil.getsrcrepo(repo) - with lockmod.lock(repo.vfs, _backuplockname, timeout=timeout): - pass - except error.LockHeld as e: - if e.errno == errno.ETIMEDOUT: - raise error.Abort(_('timeout while waiting for backup')) - raise - -@command('isbackedup', - [('r', 'rev', [], _('show the specified revision or revset'), _('REV'))]) -def isbackedup(ui, repo, **opts): - """checks if commit was backed up to infinitepush - - If no revision are specified then it checks working copy parent - """ - - revs = opts.get('rev') - if not revs: - revs = ['.'] - bkpstate = _readlocalbackupstate(ui, repo) - unfi = repo.unfiltered() - backeduprevs = unfi.revs('draft() and ::%ls', bkpstate.heads) - for r in scmutil.revrange(unfi, revs): - ui.write(_(unfi[r].hex() + ' ')) - ui.write(_('backed up' if r in backeduprevs else 'not backed up')) - ui.write(_('\n')) - -@revsetpredicate('backedup') -def backedup(repo, subset, x): - """Draft changesets that have been backed up by infinitepush""" - unfi = repo.unfiltered() - bkpstate = _readlocalbackupstate(repo.ui, repo) - return subset & unfi.revs('draft() and ::%ls and not hidden()', - bkpstate.heads) - -@revsetpredicate('notbackedup') -def notbackedup(repo, subset, x): - """Changesets that have not yet been backed up by infinitepush""" - bkpstate = _readlocalbackupstate(repo.ui, repo) - bkpheads = set(bkpstate.heads) - candidates = set(_backupheads(repo.ui, repo)) - notbackeduprevs = set() - # Find all revisions that are ancestors of the expected backup heads, - # stopping when we reach either a public commit or a known backup head. - while candidates: - candidate = candidates.pop() - if candidate not in bkpheads: - ctx = repo[candidate] - rev = ctx.rev() - if rev not in notbackeduprevs and ctx.phase() != phases.public: - # This rev may not have been backed up. Record it, and add its - # parents as candidates. - notbackeduprevs.add(rev) - candidates.update([p.hex() for p in ctx.parents()]) - if notbackeduprevs: - # Some revisions in this set may actually have been backed up by - # virtue of being an ancestor of a different backup head, which may - # have been hidden since the backup was made. Find these and remove - # them from the set. - unfi = repo.unfiltered() - candidates = bkpheads - while candidates: - candidate = candidates.pop() - if candidate in unfi: - ctx = unfi[candidate] - if ctx.phase() != phases.public: - notbackeduprevs.discard(ctx.rev()) - candidates.update([p.hex() for p in ctx.parents()]) - return subset & notbackeduprevs - -@templatekeyword('backingup') -def backingup(repo, ctx, **args): - """Whether infinitepush is currently backing up commits.""" - # If the backup lock exists then a backup should be in progress. - srcrepo = shareutil.getsrcrepo(repo) - return srcrepo.vfs.lexists(_backuplockname) - -def smartlogsummary(ui, repo): - if not ui.configbool('infinitepushbackup', 'enablestatus'): - return - - # Don't output the summary if a backup is currently in progress. - srcrepo = shareutil.getsrcrepo(repo) - if srcrepo.vfs.lexists(_backuplockname): - return - - unbackeduprevs = repo.revs('notbackedup()') - - # Count the number of changesets that haven't been backed up for 10 minutes. - # If there is only one, also print out its hash. - backuptime = time.time() - 10 * 60 # 10 minutes ago - count = 0 - singleunbackeduprev = None - for rev in unbackeduprevs: - if repo[rev].date()[0] <= backuptime: - singleunbackeduprev = rev - count += 1 - if count > 0: - if count > 1: - ui.warn(_('note: %d changesets are not backed up.\n') % count) - else: - ui.warn(_('note: changeset %s is not backed up.\n') % - short(repo[singleunbackeduprev].node())) - 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) \ - and not getattr(repo, 'ignoreautobackup', False): - lui.debug("starting infinitepush autobackup in the background\n") - _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', - 'maxheadstobackup', -1) - - revset = 'heads(draft()) & not obsolete()' - - backupheads = [ctx.hex() for ctx in repo.set(revset)] - if maxheadstobackup > 0: - backupheads = backupheads[-maxheadstobackup:] - elif maxheadstobackup == 0: - backupheads = [] - return set(backupheads) - -def _dobackup(ui, repo, dest, **opts): - ui.status(_('starting backup %s\n') % time.strftime('%H:%M:%S %d %b %Y %Z')) - start = time.time() - # to handle multiple working copies correctly - repo = shareutil.getsrcrepo(repo) - currentbkpgenerationvalue = _readbackupgenerationfile(repo.vfs) - newbkpgenerationvalue = ui.configint('infinitepushbackup', - 'backupgeneration', 0) - if currentbkpgenerationvalue != newbkpgenerationvalue: - # Unlinking local backup state will trigger re-backuping - _deletebackupstate(repo) - _writebackupgenerationfile(repo.vfs, newbkpgenerationvalue) - bkpstate = _readlocalbackupstate(ui, repo) - - # this variable stores the local store info (tip numeric revision and date) - # which we use to quickly tell if our backup is stale - afterbackupinfo = _getlocalinfo(repo) - - # This variable will store what heads will be saved in backup state file - # if backup finishes successfully - afterbackupheads = _backupheads(ui, repo) - other = _getremote(repo, ui, dest, **opts) - outgoing, badhexnodes = _getrevstobackup(repo, ui, other, - afterbackupheads - bkpstate.heads) - # If remotefilelog extension is enabled then there can be nodes that we - # can't backup. In this case let's remove them from afterbackupheads - afterbackupheads.difference_update(badhexnodes) - - # As afterbackupheads this variable stores what heads will be saved in - # backup state file if backup finishes successfully - afterbackuplocalbooks = _getlocalbookmarks(repo) - afterbackuplocalbooks = _filterbookmarks( - afterbackuplocalbooks, repo, afterbackupheads) - - newheads = afterbackupheads - bkpstate.heads - removedheads = bkpstate.heads - afterbackupheads - newbookmarks = _dictdiff(afterbackuplocalbooks, bkpstate.localbookmarks) - removedbookmarks = _dictdiff(bkpstate.localbookmarks, afterbackuplocalbooks) - - namingmgr = BackupBookmarkNamingManager(ui, repo) - bookmarkstobackup = _getbookmarkstobackup( - repo, newbookmarks, removedbookmarks, - newheads, removedheads, namingmgr) - - # Special case if backup state is empty. Clean all backup bookmarks from the - # server. - if bkpstate.empty(): - bookmarkstobackup[namingmgr.getbackupheadprefix()] = '' - bookmarkstobackup[namingmgr.getbackupbookmarkprefix()] = '' - - # Wrap deltaparent function to make sure that bundle takes less space - # See _deltaparent comments for details - wrapfunction(changegroup.cg2packer, 'deltaparent', _deltaparent) - try: - bundler = _createbundler(ui, repo, other) - bundler.addparam("infinitepush", "True") - backup = False - if outgoing and outgoing.missing: - backup = True - parts = getscratchbranchparts(repo, other, outgoing, - confignonforwardmove=False, - ui=ui, bookmark=None, - create=False) - for part in parts: - bundler.addpart(part) - - if bookmarkstobackup: - backup = True - bundler.addpart(getscratchbookmarkspart(other, bookmarkstobackup)) - - if backup: - _sendbundle(bundler, other) - _writelocalbackupstate(repo.vfs, afterbackupheads, - afterbackuplocalbooks) - if ui.config('infinitepushbackup', 'savelatestbackupinfo'): - _writelocalbackupinfo(repo.vfs, **afterbackupinfo) - else: - ui.status(_('nothing to backup\n')) - finally: - # cleanup ensures that all pipes are flushed - cleanup = getattr(other, '_cleanup', None) or getattr(other, 'cleanup') - try: - cleanup() - except Exception: - ui.warn(_('remote connection cleanup failed\n')) - ui.status(_('finished in %f seconds\n') % (time.time() - start)) - 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())) - if not remotehexnodes: - return True - other = _getremote(repo, ui, dest, **opts) - batch = other.iterbatch() - for hexnode in remotehexnodes: - batch.lookup(hexnode) - batch.submit() - lookupresults = batch.results() - i = 0 - try: - for i, r in enumerate(lookupresults): - # iterate over results to make it throw if revision - # was not found - pass - return True - except error.RepoError: - ui.warn(_('unknown revision %r\n') % remotehexnodes[i]) - return False - -_backuplatestinfofile = 'infinitepushlatestbackupinfo' -_backupstatefile = 'infinitepushbackupstate' -_backupgenerationfile = 'infinitepushbackupgeneration' - -# Common helper functions -def _getlocalinfo(repo): - localinfo = {} - localinfo['rev'] = repo[repo.changelog.tip()].rev() - localinfo['time'] = int(time.time()) - return localinfo - -def _getlocalbookmarks(repo): - localbookmarks = {} - for bookmark, node in repo._bookmarks.iteritems(): - hexnode = hex(node) - localbookmarks[bookmark] = hexnode - return localbookmarks - -def _filterbookmarks(localbookmarks, repo, headstobackup): - '''Filters out some bookmarks from being backed up - - Filters out bookmarks that do not point to ancestors of headstobackup or - public commits - ''' - - headrevstobackup = [repo[hexhead].rev() for hexhead in headstobackup] - ancestors = repo.changelog.ancestors(headrevstobackup, inclusive=True) - filteredbooks = {} - for bookmark, hexnode in localbookmarks.iteritems(): - if (repo[hexnode].rev() in ancestors or - repo[hexnode].phase() == phases.public): - filteredbooks[bookmark] = hexnode - return filteredbooks - -def _downloadbackupstate(ui, other, sourcereporoot, sourcehostname, namingmgr): - pattern = namingmgr.getcommonuserprefix() - fetchedbookmarks = other.listkeyspatterns('bookmarks', patterns=[pattern]) - allbackupstates = defaultdict(backupstate) - for book, hexnode in fetchedbookmarks.iteritems(): - parsed = _parsebackupbookmark(book, namingmgr) - if parsed: - if sourcereporoot and sourcereporoot != parsed.reporoot: - continue - if sourcehostname and sourcehostname != parsed.hostname: - continue - key = (parsed.hostname, parsed.reporoot) - if parsed.localbookmark: - bookname = parsed.localbookmark - allbackupstates[key].localbookmarks[bookname] = hexnode - else: - allbackupstates[key].heads.add(hexnode) - else: - ui.warn(_('wrong format of backup bookmark: %s') % book) - - return allbackupstates - -def _checkbackupstates(allbackupstates): - if len(allbackupstates) == 0: - raise error.Abort('no backups found!') - - hostnames = set(key[0] for key in allbackupstates.iterkeys()) - reporoots = set(key[1] for key in allbackupstates.iterkeys()) - - if len(hostnames) > 1: - raise error.Abort( - _('ambiguous hostname to restore: %s') % sorted(hostnames), - hint=_('set --hostname to disambiguate')) - - if len(reporoots) > 1: - raise error.Abort( - _('ambiguous repo root to restore: %s') % sorted(reporoots), - hint=_('set --reporoot to disambiguate')) - -class BackupBookmarkNamingManager(object): - def __init__(self, ui, repo, username=None): - self.ui = ui - self.repo = repo - if not username: - username = util.shortuser(ui.username()) - self.username = username - - self.hostname = self.ui.config('infinitepushbackup', 'hostname') - if not self.hostname: - self.hostname = socket.gethostname() - - def getcommonuserprefix(self): - return '/'.join((self._getcommonuserprefix(), '*')) - - def getcommonprefix(self): - return '/'.join((self._getcommonprefix(), '*')) - - def getbackupbookmarkprefix(self): - return '/'.join((self._getbackupbookmarkprefix(), '*')) - - def getbackupbookmarkname(self, bookmark): - bookmark = _escapebookmark(bookmark) - return '/'.join((self._getbackupbookmarkprefix(), bookmark)) - - def getbackupheadprefix(self): - return '/'.join((self._getbackupheadprefix(), '*')) - - def getbackupheadname(self, hexhead): - return '/'.join((self._getbackupheadprefix(), hexhead)) - - def _getbackupbookmarkprefix(self): - return '/'.join((self._getcommonprefix(), 'bookmarks')) - - def _getbackupheadprefix(self): - return '/'.join((self._getcommonprefix(), 'heads')) - - def _getcommonuserprefix(self): - return '/'.join(('infinitepush', 'backups', self.username)) - - def _getcommonprefix(self): - reporoot = self.repo.origroot - - result = '/'.join((self._getcommonuserprefix(), self.hostname)) - if not reporoot.startswith('/'): - result += '/' - result += reporoot - if result.endswith('/'): - result = result[:-1] - return result - -def _escapebookmark(bookmark): - ''' - If `bookmark` contains "bookmarks" as a substring then replace it with - "bookmarksbookmarks". This will make parsing remote bookmark name - unambigious. - ''' - - bookmark = encoding.fromlocal(bookmark) - return bookmark.replace('bookmarks', 'bookmarksbookmarks') - -def _unescapebookmark(bookmark): - bookmark = encoding.tolocal(bookmark) - return bookmark.replace('bookmarksbookmarks', 'bookmarks') - -def _getremote(repo, ui, dest, **opts): - path = ui.paths.getpath(dest, default=('infinitepush', 'default')) - if not path: - raise error.Abort(_('default repository not configured!'), - hint=_("see 'hg help config.paths'")) - dest = path.pushloc or path.loc - return hg.peer(repo, opts, dest) - -def _getcommandandoptions(command): - cmd = commands.table[command][0] - opts = dict(opt[1:3] for opt in commands.table[command][1]) - return cmd, opts - -# Backup helper functions - -def _deltaparent(orig, self, revlog, rev, p1, p2, prev): - # This version of deltaparent prefers p1 over prev to use less space - dp = revlog.deltaparent(rev) - if dp == nullrev and not revlog.storedeltachains: - # send full snapshot only if revlog configured to do so - return nullrev - return p1 - -def _getbookmarkstobackup(repo, newbookmarks, removedbookmarks, - newheads, removedheads, namingmgr): - bookmarkstobackup = {} - - for bookmark, hexnode in removedbookmarks.items(): - backupbookmark = namingmgr.getbackupbookmarkname(bookmark) - bookmarkstobackup[backupbookmark] = '' - - for bookmark, hexnode in newbookmarks.items(): - backupbookmark = namingmgr.getbackupbookmarkname(bookmark) - bookmarkstobackup[backupbookmark] = hexnode - - for hexhead in removedheads: - headbookmarksname = namingmgr.getbackupheadname(hexhead) - bookmarkstobackup[headbookmarksname] = '' - - for hexhead in newheads: - headbookmarksname = namingmgr.getbackupheadname(hexhead) - bookmarkstobackup[headbookmarksname] = hexhead - - return bookmarkstobackup - -def _createbundler(ui, repo, other): - bundler = bundle2.bundle20(ui, bundle2.bundle2caps(other)) - # Disallow pushback because we want to avoid taking repo locks. - # And we don't need pushback anyway - capsblob = bundle2.encodecaps(bundle2.getrepocaps(repo, - allowpushback=False)) - bundler.newpart('replycaps', data=capsblob) - return bundler - -def _sendbundle(bundler, other): - stream = util.chunkbuffer(bundler.getchunks()) - try: - other.unbundle(stream, ['force'], other.url()) - except error.BundleValueError as exc: - raise error.Abort(_('missing support for %s') % exc) - -def findcommonoutgoing(repo, ui, other, heads): - if heads: - # Avoid using remotenames fastheaddiscovery heuristic. It uses - # remotenames file to quickly find commonoutgoing set, but it can - # result in sending public commits to infinitepush servers. - # For example: - # - # o draft - # / - # o C1 - # | - # ... - # | - # o remote/master - # - # pushbackup in that case results in sending to the infinitepush server - # all public commits from 'remote/master' to C1. It increases size of - # the bundle + it may result in storing data about public commits - # in infinitepush table. - - with ui.configoverride({("remotenames", "fastheaddiscovery"): False}): - nodes = map(repo.changelog.node, heads) - return discovery.findcommonoutgoing(repo, other, onlyheads=nodes) - else: - return None - -def _getrevstobackup(repo, ui, other, headstobackup): - # In rare cases it's possible to have a local node without filelogs. - # This is possible if remotefilelog is enabled and if the node was - # stripped server-side. We want to filter out these bad nodes and all - # of their descendants. - badnodes = ui.configlist('infinitepushbackup', 'dontbackupnodes', []) - badnodes = [node for node in badnodes if node in repo] - badrevs = [repo[node].rev() for node in badnodes] - badnodesdescendants = repo.set('%ld::', badrevs) if badrevs else set() - badnodesdescendants = set(ctx.hex() for ctx in badnodesdescendants) - filteredheads = filter(lambda head: head in badnodesdescendants, - headstobackup) - - if filteredheads: - ui.warn(_('filtering nodes: %s\n') % filteredheads) - ui.log('infinitepushbackup', 'corrupted nodes found', - infinitepushbackupcorruptednodes='failure') - headstobackup = filter(lambda head: head not in badnodesdescendants, - headstobackup) - - revs = list(repo[hexnode].rev() for hexnode in headstobackup) - outgoing = findcommonoutgoing(repo, ui, other, revs) - nodeslimit = 1000 - if outgoing and len(outgoing.missing) > nodeslimit: - # trying to push too many nodes usually means that there is a bug - # somewhere. Let's be safe and avoid pushing too many nodes at once - raise error.Abort('trying to back up too many nodes: %d' % - (len(outgoing.missing),)) - return outgoing, set(filteredheads) - -def _localbackupstateexists(repo): - return repo.vfs.exists(_backupstatefile) - -def _deletebackupstate(repo): - return repo.vfs.tryunlink(_backupstatefile) - -def _readlocalbackupstate(ui, repo): - repo = shareutil.getsrcrepo(repo) - if not _localbackupstateexists(repo): - return backupstate() - - with repo.vfs(_backupstatefile) as f: - try: - state = json.loads(f.read()) - if (not isinstance(state['bookmarks'], dict) or - not isinstance(state['heads'], list)): - raise ValueError('bad types of bookmarks or heads') - - result = backupstate() - result.heads = set(map(str, state['heads'])) - result.localbookmarks = state['bookmarks'] - return result - except (ValueError, KeyError, TypeError) as e: - ui.warn(_('corrupt file: %s (%s)\n') % (_backupstatefile, e)) - return backupstate() - return backupstate() - -def _writelocalbackupstate(vfs, heads, bookmarks): - with vfs(_backupstatefile, 'w') as f: - f.write(json.dumps({'heads': list(heads), 'bookmarks': bookmarks})) - -def _readbackupgenerationfile(vfs): - try: - with vfs(_backupgenerationfile) as f: - return int(f.read()) - except (IOError, OSError, ValueError): - return 0 - -def _writebackupgenerationfile(vfs, backupgenerationvalue): - with vfs(_backupgenerationfile, 'w', atomictemp=True) as f: - f.write(str(backupgenerationvalue)) - -def _writelocalbackupinfo(vfs, rev, time): - with vfs(_backuplatestinfofile, 'w', atomictemp=True) as f: - f.write(('backuprevision=%d\nbackuptime=%d\n') % (rev, time)) - -# Restore helper functions -def _parsebackupbookmark(backupbookmark, namingmgr): - '''Parses backup bookmark and returns info about it - - Backup bookmark may represent either a local bookmark or a head. - Returns None if backup bookmark has wrong format or tuple. - First entry is a hostname where this bookmark came from. - Second entry is a root of the repo where this bookmark came from. - Third entry in a tuple is local bookmark if backup bookmark - represents a local bookmark and None otherwise. - ''' - - backupbookmarkprefix = namingmgr._getcommonuserprefix() - commonre = '^{0}/([-\w.]+)(/.*)'.format(re.escape(backupbookmarkprefix)) - bookmarkre = commonre + '/bookmarks/(.*)$' - headsre = commonre + '/heads/[a-f0-9]{40}$' - - match = re.search(bookmarkre, backupbookmark) - if not match: - match = re.search(headsre, backupbookmark) - if not match: - return None - # It's a local head not a local bookmark. - # That's why localbookmark is None - return backupbookmarktuple(hostname=match.group(1), - reporoot=match.group(2), - localbookmark=None) - - return backupbookmarktuple(hostname=match.group(1), - reporoot=match.group(2), - localbookmark=_unescapebookmark(match.group(3))) - -_timeformat = '%Y%m%d' - -def _getlogfilename(logdir, username, reponame): - '''Returns name of the log file for particular user and repo - - Different users have different directories inside logdir. Log filename - consists of reponame (basename of repo path) and current day - (see _timeformat). That means that two different repos with the same name - can share the same log file. This is not a big problem so we ignore it. - ''' - - currentday = time.strftime(_timeformat) - return os.path.join(logdir, username, reponame + currentday) - -def _removeoldlogfiles(userlogdir, reponame): - existinglogfiles = [] - for entry in osutil.listdir(userlogdir): - filename = entry[0] - fullpath = os.path.join(userlogdir, filename) - if filename.startswith(reponame) and os.path.isfile(fullpath): - try: - time.strptime(filename[len(reponame):], _timeformat) - except ValueError: - continue - existinglogfiles.append(filename) - - # _timeformat gives us a property that if we sort log file names in - # descending order then newer files are going to be in the beginning - existinglogfiles = sorted(existinglogfiles, reverse=True) - # Delete logs that are older than 5 days - maxlogfilenumber = 5 - if len(existinglogfiles) > maxlogfilenumber: - for filename in existinglogfiles[maxlogfilenumber:]: - os.unlink(os.path.join(userlogdir, filename)) - -def _checkcommonlogdir(logdir): - '''Checks permissions of the log directory - - We want log directory to actually be a directory, have restricting - deletion flag set (sticky bit) - ''' - - try: - st = os.stat(logdir) - return stat.S_ISDIR(st.st_mode) and st.st_mode & stat.S_ISVTX - except OSError: - # is raised by os.stat() - return False - -def _checkuserlogdir(userlogdir): - '''Checks permissions of the user log directory - - We want user log directory to be writable only by the user who created it - and be owned by `username` - ''' - - try: - st = os.stat(userlogdir) - # Check that `userlogdir` is owned by `username` - if os.getuid() != st.st_uid: - return False - return ((st.st_mode & (stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH)) == - stat.S_IWUSR) - except OSError: - # is raised by os.stat() - return False - -def _dictdiff(first, second): - '''Returns new dict that contains items from the first dict that are missing - from the second dict. - ''' - result = {} - for book, hexnode in first.items(): - if second.get(book) != hexnode: - result[book] = hexnode - return result diff --git a/infinitepush/bundleparts.py b/infinitepush/bundleparts.py deleted file mode 100644 --- a/infinitepush/bundleparts.py +++ /dev/null @@ -1,139 +0,0 @@ -# Copyright 2017 Facebook, Inc. -# -# This software may be used and distributed according to the terms of the -# GNU General Public License version 2 or any later version. - -from .common import ( - encodebookmarks, - isremotebooksenabled, -) -from mercurial import ( - bundle2, - changegroup, - error, - extensions, - revsetlang, - util, -) -from mercurial.i18n import _ - -scratchbranchparttype = 'b2x:infinitepush' -scratchbookmarksparttype = 'b2x:infinitepushscratchbookmarks' - -def getscratchbranchparts(repo, peer, outgoing, confignonforwardmove, - ui, bookmark, create): - if not outgoing.missing: - raise error.Abort(_('no commits to push')) - - if scratchbranchparttype not in bundle2.bundle2caps(peer): - raise error.Abort(_('no server support for %r') % scratchbranchparttype) - - _validaterevset(repo, revsetlang.formatspec('%ln', outgoing.missing), - bookmark) - - supportedversions = changegroup.supportedoutgoingversions(repo) - # Explicitly avoid using '01' changegroup version in infinitepush to - # support general delta - supportedversions.discard('01') - cgversion = min(supportedversions) - _handlelfs(repo, outgoing.missing) - cg = changegroup.makestream(repo, outgoing, cgversion, 'push') - - params = {} - params['cgversion'] = cgversion - if bookmark: - params['bookmark'] = bookmark - # 'prevbooknode' is necessary for pushkey reply part - params['bookprevnode'] = '' - if bookmark in repo: - params['bookprevnode'] = repo[bookmark].hex() - if create: - params['create'] = '1' - if confignonforwardmove: - params['force'] = '1' - - # Do not send pushback bundle2 part with bookmarks if remotenames extension - # is enabled. It will be handled manually in `_push()` - if not isremotebooksenabled(ui): - params['pushbackbookmarks'] = '1' - - parts = [] - - # .upper() marks this as a mandatory part: server will abort if there's no - # handler - parts.append(bundle2.bundlepart( - scratchbranchparttype.upper(), - advisoryparams=params.iteritems(), - data=cg)) - - try: - treemod = extensions.find('treemanifest') - mfnodes = [] - for node in outgoing.missing: - mfnodes.append(('', repo[node].manifestnode())) - - # Only include the tree parts if they all exist - if not repo.manifestlog.datastore.getmissing(mfnodes): - parts.append(treemod.createtreepackpart( - repo, outgoing, treemod.TREEGROUP_PARTTYPE2)) - except KeyError: - pass - - return parts - -def getscratchbookmarkspart(peer, bookmarks): - if scratchbookmarksparttype not in bundle2.bundle2caps(peer): - raise error.Abort( - _('no server support for %r') % scratchbookmarksparttype) - - return bundle2.bundlepart( - scratchbookmarksparttype.upper(), - data=encodebookmarks(bookmarks)) - -def _validaterevset(repo, revset, bookmark): - """Abort if the revs to be pushed aren't valid for a scratch branch.""" - if not repo.revs(revset): - raise error.Abort(_('nothing to push')) - if bookmark: - # Allow bundle with many heads only if no bookmark is specified - heads = repo.revs('heads(%r)', revset) - if len(heads) > 1: - raise error.Abort( - _('cannot push more than one head to a scratch branch')) - -def _handlelfs(repo, missing): - '''Special case if lfs is enabled - - If lfs is enabled then we need to call prepush hook - to make sure large files are uploaded to lfs - ''' - try: - lfsmod = extensions.find('lfs') - lfsmod.wrapper.uploadblobsfromrevs(repo, missing) - except KeyError: - # Ignore if lfs extension is not enabled - return - -class copiedpart(object): - """a copy of unbundlepart content that can be consumed later""" - - def __init__(self, part): - # copy "public properties" - self.type = part.type - self.id = part.id - self.mandatory = part.mandatory - self.mandatoryparams = part.mandatoryparams - self.advisoryparams = part.advisoryparams - self.params = part.params - self.mandatorykeys = part.mandatorykeys - # copy the buffer - self._io = util.stringio(part.read()) - - def consume(self): - return - - def read(self, size=None): - if size is None: - return self._io.read() - else: - return self._io.read(size) diff --git a/infinitepush/common.py b/infinitepush/common.py deleted file mode 100644 --- a/infinitepush/common.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright 2017 Facebook, Inc. -# -# This software may be used and distributed according to the terms of the -# GNU General Public License version 2 or any later version. - -import json -import os -import struct -import tempfile - -from mercurial import ( - error, - extensions, -) -from mercurial.node import hex - -def isremotebooksenabled(ui): - return ('remotenames' in extensions._extensions and - ui.configbool('remotenames', 'bookmarks')) - -def encodebookmarks(bookmarks): - encoded = {} - for bookmark, node in bookmarks.iteritems(): - encoded[bookmark] = node - dumped = json.dumps(encoded) - result = struct.pack('>i', len(dumped)) + dumped - return result - -def downloadbundle(repo, unknownbinhead): - index = repo.bundlestore.index - store = repo.bundlestore.store - bundleid = index.getbundle(hex(unknownbinhead)) - if bundleid is None: - raise error.Abort('%s head is not known' % hex(unknownbinhead)) - bundleraw = store.read(bundleid) - return _makebundlefromraw(bundleraw) - -def _makebundlefromraw(data): - fp = None - fd, bundlefile = tempfile.mkstemp() - try: # guards bundlefile - try: # guards fp - fp = os.fdopen(fd, 'wb') - fp.write(data) - finally: - fp.close() - except Exception: - try: - os.unlink(bundlefile) - except Exception: - # we would rather see the original exception - pass - raise - - return bundlefile diff --git a/infinitepush/fileindexapi.py b/infinitepush/fileindexapi.py deleted file mode 100644 --- a/infinitepush/fileindexapi.py +++ /dev/null @@ -1,107 +0,0 @@ -# Infinite push -# -# Copyright 2016 Facebook, Inc. -# -# This software may be used and distributed according to the terms of the -# GNU General Public License version 2 or any later version. -""" - [infinitepush] - # Server-side option. Used only if indextype=disk. - # Filesystem path to the index store - indexpath = PATH -""" - -import os - -from indexapi import ( - indexapi, -) - -from mercurial import util - -class fileindexapi(indexapi): - def __init__(self, repo): - super(fileindexapi, self).__init__() - self._repo = repo - root = repo.ui.config('infinitepush', 'indexpath') - if not root: - root = os.path.join('scratchbranches', 'index') - - self._nodemap = os.path.join(root, 'nodemap') - self._bookmarkmap = os.path.join(root, 'bookmarkmap') - self._metadatamap = os.path.join(root, 'nodemetadatamap') - self._lock = None - - def __enter__(self): - self._lock = self._repo.wlock() - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - if self._lock: - self._lock.__exit__(exc_type, exc_val, exc_tb) - - def addbundle(self, bundleid, nodesctx): - for node in nodesctx: - nodepath = os.path.join(self._nodemap, node.hex()) - self._write(nodepath, bundleid) - - def addbookmark(self, bookmark, node): - bookmarkpath = os.path.join(self._bookmarkmap, bookmark) - self._write(bookmarkpath, node) - - def addmanybookmarks(self, bookmarks): - for bookmark, node in bookmarks.items(): - self.addbookmark(bookmark, node) - - def deletebookmarks(self, patterns): - for pattern in patterns: - for bookmark, _ in self._listbookmarks(pattern): - bookmarkpath = os.path.join(self._bookmarkmap, bookmark) - self._delete(bookmarkpath) - - def getbundle(self, node): - nodepath = os.path.join(self._nodemap, node) - return self._read(nodepath) - - def getnode(self, bookmark): - bookmarkpath = os.path.join(self._bookmarkmap, bookmark) - return self._read(bookmarkpath) - - def getbookmarks(self, query): - return dict(self._listbookmarks(query)) - - def saveoptionaljsonmetadata(self, node, jsonmetadata): - vfs = self._repo.vfs - vfs.write(os.path.join(self._metadatamap, node), jsonmetadata) - - def _listbookmarks(self, pattern): - if pattern.endswith('*'): - pattern = 're:^' + pattern[:-1] + '.*' - kind, pat, matcher = util.stringmatcher(pattern) - prefixlen = len(self._bookmarkmap) + 1 - for dirpath, _, books in self._repo.vfs.walk(self._bookmarkmap): - for book in books: - bookmark = os.path.join(dirpath, book)[prefixlen:] - if not matcher(bookmark): - continue - yield bookmark, self._read(os.path.join(dirpath, book)) - - def _write(self, path, value): - vfs = self._repo.vfs - dirname = vfs.dirname(path) - if not vfs.exists(dirname): - vfs.makedirs(dirname) - - vfs.write(path, value) - - def _read(self, path): - vfs = self._repo.vfs - if not vfs.exists(path): - return None - return vfs.read(path) - - def _delete(self, path): - vfs = self._repo.vfs - if not vfs.exists(path): - return - return vfs.unlink(path) diff --git a/infinitepush/indexapi.py b/infinitepush/indexapi.py deleted file mode 100644 --- a/infinitepush/indexapi.py +++ /dev/null @@ -1,68 +0,0 @@ -# Infinite push -# -# Copyright 2016 Facebook, Inc. -# -# This software may be used and distributed according to the terms of the -# GNU General Public License version 2 or any later version. - -class indexapi(object): - """Class that manages access to infinitepush index. - - This class is a context manager and all write operations (like - deletebookmarks, addbookmark etc) should use `with` statement: - - with index: - index.deletebookmarks(...) - ... - """ - - def __init__(self): - """Initializes the metadata store connection.""" - - def close(self): - """Cleans up the metadata store connection.""" - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - pass - - def addbundle(self, bundleid, nodesctx): - """Takes a bundleid and a list of node contexts for each node - in that bundle and records that.""" - raise NotImplementedError() - - def addbookmark(self, bookmark, node): - """Takes a bookmark name and hash, and records mapping in the metadata - store.""" - raise NotImplementedError() - - def addmanybookmarks(self, bookmarks): - """Takes a dict with mapping from bookmark to hash and records mapping - in the metadata store.""" - raise NotImplementedError() - - def deletebookmarks(self, patterns): - """Accepts list of bookmarks and deletes them. - """ - raise NotImplementedError() - - def getbundle(self, node): - """Returns the bundleid for the bundle that contains the given node.""" - raise NotImplementedError() - - def getnode(self, bookmark): - """Returns the node for the given bookmark. None if it doesn't exist.""" - raise NotImplementedError() - - def getbookmarks(self, query): - """Returns bookmarks that match the query""" - raise NotImplementedError() - - def saveoptionaljsonmetadata(self, node, jsonmetadata): - """Saves optional metadata for a given node""" - raise NotImplementedError() - -class indexexception(Exception): - pass diff --git a/infinitepush/infinitepushcommands.py b/infinitepush/infinitepushcommands.py deleted file mode 100644 --- a/infinitepush/infinitepushcommands.py +++ /dev/null @@ -1,98 +0,0 @@ -# Copyright 2016 Facebook, Inc. -# -# This software may be used and distributed according to the terms of the -# GNU General Public License version 2 or any later version. -""" -Config:: - - [infinitepush] - # limit number of files in the node metadata. This is to make sure we don't - # waste too much space on huge codemod commits. - metadatafilelimit = 100 -""" - -from __future__ import absolute_import - -import json - -from .backupcommands import cmdtable as backupcmdtable - -from mercurial import ( - copies as copiesmod, - encoding, - error, - hg, - patch, - registrar, - scmutil, - util, -) - -from .common import downloadbundle -from mercurial.node import bin -from mercurial.i18n import _ - -cmdtable = backupcmdtable -command = registrar.command(cmdtable) - -@command('debugfillinfinitepushmetadata', - [('', 'node', [], 'node to fill metadata for')]) -def debugfillinfinitepushmetadata(ui, repo, **opts): - '''Special command that fills infinitepush metadata for a node - ''' - - nodes = opts['node'] - if not nodes: - raise error.Abort(_('nodes are not specified')) - - filelimit = ui.configint('infinitepush', 'metadatafilelimit', 100) - nodesmetadata = {} - for node in nodes: - index = repo.bundlestore.index - if not bool(index.getbundle(node)): - raise error.Abort(_('node %s is not found') % node) - - if node not in repo: - newbundlefile = downloadbundle(repo, bin(node)) - bundlepath = "bundle:%s+%s" % (repo.root, newbundlefile) - bundlerepo = hg.repository(ui, bundlepath) - repo = bundlerepo - - p1 = repo[node].p1().node() - diffopts = patch.diffallopts(ui, {}) - match = scmutil.matchall(repo) - chunks = patch.diff(repo, p1, node, match, None, diffopts, relroot='') - difflines = util.iterlines(chunks) - - states = 'modified added removed deleted unknown ignored clean'.split() - status = repo.status(p1, node) - status = zip(states, status) - - filestatus = {} - for state, files in status: - for f in files: - filestatus[f] = state - - diffstat = patch.diffstatdata(difflines) - changed_files = {} - copies = copiesmod.pathcopies(repo[p1], repo[node]) - for filename, adds, removes, isbinary in diffstat[:filelimit]: - # use special encoding that allows non-utf8 filenames - filename = encoding.jsonescape(filename, paranoid=True) - changed_files[filename] = { - 'adds': adds, 'removes': removes, 'isbinary': isbinary, - 'status': filestatus.get(filename, 'unknown') - } - if filename in copies: - changed_files[filename]['copies'] = copies[filename] - - output = {} - output['changed_files'] = changed_files - if len(diffstat) > filelimit: - output['changed_files_truncated'] = True - nodesmetadata[node] = output - - with index: - for node, metadata in nodesmetadata.iteritems(): - dumped = json.dumps(metadata, sort_keys=True) - index.saveoptionaljsonmetadata(node, dumped) diff --git a/infinitepush/schema.sql b/infinitepush/schema.sql deleted file mode 100644 --- a/infinitepush/schema.sql +++ /dev/null @@ -1,33 +0,0 @@ -CREATE TABLE `bookmarkstonode` ( - `node` varbinary(64) NOT NULL, - `bookmark` varbinary(512) NOT NULL, - `reponame` varbinary(255) NOT NULL, - PRIMARY KEY (`reponame`,`bookmark`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - -CREATE TABLE `bundles` ( - `bundle` varbinary(512) NOT NULL, - `reponame` varbinary(255) NOT NULL, - PRIMARY KEY (`bundle`,`reponame`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - -CREATE TABLE `nodestobundle` ( - `node` varbinary(64) NOT NULL, - `bundle` varbinary(512) NOT NULL, - `reponame` varbinary(255) NOT NULL, - PRIMARY KEY (`node`,`reponame`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - -CREATE TABLE `nodesmetadata` ( - `node` varbinary(64) NOT NULL, - `message` mediumblob NOT NULL, - `p1` varbinary(64) NOT NULL, - `p2` varbinary(64) DEFAULT NULL, - `author` varbinary(255) NOT NULL, - `committer` varbinary(255) DEFAULT NULL, - `author_date` bigint(20) NOT NULL, - `committer_date` bigint(20) DEFAULT NULL, - `reponame` varbinary(255) NOT NULL, - `optional_json_metadata` mediumblob, - PRIMARY KEY (`reponame`,`node`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/infinitepush/sqlindexapi.py b/infinitepush/sqlindexapi.py deleted file mode 100644 --- a/infinitepush/sqlindexapi.py +++ /dev/null @@ -1,257 +0,0 @@ -# Infinite push -# -# Copyright 2016 Facebook, Inc. -# -# This software may be used and distributed according to the terms of the -# GNU General Public License version 2 or any later version. - -import os -import time -import logging - -import warnings -import mysql.connector - -from indexapi import ( - indexapi, - indexexception, -) - -def _convertbookmarkpattern(pattern): - pattern = pattern.replace('_', '\\_') - pattern = pattern.replace('%', '\\%') - if pattern.endswith('*'): - pattern = pattern[:-1] + '%' - return pattern - -class sqlindexapi(indexapi): - ''' - Sql backend for infinitepush index. See schema.sql - ''' - - def __init__(self, reponame, host, port, - database, user, password, logfile, loglevel, - waittimeout=300, locktimeout=120): - super(sqlindexapi, self).__init__() - self.reponame = reponame - self.sqlargs = { - 'host': host, - 'port': port, - 'database': database, - 'user': user, - 'password': password, - } - self.sqlconn = None - self.sqlcursor = None - if not logfile: - logfile = os.devnull - logging.basicConfig(filename=logfile) - self.log = logging.getLogger() - self.log.setLevel(loglevel) - self._connected = False - self._waittimeout = waittimeout - self._locktimeout = locktimeout - - def sqlconnect(self): - if self.sqlconn: - raise indexexception("SQL connection already open") - if self.sqlcursor: - raise indexexception("SQL cursor already open without connection") - retry = 3 - while True: - try: - self.sqlconn = mysql.connector.connect( - force_ipv6=True, **self.sqlargs) - - # Code is copy-pasted from hgsql. Bug fixes need to be - # back-ported! - # The default behavior is to return byte arrays, when we - # need strings. This custom convert returns strings. - self.sqlconn.set_converter_class(CustomConverter) - self.sqlconn.autocommit = False - break - except mysql.connector.errors.Error: - # mysql can be flakey occasionally, so do some minimal - # retrying. - retry -= 1 - if retry == 0: - raise - time.sleep(0.2) - - waittimeout = self.sqlconn.converter.escape('%s' % self._waittimeout) - - self.sqlcursor = self.sqlconn.cursor() - self.sqlcursor.execute("SET wait_timeout=%s" % waittimeout) - self.sqlcursor.execute("SET innodb_lock_wait_timeout=%s" % - self._locktimeout) - self._connected = True - - def close(self): - """Cleans up the metadata store connection.""" - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - self.sqlcursor.close() - self.sqlconn.close() - self.sqlcursor = None - self.sqlconn = None - - def __enter__(self): - if not self._connected: - self.sqlconnect() - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - if exc_type is None: - self.sqlconn.commit() - else: - self.sqlconn.rollback() - - def addbundle(self, bundleid, nodesctx): - if not self._connected: - self.sqlconnect() - self.log.info("ADD BUNDLE %r %r" % (self.reponame, bundleid)) - self.sqlcursor.execute( - "INSERT INTO bundles(bundle, reponame) VALUES " - "(%s, %s)", params=(bundleid, self.reponame)) - for ctx in nodesctx: - self.sqlcursor.execute( - "INSERT INTO nodestobundle(node, bundle, reponame) " - "VALUES (%s, %s, %s) ON DUPLICATE KEY UPDATE " - "bundle=VALUES(bundle)", - params=(ctx.hex(), bundleid, self.reponame)) - - extra = ctx.extra() - author_name = ctx.user() - committer_name = extra.get('committer', ctx.user()) - author_date = int(ctx.date()[0]) - committer_date = int(extra.get('committer_date', author_date)) - self.sqlcursor.execute( - "INSERT IGNORE INTO nodesmetadata(node, message, p1, p2, " - "author, committer, author_date, committer_date, " - "reponame) VALUES " - "(%s, %s, %s, %s, %s, %s, %s, %s, %s)", - params=(ctx.hex(), ctx.description(), - ctx.p1().hex(), ctx.p2().hex(), author_name, - committer_name, author_date, committer_date, - self.reponame) - ) - - def addbookmark(self, bookmark, node): - """Takes a bookmark name and hash, and records mapping in the metadata - store.""" - if not self._connected: - self.sqlconnect() - self.log.info( - "ADD BOOKMARKS %r bookmark: %r node: %r" % - (self.reponame, bookmark, node)) - self.sqlcursor.execute( - "INSERT INTO bookmarkstonode(bookmark, node, reponame) " - "VALUES (%s, %s, %s) ON DUPLICATE KEY UPDATE node=VALUES(node)", - params=(bookmark, node, self.reponame)) - - def addmanybookmarks(self, bookmarks): - if not self._connected: - self.sqlconnect() - args = [] - values = [] - for bookmark, node in bookmarks.iteritems(): - args.append('(%s, %s, %s)') - values.extend((bookmark, node, self.reponame)) - args = ','.join(args) - - self.sqlcursor.execute( - "INSERT INTO bookmarkstonode(bookmark, node, reponame) " - "VALUES %s ON DUPLICATE KEY UPDATE node=VALUES(node)" % args, - params=values) - - def deletebookmarks(self, patterns): - """Accepts list of bookmark patterns and deletes them. - If `commit` is set then bookmark will actually be deleted. Otherwise - deletion will be delayed until the end of transaction. - """ - if not self._connected: - self.sqlconnect() - self.log.info("DELETE BOOKMARKS: %s" % patterns) - for pattern in patterns: - pattern = _convertbookmarkpattern(pattern) - self.sqlcursor.execute( - "DELETE from bookmarkstonode WHERE bookmark LIKE (%s) " - "and reponame = %s", - params=(pattern, self.reponame)) - - def getbundle(self, node): - """Returns the bundleid for the bundle that contains the given node.""" - if not self._connected: - self.sqlconnect() - self.log.info("GET BUNDLE %r %r" % (self.reponame, node)) - self.sqlcursor.execute( - "SELECT bundle from nodestobundle " - "WHERE node = %s AND reponame = %s", params=(node, self.reponame)) - result = self.sqlcursor.fetchall() - if len(result) != 1 or len(result[0]) != 1: - self.log.info("No matching node") - return None - bundle = result[0][0] - self.log.info("Found bundle %r" % bundle) - return bundle - - def getnode(self, bookmark): - """Returns the node for the given bookmark. None if it doesn't exist.""" - if not self._connected: - self.sqlconnect() - self.log.info( - "GET NODE reponame: %r bookmark: %r" % (self.reponame, bookmark)) - self.sqlcursor.execute( - "SELECT node from bookmarkstonode WHERE " - "bookmark = %s AND reponame = %s", params=(bookmark, self.reponame)) - result = self.sqlcursor.fetchall() - if len(result) != 1 or len(result[0]) != 1: - self.log.info("No matching bookmark") - return None - node = result[0][0] - self.log.info("Found node %r" % node) - return node - - def getbookmarks(self, query): - if not self._connected: - self.sqlconnect() - self.log.info( - "QUERY BOOKMARKS reponame: %r query: %r" % (self.reponame, query)) - query = _convertbookmarkpattern(query) - self.sqlcursor.execute( - "SELECT bookmark, node from bookmarkstonode WHERE " - "reponame = %s AND bookmark LIKE %s", - params=(self.reponame, query)) - result = self.sqlcursor.fetchall() - bookmarks = {} - for row in result: - if len(row) != 2: - self.log.info("Bad row returned: %s" % row) - continue - bookmarks[row[0]] = row[1] - return bookmarks - - def saveoptionaljsonmetadata(self, node, jsonmetadata): - if not self._connected: - self.sqlconnect() - self.log.info( - ("INSERT METADATA, QUERY BOOKMARKS reponame: %r " + - "node: %r, jsonmetadata: %s") % - (self.reponame, node, jsonmetadata)) - - self.sqlcursor.execute( - "UPDATE nodesmetadata SET optional_json_metadata=%s WHERE " - "reponame=%s AND node=%s", - params=(jsonmetadata, self.reponame, node)) - -class CustomConverter(mysql.connector.conversion.MySQLConverter): - """Ensure that all values being returned are returned as python string - (versus the default byte arrays).""" - def _STRING_to_python(self, value, dsc=None): - return str(value) - - def _VAR_STRING_to_python(self, value, dsc=None): - return str(value) - - def _BLOB_to_python(self, value, dsc=None): - return str(value) diff --git a/infinitepush/store.py b/infinitepush/store.py deleted file mode 100644 --- a/infinitepush/store.py +++ /dev/null @@ -1,151 +0,0 @@ -# This software may be used and distributed according to the terms of the -# GNU General Public License version 2 or any later version. - -# based on bundleheads extension by Gregory Szorc - -import abc -import hashlib -import os -import subprocess -from tempfile import NamedTemporaryFile - -class BundleWriteException(Exception): - pass - -class BundleReadException(Exception): - pass - -class abstractbundlestore(object): - """Defines the interface for bundle stores. - - A bundle store is an entity that stores raw bundle data. It is a simple - key-value store. However, the keys are chosen by the store. The keys can - be any Python object understood by the corresponding bundle index (see - ``abstractbundleindex`` below). - """ - __metaclass__ = abc.ABCMeta - - @abc.abstractmethod - def write(self, data): - """Write bundle data to the store. - - This function receives the raw data to be written as a str. - Throws BundleWriteException - The key of the written data MUST be returned. - """ - - @abc.abstractmethod - def read(self, key): - """Obtain bundle data for a key. - - Returns None if the bundle isn't known. - Throws BundleReadException - The returned object should be a file object supporting read() - and close(). - """ - -class filebundlestore(object): - """bundle store in filesystem - - meant for storing bundles somewhere on disk and on network filesystems - """ - def __init__(self, ui, repo): - self.ui = ui - self.repo = repo - self.storepath = ui.configpath('scratchbranch', 'storepath') - if not self.storepath: - self.storepath = self.repo.vfs.join("scratchbranches", - "filebundlestore") - if not os.path.exists(self.storepath): - os.makedirs(self.storepath) - - def _dirpath(self, hashvalue): - """First two bytes of the hash are the name of the upper - level directory, next two bytes are the name of the - next level directory""" - return os.path.join(self.storepath, hashvalue[0:2], hashvalue[2:4]) - - def _filepath(self, filename): - return os.path.join(self._dirpath(filename), filename) - - def write(self, data): - filename = hashlib.sha1(data).hexdigest() - dirpath = self._dirpath(filename) - - if not os.path.exists(dirpath): - os.makedirs(dirpath) - - with open(self._filepath(filename), 'w') as f: - f.write(data) - - return filename - - def read(self, key): - try: - f = open(self._filepath(key), 'r') - except IOError: - return None - - return f.read() - -class externalbundlestore(abstractbundlestore): - def __init__(self, put_binary, put_args, get_binary, get_args): - """ - `put_binary` - path to binary file which uploads bundle to external - storage and prints key to stdout - `put_args` - format string with additional args to `put_binary` - {filename} replacement field can be used. - `get_binary` - path to binary file which accepts filename and key - (in that order), downloads bundle from store and saves it to file - `get_args` - format string with additional args to `get_binary`. - {filename} and {handle} replacement field can be used. - """ - - self.put_args = put_args - self.get_args = get_args - self.put_binary = put_binary - self.get_binary = get_binary - - def _call_binary(self, args): - p = subprocess.Popen( - args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - close_fds=True) - stdout, stderr = p.communicate() - returncode = p.returncode - return returncode, stdout, stderr - - def write(self, data): - # Won't work on windows because you can't open file second time without - # closing it - with NamedTemporaryFile() as temp: - temp.write(data) - temp.flush() - temp.seek(0) - formatted_args = [arg.format(filename=temp.name) - for arg in self.put_args] - returncode, stdout, stderr = self._call_binary( - [self.put_binary] + formatted_args) - - if returncode != 0: - raise BundleWriteException( - 'Failed to upload to external store: %s' % stderr) - stdout_lines = stdout.splitlines() - if len(stdout_lines) == 1: - return stdout_lines[0] - else: - raise BundleWriteException( - 'Bad output from %s: %s' % (self.put_binary, stdout)) - - def read(self, handle): - # Won't work on windows because you can't open file second time without - # closing it - with NamedTemporaryFile() as temp: - formatted_args = [arg.format(filename=temp.name, handle=handle) - for arg in self.get_args] - returncode, stdout, stderr = self._call_binary( - [self.get_binary] + formatted_args) - - if returncode != 0: - raise BundleReadException( - 'Failed to download from external store: %s' % stderr) - return temp.read() diff --git a/setup.py b/setup.py --- a/setup.py +++ b/setup.py @@ -220,7 +220,6 @@ availablepymodules = hgext3rd availablepackages = hgext3rdpkgs + [ - 'infinitepush', 'phabricator', 'remotefilelog', ] @@ -366,7 +365,6 @@ 'absorb' : ['linelog'], 'cstore' : ['ctreemanifest', 'cdatapack'], 'fastannotate' : ['linelog'], - 'infinitepush' : ['extutil'], 'remotefilelog' : ['cstore', 'extutil'], 'treemanifest' : ['cstore'], } diff --git a/tests/library-infinitepush.sh b/tests/library-infinitepush.sh deleted file mode 100644 --- a/tests/library-infinitepush.sh +++ /dev/null @@ -1,117 +0,0 @@ -scratchnodes() { - for node in `find ../repo/.hg/scratchbranches/index/nodemap/* | sort`; do - echo ${node##*/} `cat $node` - done -} - -scratchbookmarks() { - for bookmark in `find ../repo/.hg/scratchbranches/index/bookmarkmap/* -type f | sort`; do - echo "${bookmark##*/bookmarkmap/} `cat $bookmark`" - done -} - -setupcommon() { - cat >> $HGRCPATH << EOF -[extensions] -infinitepush=$TESTDIR/../infinitepush -[ui] -ssh = python "$TESTDIR/dummyssh" -[infinitepush] -branchpattern=re:scratch/.* -EOF -} - -setupserver() { -cat >> .hg/hgrc << EOF -[infinitepush] -server=yes -indextype=disk -storetype=disk -reponame=babar -EOF -} - -setupsqlclienthgrc() { -cat << EOF > .hg/hgrc -[ui] -ssh=python "$TESTDIR/dummyssh" -[extensions] -infinitepush=$TESTDIR/../infinitepush -[infinitepush] -branchpattern=re:scratch/.+ -server=False -[paths] -default = ssh://user@dummy/server -EOF -} - -setupsqlserverhgrc() { -cat << EOF > .hg/hgrc -[ui] -ssh=python "$TESTDIR/dummyssh" -[extensions] -infinitepush=$TESTDIR/../infinitepush -[infinitepush] -branchpattern=re:scratch/.+ -server=True -indextype=sql -storetype=disk -reponame=$1 -EOF -} - -createdb() { -mysql -h $DBHOST -P $DBPORT -u $DBUSER $DBPASSOPT -e "CREATE DATABASE IF NOT EXISTS $DBNAME;" 2>/dev/null -mysql -h $DBHOST -P $DBPORT -D $DBNAME -u $DBUSER $DBPASSOPT </dev/null - -if [[ -z $DBHOST && -z $DBPORT && -n $DBHOSTPORT ]]; then - # Assuming they are set using the legacy way: $DBHOSTPORT - DBHOST=`echo $DBHOSTPORT | cut -d : -f 1` - DBPORT=`echo $DBHOSTPORT | cut -d : -f 2` -fi - -[[ -z $DBHOST ]] && DBHOST=localhost -[[ -z $DBPORT ]] && DBPORT=3306 -[[ -z $DBPASS && -n $PASSWORD ]] && DBPASS="$PASSWORD" -[[ -z $DBUSER && -n $USER ]] && DBUSER="$USER" -[[ -z $DBNAME ]] && DBNAME="testdb_hg_$$_$TIME" -if [[ -z $DBPASS ]]; then - DBPASSOPT='' -else - DBPASSOPT='-p'"$DBPASS" -fi - -echo "sqlhost=$DBHOST:$DBPORT:$DBNAME:$DBUSER:$DBPASS" >> .hg/hgrc - -createdb -} - -waitbgbackup() { - sleep 1 - hg debugwaitbackup -} - -mkcommitautobackup() { - echo $1 > $1 - hg add $1 - hg ci -m $1 --config infinitepushbackup.autobackup=True -} - -setuplogdir() { - mkdir $TESTTMP/logs - chmod 0755 $TESTTMP/logs - chmod +t $TESTTMP/logs -} - - diff --git a/tests/test-check-config-hg.t b/tests/test-check-config-hg.t --- a/tests/test-check-config-hg.t +++ b/tests/test-check-config-hg.t @@ -36,7 +36,6 @@ undocumented: fbconduit.reponame (str) undocumented: fbhistedit.exec_in_user_shell (str) undocumented: grep.command (str) - undocumented: infinitepush.bundle-stream (bool) undocumented: morestatus.show (bool) undocumented: nointerrupt.interactiveonly (bool) [True] undocumented: perftweaks.cachenoderevs (bool) [True] diff --git a/tests/test-check-py3-compat-hg.t b/tests/test-check-py3-compat-hg.t --- a/tests/test-check-py3-compat-hg.t +++ b/tests/test-check-py3-compat-hg.t @@ -66,12 +66,6 @@ hgext3rd/stat.py not using absolute_import hgext3rd/upgradegeneraldelta.py not using absolute_import hgext3rd/whereami.py not using absolute_import - infinitepush/bundleparts.py not using absolute_import - infinitepush/common.py not using absolute_import - infinitepush/fileindexapi.py not using absolute_import - infinitepush/indexapi.py not using absolute_import - infinitepush/sqlindexapi.py not using absolute_import - infinitepush/store.py not using absolute_import linelog/pyext/test-random-edits.py not using absolute_import phabricator/arcconfig.py not using absolute_import phabricator/diffprops.py not using absolute_import diff --git a/tests/test-infinitepush-backup-logging.t b/tests/test-infinitepush-backup-logging.t deleted file mode 100644 --- a/tests/test-infinitepush-backup-logging.t +++ /dev/null @@ -1,28 +0,0 @@ - - $ . "$TESTDIR/library.sh" - $ . "$TESTDIR/library-infinitepush.sh" - $ setupcommon - -Setup server - $ hg init repo - $ cd repo - $ setupserver - $ cd .. - -Clone - $ hg clone ssh://user@dummy/repo client -q - $ cd client - -Create log dir - $ mkdir $TESTTMP/logs - -Setup infinitepush backup logging - $ printf "\n[infinitepushbackup]\nlogdir=$TESTTMP/logs" >> .hg/hgrc - $ mkcommit first - -Check that logging fails because of wrong permissions - $ hg pushbackup --background - $ waitbgbackup - $ hg pushbackup --background --debug - $TESTTMP/logs directory has incorrect permission, infinitepush backup logging will be disabled - $ waitbgbackup diff --git a/tests/test-infinitepush-backup-remotefilelog.t b/tests/test-infinitepush-backup-remotefilelog.t deleted file mode 100644 --- a/tests/test-infinitepush-backup-remotefilelog.t +++ /dev/null @@ -1,123 +0,0 @@ - - $ . "$TESTDIR/library.sh" - $ . "$TESTDIR/library-infinitepush.sh" - $ setupcommon - -Setup infinitepush and remotefilelog server - $ hg init repo - $ cd repo - $ setupserver - $ cat >> .hg/hgrc << EOF - > [remotefilelog] - > server=True - > EOF - $ cd .. - -Make client shallow clone - $ hgcloneshallow ssh://user@dummy/repo client - streaming all changes - 0 files to transfer, 0 bytes of data - transferred 0 bytes in \d+(\.\d+)? seconds \(0 bytes/sec\) (re) - no changes found - updating to branch default - 0 files updated, 0 files merged, 0 files removed, 0 files unresolved - -Create 3 commits, two of which will be stripped. It's important to remove file -that was created in the second commit to make sure it's filelogs won't be -downloaded to the client - $ cd repo - $ mkcommit serverinitialcommit - $ mkcommit committostripfirst - $ hg rm committostripfirst - $ echo 'committostripsecond' >> committostripsecond - $ hg add committostripsecond - $ hg ci -m committostripsecond - -Pull changes client-side - $ cd ../client - $ hg pull - pulling from ssh://user@dummy/repo - streaming all changes - 5 files to transfer, 1.06 KB of data - transferred 1.06 KB in [\d.]+ seconds \([\d.]+ .*\) (re) - searching for changes - no changes found - -Make commit on top of commit that will be stripped server-side. Also make two -bookmarks - $ hg up 0 - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - 1 files fetched over 1 fetches - \(1 misses, 0.00% hit ratio\) over [\d.]+s (re) - $ hg book goodbooktobackup - $ hg up 2 - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - (leaving bookmark goodbooktobackup) - 1 files fetched over 1 fetches - \(1 misses, 0.00% hit ratio\) over [\d.]+s (re) - $ hg book badbooktobackup - $ mkcommit clientbadcommit - $ hg log --graph -T '{desc} {node}' - @ clientbadcommit 07e73d09a07862bc2b8beb13b72d2347f83e4981 - | - o committostripsecond 221b386ae565d9866b4838ae552ce3acc26e9fec - | - o committostripfirst 48acd0edbb460dec0d93314393d41f801a9797ce - | - o serverinitialcommit 22ea264ff89d6891c2889f15f338ac9fa2474f8b - - $ cd .. - -Strip commit server-side - $ cd repo - $ hg log -r 1 -T '{node}\n' - 48acd0edbb460dec0d93314393d41f801a9797ce - $ hg strip 48acd0edbb460dec0d93314393d41f801a9797ce - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - saved backup bundle to $TESTTMP/repo/.hg/strip-backup/48acd0edbb46-9d7996f9-backup.hg (glob) - $ hg log --graph -T '{desc}' - @ serverinitialcommit - - - Add two revisions to a dontbackupnodes config: one is a revision that was really stripped - from the server, another is just a node that doesn't exists in the repo - $ cd ../client - $ cat >> .hg/hgrc << EOF - > [infinitepushbackup] - > dontbackupnodes=48acd0edbb460dec0d93314393d41f801a9797ce,unknownnode - > EOF - -Now do a backup, it should not fail - $ hg pushbackup > /dev/null - filtering nodes: ['07e73d09a07862bc2b8beb13b72d2347f83e4981'] - -Now try to restore it from different client. Make sure bookmark -`goodbooktobackup` is restored - $ cd .. - $ hgcloneshallow ssh://user@dummy/repo secondclient - streaming all changes - 2 files to transfer, 268 bytes of data - transferred 268 bytes in [\d.]+ seconds \([\d.]+ .*\) (re) - searching for changes - no changes found - updating to branch default - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ cd secondclient - $ hg pullbackup --traceback - pulling from ssh://user@dummy/repo - no changes found - $ hg book - goodbooktobackup 0:22ea264ff89d - -Create a commit which deletes a file. Make sure it is backed up correctly - $ cd ../client - $ hg up -q 0 - $ mkcommit filetodelete - created new head - $ hg rm filetodelete - $ hg ci -m 'deleted' - $ hg log -r . -T '{node}\n' - 507709f4da22941c0471885d8377c48d6dadce21 - $ hg pushbackup > /dev/null - filtering nodes: ['07e73d09a07862bc2b8beb13b72d2347f83e4981'] - $ scratchbookmarks - infinitepush/backups/test/*$TESTTMP/client/bookmarks/goodbooktobackup 22ea264ff89d6891c2889f15f338ac9fa2474f8b (glob) - infinitepush/backups/test/*$TESTTMP/client/heads/507709f4da22941c0471885d8377c48d6dadce21 507709f4da22941c0471885d8377c48d6dadce21 (glob) diff --git a/tests/test-infinitepush-backup-remotenames.t b/tests/test-infinitepush-backup-remotenames.t deleted file mode 100644 --- a/tests/test-infinitepush-backup-remotenames.t +++ /dev/null @@ -1,23 +0,0 @@ - $ . $TESTDIR/require-ext.sh remotenames - $ . "$TESTDIR/library.sh" - $ . "$TESTDIR/library-infinitepush.sh" - $ setupcommon - -Setup server - $ hg init repo - $ cd repo - $ setupserver - $ cd .. - -Create client - $ hg clone ssh://user@dummy/repo client -q - $ cd client - -Backup with remotenames enabled. Make sure that it works fine with anon heads - $ mkcommit remotenamespush - $ hg --config extensions.remotenames= pushbackup - starting backup .* (re) - searching for changes - remote: pushing 1 commit: - remote: f4ca5164f72e remotenamespush - finished in \d+\.(\d+)? seconds (re) diff --git a/tests/test-infinitepush-backup-share.t b/tests/test-infinitepush-backup-share.t deleted file mode 100644 --- a/tests/test-infinitepush-backup-share.t +++ /dev/null @@ -1,57 +0,0 @@ - $ . "$TESTDIR/library.sh" - $ . "$TESTDIR/library-infinitepush.sh" - $ setupcommon - $ mkcommit() { - > echo "$1" > "$1" - > hg add "$1" - > hg ci -m "$1" - > } - $ cat >> $HGRCPATH << EOF - > [extensions] - > share= - > EOF - $ hg init repo - $ cd repo - $ setupserver - $ cd .. - -Clone client - $ hg clone ssh://user@dummy/repo client -q - $ hg share --bookmarks client client2 - updating working directory - 0 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ cd client2 - $ mkcommit first - $ hg paths - default = ssh://user@dummy/repo - -Write smth to backup state file in the shared working copy to check that -it's not read by infinitepush backup client - $ echo 'rubbish' > .hg/infinitepushbackupstate - $ hg pushbackup - starting backup .* (re) - searching for changes - remote: pushing 1 commit: - remote: b75a450e74d5 first - finished in \d+\.(\d+)? seconds (re) - $ scratchbookmarks - infinitepush/backups/test/*$TESTTMP/client/heads/b75a450e74d5a7708da8c3144fbeb4ac88694044 b75a450e74d5a7708da8c3144fbeb4ac88694044 (glob) - -Make sure that backup state is saved only on the "main" repo - $ cat .hg/infinitepushbackupstate - rubbish - $ [ -f ../client/.hg/infinitepushbackupstate ] - -Make sure that isbackedup references the main repo - $ hg isbackedup -r : - b75a450e74d5a7708da8c3144fbeb4ac88694044 backed up - $ hg log -T '{rev}:{node} "{desc}"\n' -r 'notbackedup()' - -Make another commit that is not backed up and check that too - $ mkcommit second - $ hg isbackedup -r : - b75a450e74d5a7708da8c3144fbeb4ac88694044 backed up - bc64f6a267a06b03e9e0f96a6deae37ae89a832e not backed up - $ hg log -T '{rev}:{node} "{desc}"\n' -r 'notbackedup()' - 1:bc64f6a267a06b03e9e0f96a6deae37ae89a832e "second" - diff --git a/tests/test-infinitepush-backup-sql.t b/tests/test-infinitepush-backup-sql.t deleted file mode 100644 --- a/tests/test-infinitepush-backup-sql.t +++ /dev/null @@ -1,62 +0,0 @@ -#if no-osx - $ . "$TESTDIR/library.sh" - $ . "$TESTDIR/library-infinitepush.sh" - $ setupcommon - $ mkcommit() { - > echo "$1" > "$1" - > hg add "$1" - > hg ci -m "$1" - > } - $ hg init server - $ cd server - $ setupsqlserverhgrc babar - $ setupdb - $ cd .. - $ hg clone -q ssh://user@dummy/server client - $ cd client - $ setupsqlclienthgrc - $ mkcommit initialcommit - $ hg pushbackup - starting backup .* (re) - searching for changes - remote: pushing 1 commit: - remote: 67145f466344 initialcommit - finished in \d+\.(\d+)? seconds (re) - $ mkcommit commitwithbookmark - $ hg book abook - $ hg pushbackup - starting backup .* (re) - searching for changes - remote: pushing 2 commits: - remote: 67145f466344 initialcommit - remote: 5ea4271ca0f0 commitwithbookmark - finished in \d+\.(\d+)? seconds (re) - $ mysql -h $DBHOST -P $DBPORT -D $DBNAME -u $DBUSER $DBPASSOPT -e 'SELECT bookmark, node, reponame from bookmarkstonode' - bookmark node reponame - infinitepush/backups/test/.*\$TESTTMP/client/bookmarks/abook 5ea4271ca0f0cda5477241ae95ffc1fa7056ee6f babar (re) - infinitepush/backups/test/.*\$TESTTMP/client/heads/5ea4271ca0f0cda5477241ae95ffc1fa7056ee6f 5ea4271ca0f0cda5477241ae95ffc1fa7056ee6f babar (re) -Create a server with different name that connects to the same db - $ cd .. - $ rm -rf server - $ hg init server - $ cd server - $ setupsqlserverhgrc newserver - $ echo "sqlhost=$DBHOST:$DBPORT:$DBNAME:$DBUSER:$DBPASS" >> .hg/hgrc - -Go to client, delete backup state and run pushbackup. Make sure that it doesn't delete entries from another repo - $ cd ../client - $ rm .hg/infinitepushbackupstate - $ hg pushbackup - starting backup .* (re) - searching for changes - remote: pushing 2 commits: - remote: 67145f466344 initialcommit - remote: 5ea4271ca0f0 commitwithbookmark - finished in \d+\.(\d+)? seconds (re) - $ mysql -h $DBHOST -P $DBPORT -D $DBNAME -u $DBUSER $DBPASSOPT -e 'SELECT bookmark, node, reponame from bookmarkstonode' - bookmark node reponame - infinitepush/backups/test/.*\$TESTTMP/client/bookmarks/abook 5ea4271ca0f0cda5477241ae95ffc1fa7056ee6f babar (re) - infinitepush/backups/test/.*\$TESTTMP/client/heads/5ea4271ca0f0cda5477241ae95ffc1fa7056ee6f 5ea4271ca0f0cda5477241ae95ffc1fa7056ee6f babar (re) - infinitepush/backups/test/.*\$TESTTMP/client/bookmarks/abook 5ea4271ca0f0cda5477241ae95ffc1fa7056ee6f newserver (re) - infinitepush/backups/test/.*\$TESTTMP/client/heads/5ea4271ca0f0cda5477241ae95ffc1fa7056ee6f 5ea4271ca0f0cda5477241ae95ffc1fa7056ee6f newserver (re) -#endif diff --git a/tests/test-infinitepush-backup-status.t b/tests/test-infinitepush-backup-status.t deleted file mode 100644 --- a/tests/test-infinitepush-backup-status.t +++ /dev/null @@ -1,191 +0,0 @@ - - $ setup() { - > cat << EOF >> .hg/hgrc - > [extensions] - > fbamend=$TESTDIR/../hgext3rd/fbamend - > inhibit=$TESTDIR/../hgext3rd/inhibit.py - > smartlog=$TESTDIR/../hgext3rd/smartlog.py - > [infinitepushbackup] - > enablestatus = True - > [experimental] - > evolution=createmarkers - > EOF - > } - $ . "$TESTDIR/library.sh" - $ . "$TESTDIR/library-infinitepush.sh" - $ setupcommon - -Setup server - $ hg init repo - $ cd repo - $ setupserver - $ cd .. - -Setup client - $ hg clone ssh://user@dummy/repo client -q - $ cd client - $ setup - $ now=`date +%s` - $ touch file1 - $ hg add file1 - $ commit_time=`expr $now - 15 \* 60` - $ hg commit -d "$commit_time 0" -m "Public changeset" - $ touch file2 - $ hg add file2 - $ commit_time=`expr $now - 15 \* 60` - $ hg commit -d "$commit_time 0" -m "Public changeset 2" - $ hg push - pushing to ssh://user@dummy/repo - searching for changes - remote: adding changesets - remote: adding manifests - remote: adding file changes - remote: added 2 changesets with 2 changes to 2 files - $ hg up 0 - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - $ echo a > file1 - $ changeset_time=`expr $now - 13 \* 60` - $ hg commit -d "$commit_time 0" -m "Backed up changeset" - created new head - $ echo a1 > file1 - $ changeset_time=`expr $now - 12 \* 60` - $ hg commit -d "$commit_time 0" -m "Backed up changeset 2" - $ hg pushbackup - starting backup .* (re) - searching for changes - remote: pushing 2 commits: - remote: * Backed up changeset (glob) - remote: * Backed up changeset 2 (glob) - finished in \d+\.(\d+)? seconds (re) - -Check hiding the backup head doesn't affect backed-up changesets - $ hg up -q 2 - $ hg log -T '{rev} {desc}\n' -r 'backedup()' - 2 Backed up changeset - 3 Backed up changeset 2 - $ hg log -T '{rev} {desc}\n' -r 'notbackedup()' - $ hg hide 3 - 1 changesets hidden - $ hg log -T '{rev} {desc}\n' -r 'backedup()' - 2 Backed up changeset - $ hg log -T '{rev} {desc}\n' -r 'notbackedup()' - $ hg unhide 3 - $ hg up -q 3 - -Create some changesets that aren't backed up - $ echo b > file1 - $ commit_time=`expr $now - 11 \* 60` - $ hg commit -d "$commit_time 0" -m "Not backed up changeset" - $ echo c > file1 - $ commit_time=`expr $now - 9 \* 60` - $ hg commit -d "$commit_time 0" -m "Backup pending changeset" - -Check backup status of commits - $ hg log -T '{rev} {desc}\n' -r 'backedup()' - 2 Backed up changeset - 3 Backed up changeset 2 - $ hg log -T '{rev} {desc}\n' -r 'draft() - backedup()' - 4 Not backed up changeset - 5 Backup pending changeset - $ hg log -T '{rev} {desc}\n' -r 'notbackedup()' - 4 Not backed up changeset - 5 Backup pending changeset - -Check smartlog output - $ hg smartlog - o changeset: 1:* (glob) - | user: test - | date: * (glob) - | summary: Public changeset 2 - | - | @ changeset: 5:* (glob) - | | tag: tip - | | user: test - | | date: * (glob) - | | summary: Backup pending changeset - | | - | o changeset: 4:* (glob) - | | user: test - | | date: * (glob) - | | summary: Not backed up changeset - | | - | o changeset: 3:* (glob) - | | user: test - | | date: * (glob) - | | summary: Backed up changeset 2 - | | - | o changeset: 2:* (glob) - |/ parent: 0:* (glob) - | user: test - | date: * (glob) - | summary: Backed up changeset - | - o changeset: 0:* (glob) - user: test - date: * (glob) - summary: Public changeset - - note: changeset * is not backed up. (glob) - Run `hg pushbackup` to perform a backup. If this fails, - please report to the Source Control @ FB group. - -Check smartlog summary can be suppressed - $ hg smartlog -T '{rev}: {desc}\n' --config infinitepushbackup.enablestatus=no - o 1: Public changeset 2 - | - | @ 5: Backup pending changeset - | | - | o 4: Not backed up changeset - | | - | o 3: Backed up changeset 2 - | | - | o 2: Backed up changeset - |/ - o 0: Public changeset - -Check smartlog summary with multiple unbacked up changesets - $ hg up 2 - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ echo b2 > file1 - $ commit_time=`expr $now - 11 \* 60` - $ hg commit -d "$commit_time 0" -m "Not backed up changeset 2" - created new head - $ hg smartlog -T '{rev}: {desc}\n' - o 1: Public changeset 2 - | - | @ 6: Not backed up changeset 2 - | | - | | o 5: Backup pending changeset - | | | - | | o 4: Not backed up changeset - | | | - | | o 3: Backed up changeset 2 - | |/ - | o 2: Backed up changeset - |/ - o 0: Public changeset - - note: 2 changesets are not backed up. - Run `hg pushbackup` to perform a backup. If this fails, - please report to the Source Control @ FB group. - -Check backup status with an unbacked up changeset that is disjoint from existing backups - $ hg up 1 - 2 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ echo b > file2 - $ commit_time=`expr $now - 11 \* 60` - $ hg commit -d "$commit_time 0" -m "Not backed up changeset 3" - $ hg log -T '{rev} {desc}\n' -r 'notbackedup()' - 4 Not backed up changeset - 5 Backup pending changeset - 6 Not backed up changeset 2 - 7 Not backed up changeset 3 - -Test template keyword for when a backup is in progress - $ hg log -T '{if(backingup,"Yes","No")}\n' -r . - No - $ echo fakelock > .hg/infinitepushbackup.lock - $ hg log -T '{if(backingup,"Yes","No")}\n' -r . - Yes - $ rm -f .hg/infinitepushbackup.lock - diff --git a/tests/test-infinitepush-backup.t b/tests/test-infinitepush-backup.t deleted file mode 100644 --- a/tests/test-infinitepush-backup.t +++ /dev/null @@ -1,603 +0,0 @@ - - $ cat >> $HGRCPATH << EOF - > [extensions] - > drawdag=$RUNTESTDIR/drawdag.py - > EOF - - $ setup() { - > cat << EOF >> .hg/hgrc - > [extensions] - > fbamend=$TESTDIR/../hgext3rd/fbamend - > [experimental] - > evolution=createmarkers - > EOF - > } - $ . "$TESTDIR/library.sh" - $ . "$TESTDIR/library-infinitepush.sh" - $ setupcommon - -Setup server - $ hg init repo - $ cd repo - $ setupserver - $ cd .. - -Backup empty repo - $ hg clone ssh://user@dummy/repo client -q - $ cd client - $ setup - $ hg pushbackup - starting backup .* (re) - finished in \d+\.(\d+)? seconds (re) - $ mkcommit commit - $ hg prune . - advice: 'hg hide' provides a better UI for hiding commits - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - working directory now at 000000000000 - 1 changesets pruned - $ mkcommit newcommit - $ hg pushbackup - starting backup .* (re) - searching for changes - remote: pushing 1 commit: - remote: 606a357e69ad newcommit - finished in \d+\.(\d+)? seconds (re) - -Re-clone the client - $ cd .. - $ rm -rf client - $ hg clone ssh://user@dummy/repo client -q - $ cd client - -Setup client - $ setup - -Make commit and backup it. Use lockfail.py to make sure lock is not taken during -pushbackup - $ mkcommit commit - $ hg pushbackup --config extensions.lockfail=$TESTDIR/lockfail.py - starting backup .* (re) - searching for changes - remote: pushing 1 commit: - remote: 7e6a6fd9c7c8 commit - finished in \d+\.(\d+)? seconds (re) - $ scratchnodes - 606a357e69adb2e36d559ae3237626e82a955c9d 9fa7f02468b18919035248ab21c8267674c0a3d6 - 7e6a6fd9c7c8c8c307ee14678f03d63af3a7b455 168423c30397d95ef5f44d883f0887f0f5be0936 - $ scratchbookmarks - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/heads/7e6a6fd9c7c8c8c307ee14678f03d63af3a7b455 7e6a6fd9c7c8c8c307ee14678f03d63af3a7b455 (re) - -Make first commit public (by doing push) and then backup new commit - $ hg push - pushing to ssh://user@dummy/repo - searching for changes - remote: adding changesets - remote: adding manifests - remote: adding file changes - remote: added 1 changesets with 1 changes to 1 files - $ mkcommit newcommit - $ hg pushbackup - starting backup .* (re) - searching for changes - remote: pushing 1 commit: - remote: 94a60f5ad8b2 newcommit - finished in \d+\.(\d+)? seconds (re) - $ scratchbookmarks - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/heads/94a60f5ad8b2e007240007edab982b3638a3f38d 94a60f5ad8b2e007240007edab982b3638a3f38d (re) - -Create obsoleted commit - $ mkcommit obsoletedcommit - $ hg prune . - advice: 'hg hide' provides a better UI for hiding commits - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - working directory now at 94a60f5ad8b2 - 1 changesets pruned - -Make obsoleted commit non-extinct by committing on top of it - $ hg --hidden up 2 - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ mkcommit ontopofobsoleted - -Backup both of them - $ hg pushbackup - starting backup .* (re) - searching for changes - remote: pushing 3 commits: - remote: 94a60f5ad8b2 newcommit - remote: 361e89f06232 obsoletedcommit - remote: d5609f7fa633 ontopofobsoleted - finished in \d+\.(\d+)? seconds (re) - $ scratchbookmarks - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/heads/d5609f7fa63352da538eeffbe3ffabed1779aafc d5609f7fa63352da538eeffbe3ffabed1779aafc (re) - -Create one more head and run `hg pushbackup`. Make sure that only new head is pushed - $ hg up 0 - 0 files updated, 0 files merged, 3 files removed, 0 files unresolved - $ mkcommit newhead - created new head - $ hg pushbackup - starting backup .* (re) - searching for changes - remote: pushing 1 commit: - remote: 3a30e220fe42 newhead - finished in \d+\.(\d+)? seconds (re) - -Create two more heads and backup them - $ hg up 0 - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - $ mkcommit newhead1 - created new head - $ hg up 0 - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - $ mkcommit newhead2 - created new head - $ hg pushbackup - starting backup .* (re) - searching for changes - remote: pushing 2 commits: - remote: f79c5017def3 newhead1 - remote: 667453c0787e newhead2 - finished in \d+\.(\d+)? seconds (re) - -Backup in background - $ scratchbookmarks - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/heads/3a30e220fe42e969e34bbe8001b951a20f31f2e8 3a30e220fe42e969e34bbe8001b951a20f31f2e8 (re) - 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) - $ mkcommitautobackup newcommit - $ waitbgbackup - $ scratchbookmarks - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/heads/3a30e220fe42e969e34bbe8001b951a20f31f2e8 3a30e220fe42e969e34bbe8001b951a20f31f2e8 (re) - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/heads/773a3ba2e7c25358df2e5b3cced70371333bc61c 773a3ba2e7c25358df2e5b3cced70371333bc61c (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) - -Backup with bookmark - $ mkcommit commitwithbookmark - $ hg book abook - $ hg pushbackup - starting backup .* (re) - searching for changes - remote: pushing 3 commits: - remote: 667453c0787e newhead2 - remote: 773a3ba2e7c2 newcommit - remote: 166ff4468f7d commitwithbookmark - finished in \d+\.(\d+)? seconds (re) - $ scratchbookmarks - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/bookmarks/abook 166ff4468f7da443df90d268158ba7d75d52585a (re) - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/heads/166ff4468f7da443df90d268158ba7d75d52585a 166ff4468f7da443df90d268158ba7d75d52585a (re) - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/heads/3a30e220fe42e969e34bbe8001b951a20f31f2e8 3a30e220fe42e969e34bbe8001b951a20f31f2e8 (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) - -Backup only bookmarks - $ hg book newbook - $ hg pushbackup - starting backup .* (re) - finished in \d+\.(\d+)? seconds (re) - $ scratchbookmarks - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/bookmarks/abook 166ff4468f7da443df90d268158ba7d75d52585a (re) - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/bookmarks/newbook 166ff4468f7da443df90d268158ba7d75d52585a (re) - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/heads/166ff4468f7da443df90d268158ba7d75d52585a 166ff4468f7da443df90d268158ba7d75d52585a (re) - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/heads/3a30e220fe42e969e34bbe8001b951a20f31f2e8 3a30e220fe42e969e34bbe8001b951a20f31f2e8 (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) - -Nothing changed, make sure no backup happens - $ hg pushbackup - starting backup .* (re) - nothing to backup - finished in \d+\.(\d+)? seconds (re) - -Obsolete a head, make sure backup happens - $ hg prune . - advice: 'hg hide' provides a better UI for hiding commits - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - working directory now at 773a3ba2e7c2 - 1 changesets pruned - $ hg pushbackup - starting backup .* (re) - searching for changes - remote: pushing 2 commits: - remote: 667453c0787e newhead2 - remote: 773a3ba2e7c2 newcommit - finished in \d+\.(\d+)? seconds (re) - $ scratchbookmarks - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/bookmarks/abook 773a3ba2e7c25358df2e5b3cced70371333bc61c (re) - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/bookmarks/newbook 773a3ba2e7c25358df2e5b3cced70371333bc61c (re) - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/heads/3a30e220fe42e969e34bbe8001b951a20f31f2e8 3a30e220fe42e969e34bbe8001b951a20f31f2e8 (re) - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/heads/773a3ba2e7c25358df2e5b3cced70371333bc61c 773a3ba2e7c25358df2e5b3cced70371333bc61c (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) - -Rebase + backup. Make sure that two heads were deleted and head was saved - $ hg log --graph -T '{node} {desc}' - @ 773a3ba2e7c25358df2e5b3cced70371333bc61c newcommit - | - o 667453c0787e7830fdfb86db0f8c29aa7af2a1ea newhead2 - | - | o f79c5017def3b9af9928edbb52cc620c74b4b291 newhead1 - |/ - | o 3a30e220fe42e969e34bbe8001b951a20f31f2e8 newhead - |/ - | o d5609f7fa63352da538eeffbe3ffabed1779aafc ontopofobsoleted - | | - | x 361e89f06232897a098e3a11c49d9d8987da469d obsoletedcommit - | | - | o 94a60f5ad8b2e007240007edab982b3638a3f38d newcommit - |/ - o 7e6a6fd9c7c8c8c307ee14678f03d63af3a7b455 commit - - $ hg rebase -s f79c5017de -d 773a3ba2e7c2 - rebasing 5:f79c5017def3 "newhead1" - $ hg pushbackup - starting backup .* (re) - searching for changes - remote: pushing 3 commits: - remote: 667453c0787e newhead2 - remote: 773a3ba2e7c2 newcommit - remote: 2d2e01441947 newhead1 - finished in \d+\.(\d+)? seconds (re) - $ scratchbookmarks - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/bookmarks/abook 773a3ba2e7c25358df2e5b3cced70371333bc61c (re) - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/bookmarks/newbook 773a3ba2e7c25358df2e5b3cced70371333bc61c (re) - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/heads/2d2e01441947afbb6bb5ae0efbb901f3eebe3fbd 2d2e01441947afbb6bb5ae0efbb901f3eebe3fbd (re) - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/heads/3a30e220fe42e969e34bbe8001b951a20f31f2e8 3a30e220fe42e969e34bbe8001b951a20f31f2e8 (re) - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/heads/d5609f7fa63352da538eeffbe3ffabed1779aafc d5609f7fa63352da538eeffbe3ffabed1779aafc (re) -Make a few public commits. Make sure we don't backup them - $ hg up 2d2e01441947 - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - (leaving bookmark newbook) - $ mkcommit public1 - $ mkcommit public2 - $ hg log -r tip -T '{rev}' - 11 (no-eol) - $ hg push -r '2d2e01441947::.' - pushing to ssh://user@dummy/repo - searching for changes - remote: adding changesets - remote: adding manifests - remote: adding file changes - remote: added 5 changesets with 5 changes to 5 files - $ hg log --graph -T '{node} {desc} {phase}' - @ 3446a384dd701da41cd83cbd9562805fc6412c0e public2 public - | - o bd2412178ef2d3f3aaaf8a4f2385bdd64b5c5e54 public1 public - | - o 2d2e01441947afbb6bb5ae0efbb901f3eebe3fbd newhead1 public - | - o 773a3ba2e7c25358df2e5b3cced70371333bc61c newcommit public - | - o 667453c0787e7830fdfb86db0f8c29aa7af2a1ea newhead2 public - | - | o 3a30e220fe42e969e34bbe8001b951a20f31f2e8 newhead draft - |/ - | o d5609f7fa63352da538eeffbe3ffabed1779aafc ontopofobsoleted draft - | | - | x 361e89f06232897a098e3a11c49d9d8987da469d obsoletedcommit draft - | | - | o 94a60f5ad8b2e007240007edab982b3638a3f38d newcommit draft - |/ - o 7e6a6fd9c7c8c8c307ee14678f03d63af3a7b455 commit public - - $ hg pushbackup - starting backup .* (re) - finished in \d+\.(\d+)? seconds (re) - $ scratchbookmarks - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/bookmarks/abook 773a3ba2e7c25358df2e5b3cced70371333bc61c (re) - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/bookmarks/newbook 773a3ba2e7c25358df2e5b3cced70371333bc61c (re) - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/heads/3a30e220fe42e969e34bbe8001b951a20f31f2e8 3a30e220fe42e969e34bbe8001b951a20f31f2e8 (re) - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/heads/d5609f7fa63352da538eeffbe3ffabed1779aafc d5609f7fa63352da538eeffbe3ffabed1779aafc (re) - $ hg pushbackup - starting backup .* (re) - nothing to backup - finished in \d+\.(\d+)? seconds (re) - -Backup bookmark that has '/bookmarks/' in the name. Make sure it was escaped - $ hg book new/bookmarks/book - $ hg pushbackup - starting backup .* (re) - finished in \d+\.(\d+)? seconds (re) - $ scratchbookmarks - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/bookmarks/abook 773a3ba2e7c25358df2e5b3cced70371333bc61c (re) - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/bookmarks/new/bookmarksbookmarks/book 3446a384dd701da41cd83cbd9562805fc6412c0e (re) - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/bookmarks/newbook 773a3ba2e7c25358df2e5b3cced70371333bc61c (re) - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/heads/3a30e220fe42e969e34bbe8001b951a20f31f2e8 3a30e220fe42e969e34bbe8001b951a20f31f2e8 (re) - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/heads/d5609f7fa63352da538eeffbe3ffabed1779aafc d5609f7fa63352da538eeffbe3ffabed1779aafc (re) - -Backup to different path - $ cat >> .hg/hgrc << EOF - > [paths] - > default = brokenpath - > nondefault = ssh://user@dummy/repo - > EOF - $ hg book somebook - $ hg --config paths.default=brokenpath pushbackup - starting backup .* (re) - abort: repository $TESTTMP/client/brokenpath not found! - [255] - $ hg pushbackup nondefault --traceback - starting backup .* (re) - finished in \d+\.(\d+)? seconds (re) - $ scratchbookmarks - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/bookmarks/abook 773a3ba2e7c25358df2e5b3cced70371333bc61c (re) - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/bookmarks/new/bookmarksbookmarks/book 3446a384dd701da41cd83cbd9562805fc6412c0e (re) - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/bookmarks/newbook 773a3ba2e7c25358df2e5b3cced70371333bc61c (re) - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/bookmarks/somebook 3446a384dd701da41cd83cbd9562805fc6412c0e (re) - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/heads/3a30e220fe42e969e34bbe8001b951a20f31f2e8 3a30e220fe42e969e34bbe8001b951a20f31f2e8 (re) - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/heads/d5609f7fa63352da538eeffbe3ffabed1779aafc d5609f7fa63352da538eeffbe3ffabed1779aafc (re) - -Backup in background to different path - $ mkcommit backgroundcommittodifferentpath - $ hg pushbackup nondefault --background - $ waitbgbackup - $ scratchbookmarks - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/bookmarks/abook 773a3ba2e7c25358df2e5b3cced70371333bc61c (re) - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/bookmarks/new/bookmarksbookmarks/book 3446a384dd701da41cd83cbd9562805fc6412c0e (re) - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/bookmarks/newbook 773a3ba2e7c25358df2e5b3cced70371333bc61c (re) - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/bookmarks/somebook 268f86e364f9aed2f5bb9d11e2df6381ace129a2 (re) - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/heads/268f86e364f9aed2f5bb9d11e2df6381ace129a2 268f86e364f9aed2f5bb9d11e2df6381ace129a2 (re) - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/heads/3a30e220fe42e969e34bbe8001b951a20f31f2e8 3a30e220fe42e969e34bbe8001b951a20f31f2e8 (re) - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/heads/d5609f7fa63352da538eeffbe3ffabed1779aafc d5609f7fa63352da538eeffbe3ffabed1779aafc (re) - -Clean client and repo - $ cd .. - $ rm -rf repo - $ rm -rf client - $ hg init repo - $ cd repo - $ setupserver - $ cd .. - $ hg clone ssh://user@dummy/repo client -q - $ cd client - $ setup - -Create public commit - $ mkcommit initial - $ hg push - pushing to ssh://user@dummy/repo - searching for changes - remote: adding changesets - remote: adding manifests - remote: adding file changes - remote: added 1 changesets with 1 changes to 1 files - -Make commit and immediately obsolete it, then create a bookmark. -Make sure pushbackup works - $ mkcommit toobsolete - $ hg prune . - advice: 'hg hide' provides a better UI for hiding commits - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - working directory now at 630839011471 - 1 changesets pruned - $ hg book somebook - $ hg pushbackup - starting backup .* (re) - finished in \d+\.(\d+)? seconds (re) - $ scratchbookmarks - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/bookmarks/somebook 630839011471e17f808b92ab084bedfaca33b110 (re) - -Make secret commit and bookmark on top of it. Then run pushbackup. -Make sure it wasn't backed up. - $ hg book bookonsecret - $ echo secret >> secret - $ hg add secret - $ hg ci -Am secret --secret - $ hg pushbackup - starting backup .* (re) - nothing to backup - finished in \d+\.(\d+)? seconds (re) - $ scratchbookmarks - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/bookmarks/somebook 630839011471e17f808b92ab084bedfaca33b110 (re) - -Create two heads, set maxheadstobackup to 1, make sure only latest head was backed up - $ hg up -q 0 - $ mkcommit headone - created new head - $ hg up -q 0 - $ mkcommit headtwo - created new head - $ hg pushbackup --config infinitepushbackup.maxheadstobackup=1 - starting backup .* (re) - searching for changes - remote: pushing 1 commit: - remote: 6c4f4b30ae4c headtwo - finished in \d+\.(\d+)? seconds (re) - $ scratchbookmarks - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/bookmarks/somebook 630839011471e17f808b92ab084bedfaca33b110 (re) - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/heads/6c4f4b30ae4c2dd928d551836c70c741ee836650 6c4f4b30ae4c2dd928d551836c70c741ee836650 (re) - -Now set maxheadstobackup to 0 and backup again. Make sure nothing is backed up now - $ hg pushbackup --config infinitepushbackup.maxheadstobackup=0 - starting backup .* (re) - finished in \d+\.(\d+)? seconds (re) - $ scratchbookmarks - infinitepush/backups/test/[0-9a-zA-Z.-]+\$TESTTMP/client/bookmarks/somebook 630839011471e17f808b92ab084bedfaca33b110 (re) - -Test isbackedup command - $ hg pushbackup - starting backup .* (re) - searching for changes - remote: pushing 2 commits: - remote: cf2adfba1469 headone - remote: 6c4f4b30ae4c headtwo - finished in \d+\.(\d+)? seconds (re) - $ hg isbackedup - 6c4f4b30ae4c2dd928d551836c70c741ee836650 backed up - $ hg isbackedup -r 630839011471e17 - 630839011471e17f808b92ab084bedfaca33b110 not backed up - $ hg isbackedup -r . -r 630839011471e17 - 6c4f4b30ae4c2dd928d551836c70c741ee836650 backed up - 630839011471e17f808b92ab084bedfaca33b110 not backed up - -Delete backup state file and try again - $ rm .hg/infinitepushbackupstate - $ hg isbackedup -r . -r 630839011471e17 - 6c4f4b30ae4c2dd928d551836c70c741ee836650 not backed up - 630839011471e17f808b92ab084bedfaca33b110 not backed up - -Prune commit and then inhibit obsmarkers. Make sure isbackedup still works - $ hg pushbackup - starting backup .* (re) - searching for changes - remote: pushing 2 commits: - remote: cf2adfba1469 headone - remote: 6c4f4b30ae4c headtwo - finished in \d+\.(\d+)? seconds (re) - $ hg prune 6c4f4b30ae4c2dd928d551836c70c741ee836650 - advice: 'hg hide' provides a better UI for hiding commits - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - working directory now at 630839011471 - 1 changesets pruned - $ hg isbackedup -r 6c4f4b30ae4c2dd928d551836c70c741ee836650 - 6c4f4b30ae4c2dd928d551836c70c741ee836650 backed up - -Test backupgeneration config option. If this config option value changes then -new full backup should be made. - $ hg pushbackup - starting backup .* (re) - finished in \d+\.(\d+)? seconds (re) - $ hg pushbackup --config infinitepushbackup.backupgeneration=1 - starting backup .* (re) - searching for changes - remote: pushing 1 commit: - remote: cf2adfba1469 headone - finished in \d+\.(\d+)? seconds (re) - -Next backup with the same backup generation value should not trigger full backup - $ hg pushbackup --config infinitepushbackup.backupgeneration=1 - starting backup .* (re) - nothing to backup - finished in \d+\.(\d+)? seconds (re) - $ cat .hg/infinitepushbackupgeneration - 1 (no-eol) - -Print garbage to infinitepushbackupgeneration file, make sure backup works fine - $ echo 'garbage' > .hg/infinitepushbackupgeneration - $ hg pushbackup - starting backup .* (re) - nothing to backup - finished in \d+\.(\d+)? seconds (re) - -Delete infinitepushbackupstate and set backupgeneration. Make sure it doesn't fail - $ rm .hg/infinitepushbackupstate - $ hg pushbackup --config infinitepushbackup.backupgeneration=2 - starting backup * (glob) - searching for changes - remote: pushing 1 commit: - remote: cf2adfba1469 headone - finished in * seconds (glob) - -Test hostname option - $ rm .hg/infinitepushbackupstate - $ hg pushbackup --config infinitepushbackup.hostname=hostname - starting backup * (glob) - searching for changes - remote: pushing 1 commit: - remote: cf2adfba1469 headone - finished in \d+\.(\d+)? seconds (re) - $ scratchbookmarks | grep test/hostname - infinitepush/backups/test/hostname$TESTTMP/client/bookmarks/somebook 630839011471e17f808b92ab084bedfaca33b110 - infinitepush/backups/test/hostname$TESTTMP/client/heads/cf2adfba146909529bcca8c1626de6b4d9e73846 cf2adfba146909529bcca8c1626de6b4d9e73846 - -Malformed backup state file - $ echo rubbish > .hg/infinitepushbackupstate - $ hg pushbackup - starting backup * (glob) - corrupt file: infinitepushbackupstate (No JSON object could be decoded) - searching for changes - remote: pushing 1 commit: - remote: cf2adfba1469 headone - finished in * seconds (glob) - -Test backupinfo file generation. - $ [ -f .hg/infinitepushlatestbackupinfo ] - [1] - $ rm .hg/infinitepushbackupstate - $ hg pushbackup --config infinitepushbackup.savelatestbackupinfo=True - starting backup .* (re) - searching for changes - remote: pushing 1 commit: - remote: cf2adfba1469 headone - finished in \d+\.(\d+)? seconds (re) - $ cat .hg/infinitepushlatestbackupinfo - backuprevision=(\d+) (re) - backuptime=(\d+) (re) - -Run command that creates multiple transactions. Make sure that just one backup is started - $ cd .. - $ rm -rf client - $ hg clone ssh://user@dummy/repo client -q - $ cd client - $ setup - $ hg debugdrawdag <<'EOS' - > C - > | - > B D - > |/ - > A - > EOS - $ hg log -r ':' -G -T '{desc} {node}' - o C 26805aba1e600a82e93661149f2313866a221a7b - | - | o D b18e25de2cf5fc4699a029ed635882849e53ef73 - | | - o | B 112478962961147124edd43549aedd1a335e44bf - |/ - o A 426bada5c67598ca65036d57d9e4b64b0c1ce7a0 - - @ initial 630839011471e17f808b92ab084bedfaca33b110 - - -Create logs directory and set correct permissions - $ setuplogdir - - $ hg pushbackup --config infinitepushbackup.logdir=$TESTTMP/logs - starting backup .* (re) - searching for changes - remote: pushing 4 commits: - remote: 426bada5c675 A - remote: 112478962961 B - remote: b18e25de2cf5 D - remote: 26805aba1e60 C - finished in \d+\.(\d+)? seconds (re) - $ hg isbackedup -r ':' - 630839011471e17f808b92ab084bedfaca33b110 not backed up - 426bada5c67598ca65036d57d9e4b64b0c1ce7a0 backed up - 112478962961147124edd43549aedd1a335e44bf backed up - b18e25de2cf5fc4699a029ed635882849e53ef73 backed up - 26805aba1e600a82e93661149f2313866a221a7b backed up - $ hg rebase -s B -d D --config infinitepushbackup.autobackup=True --config infinitepushbackup.logdir=$TESTTMP/logs - rebasing 2:112478962961 "B" (B) - rebasing 4:26805aba1e60 "C" (C tip) - $ waitbgbackup - $ hg log -r ':' -G -T '{desc} {node}' - o C ffeec75ec60331057b875fc5356c57c3ff204500 - | - o B 1ef11233b74dfa8b57e8285fd6f546096af8f4c2 - | - | x C 26805aba1e600a82e93661149f2313866a221a7b - | | - o | D b18e25de2cf5fc4699a029ed635882849e53ef73 - | | - | x B 112478962961147124edd43549aedd1a335e44bf - |/ - o A 426bada5c67598ca65036d57d9e4b64b0c1ce7a0 - - @ initial 630839011471e17f808b92ab084bedfaca33b110 - - $ hg isbackedup -r 'ffeec75ec + 1ef11233b7' - ffeec75ec60331057b875fc5356c57c3ff204500 backed up - 1ef11233b74dfa8b57e8285fd6f546096af8f4c2 backed up - -Check the logs, make sure just one process was started - $ cat $TESTTMP/logs/test/* - starting backup .* (re) - searching for changes - remote: pushing 4 commits: - remote: 426bada5c675 A - remote: b18e25de2cf5 D - remote: 1ef11233b74d B - remote: ffeec75ec603 C - finished in \d+\.(\d+)? seconds (re) diff --git a/tests/test-infinitepush-bundlestore.t b/tests/test-infinitepush-bundlestore.t deleted file mode 100644 --- a/tests/test-infinitepush-bundlestore.t +++ /dev/null @@ -1,822 +0,0 @@ - -Create an ondisk bundlestore in .hg/scratchbranches - $ . "$TESTDIR/library.sh" - $ . "$TESTDIR/library-infinitepush.sh" - $ cp $HGRCPATH $TESTTMP/defaulthgrc - $ setupcommon - $ hg init repo - $ cd repo - -Check that we can send a scratch on the server and it does not show there in -the history but is stored on disk - $ setupserver - $ cd .. - $ hg clone ssh://user@dummy/repo client -q - $ cd client - $ mkcommit initialcommit - $ hg push -r . --create - pushing to ssh://user@dummy/repo - searching for changes - remote: adding changesets - remote: adding manifests - remote: adding file changes - remote: added 1 changesets with 1 changes to 1 files - $ mkcommit scratchcommit - $ hg push -r . --to scratch/mybranch --create - pushing to ssh://user@dummy/repo - searching for changes - remote: pushing 1 commit: - remote: 20759b6926ce scratchcommit - $ hg log -G - @ changeset: 1:20759b6926ce - | bookmark: scratch/mybranch - | tag: tip - | user: test - | date: Thu Jan 01 00:00:00 1970 +0000 - | summary: scratchcommit - | - o changeset: 0:67145f466344 - user: test - date: Thu Jan 01 00:00:00 1970 +0000 - summary: initialcommit - - $ hg log -G -R ../repo - o changeset: 0:67145f466344 - tag: tip - user: test - date: Thu Jan 01 00:00:00 1970 +0000 - summary: initialcommit - - $ find ../repo/.hg/scratchbranches | sort - ../repo/.hg/scratchbranches - ../repo/.hg/scratchbranches/filebundlestore - ../repo/.hg/scratchbranches/filebundlestore/b9 - ../repo/.hg/scratchbranches/filebundlestore/b9/e1 - ../repo/.hg/scratchbranches/filebundlestore/b9/e1/b9e1ee5f93fb6d7c42496fc176c09839639dd9cc - ../repo/.hg/scratchbranches/index - ../repo/.hg/scratchbranches/index/bookmarkmap - ../repo/.hg/scratchbranches/index/bookmarkmap/scratch - ../repo/.hg/scratchbranches/index/bookmarkmap/scratch/mybranch - ../repo/.hg/scratchbranches/index/nodemap - ../repo/.hg/scratchbranches/index/nodemap/20759b6926ce827d5a8c73eb1fa9726d6f7defb2 - -From another client we can get the scratchbranch if we ask for it explicitely - - $ cd .. - $ hg clone ssh://user@dummy/repo client2 -q - $ cd client2 - $ hg pull -B scratch/mybranch --traceback - pulling from ssh://user@dummy/repo - searching for changes - adding changesets - adding manifests - adding file changes - added 1 changesets with 1 changes to 1 files - new changesets 20759b6926ce - (run 'hg update' to get a working copy) - $ hg log -G - o changeset: 1:20759b6926ce - | bookmark: scratch/mybranch - | tag: tip - | user: test - | date: Thu Jan 01 00:00:00 1970 +0000 - | summary: scratchcommit - | - @ changeset: 0:67145f466344 - user: test - date: Thu Jan 01 00:00:00 1970 +0000 - summary: initialcommit - - $ cd .. - -Push to non-scratch bookmark - - $ cd client - $ hg up 0 - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - $ mkcommit newcommit - created new head - $ hg push -r . - pushing to ssh://user@dummy/repo - searching for changes - remote: adding changesets - remote: adding manifests - remote: adding file changes - remote: added 1 changesets with 1 changes to 1 files - $ hg log -G -T '{desc} {phase} {bookmarks}' - @ newcommit public - | - | o scratchcommit draft scratch/mybranch - |/ - o initialcommit public - - -Push to scratch branch - $ cd ../client2 - $ hg up -q scratch/mybranch - $ mkcommit 'new scratch commit' - $ hg push -r . --to scratch/mybranch - pushing to ssh://user@dummy/repo - searching for changes - remote: pushing 2 commits: - remote: 20759b6926ce scratchcommit - remote: 1de1d7d92f89 new scratch commit - $ hg log -G -T '{desc} {phase} {bookmarks}' - @ new scratch commit draft scratch/mybranch - | - o scratchcommit draft - | - o initialcommit public - - $ scratchnodes - 1de1d7d92f8965260391d0513fe8a8d5973d3042 bed63daed3beba97fff2e819a148cf415c217a85 - 20759b6926ce827d5a8c73eb1fa9726d6f7defb2 bed63daed3beba97fff2e819a148cf415c217a85 - - $ scratchbookmarks - scratch/mybranch 1de1d7d92f8965260391d0513fe8a8d5973d3042 - -Push scratch bookmark with no new revs - $ hg push -r . --to scratch/anotherbranch --create - pushing to ssh://user@dummy/repo - searching for changes - remote: pushing 2 commits: - remote: 20759b6926ce scratchcommit - remote: 1de1d7d92f89 new scratch commit - $ hg log -G -T '{desc} {phase} {bookmarks}' - @ new scratch commit draft scratch/anotherbranch scratch/mybranch - | - o scratchcommit draft - | - o initialcommit public - - $ scratchbookmarks - scratch/anotherbranch 1de1d7d92f8965260391d0513fe8a8d5973d3042 - scratch/mybranch 1de1d7d92f8965260391d0513fe8a8d5973d3042 - -Pull scratch and non-scratch bookmark at the same time - - $ hg -R ../repo book newbook - $ cd ../client - $ hg pull -B newbook -B scratch/mybranch --traceback - pulling from ssh://user@dummy/repo - searching for changes - adding changesets - adding manifests - adding file changes - added 1 changesets with 1 changes to 2 files - adding remote bookmark newbook - new changesets 1de1d7d92f89 - (run 'hg update' to get a working copy) - $ hg log -G -T '{desc} {phase} {bookmarks}' - o new scratch commit draft scratch/mybranch - | - | @ newcommit public - | | - o | scratchcommit draft - |/ - o initialcommit public - - -Push scratch revision without bookmark with --bundle-store - - $ hg up -q tip - $ mkcommit scratchcommitnobook - $ hg log -G -T '{desc} {phase} {bookmarks}' - @ scratchcommitnobook draft - | - o new scratch commit draft scratch/mybranch - | - | o newcommit public - | | - o | scratchcommit draft - |/ - o initialcommit public - - $ hg push -r . --bundle-store - pushing to ssh://user@dummy/repo - searching for changes - remote: pushing 3 commits: - remote: 20759b6926ce scratchcommit - remote: 1de1d7d92f89 new scratch commit - remote: 2b5d271c7e0d scratchcommitnobook - $ hg -R ../repo log -G -T '{desc} {phase}' - o newcommit public - | - o initialcommit public - - - $ scratchnodes - 1de1d7d92f8965260391d0513fe8a8d5973d3042 66fa08ff107451320512817bed42b7f467a1bec3 - 20759b6926ce827d5a8c73eb1fa9726d6f7defb2 66fa08ff107451320512817bed42b7f467a1bec3 - 2b5d271c7e0d25d811359a314d413ebcc75c9524 66fa08ff107451320512817bed42b7f467a1bec3 - -Test with pushrebase - $ cp $TESTTMP/defaulthgrc $HGRCPATH - $ cat >> $HGRCPATH << EOF - > [extensions] - > pushrebase=$TESTDIR/../hgext3rd/pushrebase.py - > infinitepush=$TESTDIR/../infinitepush - > [infinitepush] - > branchpattern=re:scratch/.+ - > [ui] - > ssh = python "$TESTDIR/dummyssh" - > EOF - $ mkcommit scratchcommitwithpushrebase - $ hg push -r . --to scratch/mybranch - pushing to ssh://user@dummy/repo - searching for changes - remote: pushing 4 commits: - remote: 20759b6926ce scratchcommit - remote: 1de1d7d92f89 new scratch commit - remote: 2b5d271c7e0d scratchcommitnobook - remote: d8c4f54ab678 scratchcommitwithpushrebase - $ hg -R ../repo log -G -T '{desc} {phase}' - o newcommit public - | - o initialcommit public - - $ scratchnodes - 1de1d7d92f8965260391d0513fe8a8d5973d3042 e3cb2ac50f9e1e6a5ead3217fc21236c84af4397 - 20759b6926ce827d5a8c73eb1fa9726d6f7defb2 e3cb2ac50f9e1e6a5ead3217fc21236c84af4397 - 2b5d271c7e0d25d811359a314d413ebcc75c9524 e3cb2ac50f9e1e6a5ead3217fc21236c84af4397 - d8c4f54ab678fd67cb90bb3f272a2dc6513a59a7 e3cb2ac50f9e1e6a5ead3217fc21236c84af4397 - -Change the order of pushrebase and infinitepush - $ cp $TESTTMP/defaulthgrc $HGRCPATH - $ cat >> $HGRCPATH << EOF - > [extensions] - > infinitepush=$TESTDIR/../infinitepush - > pushrebase=$TESTDIR/../hgext3rd/pushrebase.py - > [infinitepush] - > branchpattern=re:scratch/.+ - > [ui] - > ssh = python "$TESTDIR/dummyssh" - > EOF - $ mkcommit scratchcommitwithpushrebase2 - $ hg push -r . --to scratch/mybranch - pushing to ssh://user@dummy/repo - searching for changes - remote: pushing 5 commits: - remote: 20759b6926ce scratchcommit - remote: 1de1d7d92f89 new scratch commit - remote: 2b5d271c7e0d scratchcommitnobook - remote: d8c4f54ab678 scratchcommitwithpushrebase - remote: 6c10d49fe927 scratchcommitwithpushrebase2 - $ hg -R ../repo log -G -T '{desc} {phase}' - o newcommit public - | - o initialcommit public - - $ scratchnodes - 1de1d7d92f8965260391d0513fe8a8d5973d3042 cd0586065eaf8b483698518f5fc32531e36fd8e0 - 20759b6926ce827d5a8c73eb1fa9726d6f7defb2 cd0586065eaf8b483698518f5fc32531e36fd8e0 - 2b5d271c7e0d25d811359a314d413ebcc75c9524 cd0586065eaf8b483698518f5fc32531e36fd8e0 - 6c10d49fe92751666c40263f96721b918170d3da cd0586065eaf8b483698518f5fc32531e36fd8e0 - d8c4f54ab678fd67cb90bb3f272a2dc6513a59a7 cd0586065eaf8b483698518f5fc32531e36fd8e0 - -Non-fastforward scratch bookmark push - $ hg up 6c10d49fe927 - 0 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ echo 1 > amend - $ hg add amend - $ hg ci --amend -m 'scratch amended commit' - saved backup bundle to $TESTTMP/client/.hg/strip-backup/6c10d49fe927-c99ffec5-amend.hg (glob) - $ hg log -G -T '{desc} {phase} {bookmarks}' - @ scratch amended commit draft scratch/mybranch - | - o scratchcommitwithpushrebase draft - | - o scratchcommitnobook draft - | - o new scratch commit draft - | - | o newcommit public - | | - o | scratchcommit draft - |/ - o initialcommit public - - - $ scratchbookmarks - scratch/anotherbranch 1de1d7d92f8965260391d0513fe8a8d5973d3042 - scratch/mybranch 6c10d49fe92751666c40263f96721b918170d3da - $ hg push -r . --to scratch/mybranch - pushing to ssh://user@dummy/repo - searching for changes - remote: non-forward push - remote: (use --non-forward-move to override) - abort: push failed on remote - [255] - - $ hg push -r . --to scratch/mybranch --non-forward-move - pushing to ssh://user@dummy/repo - searching for changes - remote: pushing 5 commits: - remote: 20759b6926ce scratchcommit - remote: 1de1d7d92f89 new scratch commit - remote: 2b5d271c7e0d scratchcommitnobook - remote: d8c4f54ab678 scratchcommitwithpushrebase - remote: 8872775dd97a scratch amended commit - $ scratchbookmarks - scratch/anotherbranch 1de1d7d92f8965260391d0513fe8a8d5973d3042 - scratch/mybranch 8872775dd97a750e1533dc1fbbca665644b32547 - $ hg log -G -T '{desc} {phase} {bookmarks}' - @ scratch amended commit draft scratch/mybranch - | - o scratchcommitwithpushrebase draft - | - o scratchcommitnobook draft - | - o new scratch commit draft - | - | o newcommit public - | | - o | scratchcommit draft - |/ - o initialcommit public - -Check that push path is not ignored. Add new path to the hgrc - $ cat >> .hg/hgrc << EOF - > [paths] - > peer=ssh://user@dummy/client2 - > EOF - -Checkout last non-scrath commit - $ hg up 91894e11e8255 - 1 files updated, 0 files merged, 6 files removed, 0 files unresolved - $ mkcommit peercommit -Use --force because this push creates new head - $ hg push peer -r . -f - pushing to ssh://user@dummy/client2 - searching for changes - remote: adding changesets - remote: adding manifests - remote: adding file changes - remote: added 2 changesets with 2 changes to 2 files (+1 heads) - $ hg -R ../repo log -G -T '{desc} {phase} {bookmarks}' - o newcommit public - | - o initialcommit public - - $ hg -R ../client2 log -G -T '{desc} {phase} {bookmarks}' - o peercommit public - | - o newcommit public - | - | @ new scratch commit draft scratch/anotherbranch scratch/mybranch - | | - | o scratchcommit draft - |/ - o initialcommit public - - $ hg book --list-remote scratch/* - scratch/anotherbranch 1de1d7d92f8965260391d0513fe8a8d5973d3042 - scratch/mybranch 8872775dd97a750e1533dc1fbbca665644b32547 - $ hg book --list-remote - abort: --list-remote requires a bookmark pattern - (use "hg book" to get a list of your local bookmarks) - [255] - $ hg book --config infinitepush.defaultremotepatterns=scratch/another* --list-remote - abort: --list-remote requires a bookmark pattern - (use "hg book" to get a list of your local bookmarks) - [255] - $ hg book --list-remote scratch/my - $ hg book --list-remote scratch/my* - scratch/mybranch 8872775dd97a750e1533dc1fbbca665644b32547 - $ hg book --list-remote scratch/my* -T json - [ - { - "bookmark": "scratch/mybranch", - "node": "8872775dd97a750e1533dc1fbbca665644b32547" - } - ] - $ cd ../repo - $ hg book scratch/serversidebook - $ hg book serversidebook - $ cd ../client - $ hg book --list-remote scratch/* -T json - [ - { - "bookmark": "scratch/anotherbranch", - "node": "1de1d7d92f8965260391d0513fe8a8d5973d3042" - }, - { - "bookmark": "scratch/mybranch", - "node": "8872775dd97a750e1533dc1fbbca665644b32547" - }, - { - "bookmark": "scratch/serversidebook", - "node": "0000000000000000000000000000000000000000" - } - ] - -Push to svn server should fail - $ hg push svn+ssh://svn.vip.facebook.com/svnroot/tfb/trunk/www -r . --to scratch/serversidebook - abort: infinite push does not work with svn repo - (did you forget to `hg push default`?) - [255] - -Scratch pull of pruned commits - $ cat >> .hg/hgrc << EOF - > [extensions] - > fbamend=$TESTDIR/../hgext3rd/fbamend - > inhibit=$TESTDIR/../hgext3rd/inhibit.py - > [experimental] - > evolution=createmarkers - > EOF - $ hg prune -r scratch/mybranch - advice: 'hg hide' provides a better UI for hiding commits - 1 changesets pruned - $ hg log -r 'reverse(::scratch/mybranch)' -T '{desc}\n' - scratchcommitwithpushrebase - scratchcommitnobook - new scratch commit - scratchcommit - initialcommit - $ hg pull -B scratch/mybranch - pulling from ssh://user@dummy/repo - no changes found - adding changesets - adding manifests - adding file changes - added 0 changesets with 0 changes to 6 files - adding remote bookmark scratch/serversidebook - adding remote bookmark serversidebook - $ hg log -r 'reverse(::scratch/mybranch)' -T '{desc}\n' - scratch amended commit - scratchcommitwithpushrebase - scratchcommitnobook - new scratch commit - scratchcommit - initialcommit - -Prune it again and pull it via commit hash - $ hg log -r scratch/mybranch -T '{node}\n' - 8872775dd97a750e1533dc1fbbca665644b32547 - $ hg prune -r scratch/mybranch - advice: 'hg hide' provides a better UI for hiding commits - 1 changesets pruned - $ hg log -G -T '{node|short} {desc} {bookmarks}' - @ fe8283fe1190 peercommit - | - | o d8c4f54ab678 scratchcommitwithpushrebase scratch/mybranch - | | - | o 2b5d271c7e0d scratchcommitnobook - | | - | o 1de1d7d92f89 new scratch commit - | | - o | 91894e11e825 newcommit - | | - | o 20759b6926ce scratchcommit - |/ - o 67145f466344 initialcommit - -Have to use full hash because short hashes are not supported yet - $ hg pull -r 8872775dd97a750e1533dc1fbbca665644b32547 - pulling from ssh://user@dummy/repo - no changes found - adding changesets - adding manifests - adding file changes - added 0 changesets with 0 changes to 6 files - $ hg log -G -T '{node|short} {desc} {bookmarks}' - @ fe8283fe1190 peercommit - | - | o 8872775dd97a scratch amended commit - | | - | o d8c4f54ab678 scratchcommitwithpushrebase scratch/mybranch - | | - | o 2b5d271c7e0d scratchcommitnobook - | | - | o 1de1d7d92f89 new scratch commit - | | - o | 91894e11e825 newcommit - | | - | o 20759b6926ce scratchcommit - |/ - o 67145f466344 initialcommit - -Push new scratch head. Make sure that new bundle is created but 8872775dd97a -still in the old bundle - $ hg up scratch/mybranch - 4 files updated, 0 files merged, 2 files removed, 0 files unresolved - (activating bookmark scratch/mybranch) - $ mkcommit newscratchhead - created new head - $ hg push -r . --to scratch/newscratchhead --create - pushing to ssh://user@dummy/repo - searching for changes - remote: pushing 5 commits: - remote: 20759b6926ce scratchcommit - remote: 1de1d7d92f89 new scratch commit - remote: 2b5d271c7e0d scratchcommitnobook - remote: d8c4f54ab678 scratchcommitwithpushrebase - remote: 8611afacb870 newscratchhead - $ scratchnodes - 1de1d7d92f8965260391d0513fe8a8d5973d3042 d1b4f12087a79b2b1d342e222686a829d09d399b - 20759b6926ce827d5a8c73eb1fa9726d6f7defb2 d1b4f12087a79b2b1d342e222686a829d09d399b - 2b5d271c7e0d25d811359a314d413ebcc75c9524 d1b4f12087a79b2b1d342e222686a829d09d399b - 6c10d49fe92751666c40263f96721b918170d3da cd0586065eaf8b483698518f5fc32531e36fd8e0 - 8611afacb87078300a6d6b2f0c4b49fa506a8db9 d1b4f12087a79b2b1d342e222686a829d09d399b - 8872775dd97a750e1533dc1fbbca665644b32547 ac7f12436d58e685616ffc1f619bcecce8829e25 - d8c4f54ab678fd67cb90bb3f272a2dc6513a59a7 d1b4f12087a79b2b1d342e222686a829d09d399b - -Recreate the repo - $ cd .. - $ rm -rf repo - $ hg init repo - $ cd repo - $ setupserver - $ mkcommit initialcommit - $ hg phase --public . - -Recreate the clients - $ cd .. - $ rm -rf client - $ rm -rf client2 - $ hg clone ssh://user@dummy/repo client -q - -Create two heads. Push first head alone, then two heads together. Make sure that -multihead push works. - $ cd client - $ mkcommit multihead1 - $ hg up null - 0 files updated, 0 files merged, 2 files removed, 0 files unresolved - $ mkcommit multihead2 - created new head - $ hg push -r . --bundle-store - pushing to ssh://user@dummy/repo - searching for changes - remote: pushing 1 commit: - remote: ee4802bf6864 multihead2 - $ hg push -r '1:2' --bundle-store - pushing to ssh://user@dummy/repo - searching for changes - remote: pushing 2 commits: - remote: bc22f9a30a82 multihead1 - remote: ee4802bf6864 multihead2 - $ scratchnodes - bc22f9a30a821118244deacbd732e394ed0b686c ab1bc557aa090a9e4145512c734b6e8a828393a5 - ee4802bf6864326a6b3dcfff5a03abc2a0a69b8f ab1bc557aa090a9e4145512c734b6e8a828393a5 - -Create two new scratch bookmarks - $ hg up 0 - 1 files updated, 0 files merged, 1 files removed, 0 files unresolved - $ mkcommit scratchfirstpart - created new head - $ hg push -r . --to scratch/firstpart --create - pushing to ssh://user@dummy/repo - searching for changes - remote: pushing 1 commit: - remote: 176993b87e39 scratchfirstpart - $ hg up 0 - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - $ mkcommit scratchsecondpart - created new head - $ hg push -r . --to scratch/secondpart --create - pushing to ssh://user@dummy/repo - searching for changes - remote: pushing 1 commit: - remote: 8db3891c220e scratchsecondpart - -Pull two bookmarks from the second client - $ cd .. - $ hg clone ssh://user@dummy/repo client2 -q - $ cd client2 - $ hg pull -B scratch/firstpart -B scratch/secondpart - pulling from ssh://user@dummy/repo - searching for changes - adding changesets - adding manifests - adding file changes - added 1 changesets with 1 changes to 1 files - adding changesets - adding manifests - adding file changes - added 1 changesets with 1 changes to 1 files (+1 heads) - new changesets * (glob) - (run 'hg heads' to see heads, 'hg merge' to merge) - $ hg log -r scratch/secondpart -T '{node}' - 8db3891c220e216f6da214e8254bd4371f55efca (no-eol) - $ hg log -r scratch/firstpart -T '{node}' - 176993b87e39bd88d66a2cccadabe33f0b346339 (no-eol) -Make two commits to the scratch branch - $ mkcommit testpullbycommithash1 - created new head - $ hg log -r '.' -T '{node}\n' > ../testpullbycommithash1 - $ mkcommit testpullbycommithash2 - $ hg push -r . --to scratch/mybranch --create -q - -Create third client and pull by commit hash. -Make sure testpullbycommithash2 has not fetched - $ cd .. - $ hg clone ssh://user@dummy/repo client3 -q - $ cd client3 - $ hg pull -r `cat ../testpullbycommithash1` - pulling from ssh://user@dummy/repo - searching for changes - adding changesets - adding manifests - adding file changes - added 1 changesets with 1 changes to 1 files - new changesets 33910bfe6ffe - (run 'hg update' to get a working copy) - $ hg log -G -T '{desc} {phase} {bookmarks}' - o testpullbycommithash1 draft - | - @ initialcommit public - -Make public commit in the repo and pull it. -Make sure phase on the client is public. - $ cd ../repo - $ mkcommit publiccommit - $ hg phase --public . - $ cd ../client3 - $ hg pull - pulling from ssh://user@dummy/repo - searching for changes - adding changesets - adding manifests - adding file changes - added 1 changesets with 1 changes to 1 files (+1 heads) - new changesets a79b6597f322 - (run 'hg heads' to see heads, 'hg merge' to merge) - $ hg log -G -T '{desc} {phase} {bookmarks} {node|short}' - o publiccommit public a79b6597f322 - | - | o testpullbycommithash1 draft 33910bfe6ffe - |/ - @ initialcommit public 67145f466344 - - $ hg up a79b6597f322 - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ mkcommit scratchontopofpublic - $ hg push -r . --to scratch/scratchontopofpublic --create - pushing to ssh://user@dummy/repo - searching for changes - remote: pushing 1 commit: - remote: c70aee6da07d scratchontopofpublic - $ cd ../client2 - $ hg pull -B scratch/scratchontopofpublic - pulling from ssh://user@dummy/repo - searching for changes - adding changesets - adding manifests - adding file changes - added 1 changesets with 1 changes to 1 files (+1 heads) - adding changesets - adding manifests - adding file changes - added 1 changesets with 1 changes to 1 files - new changesets a79b6597f322:c70aee6da07d - (run 'hg heads .' to see heads, 'hg merge' to merge) - $ hg log -r scratch/scratchontopofpublic -T '{phase}' - draft (no-eol) -Strip scratchontopofpublic commit and do hg update - $ hg log -r tip -T '{node}\n' - c70aee6da07d7cdb9897375473690df3a8563339 - $ hg strip -q tip - $ hg up c70aee6da07d7cdb9897375473690df3a8563339 - 'c70aee6da07d7cdb9897375473690df3a8563339' does not exist locally - looking for it remotely... - pulling from ssh://user@dummy/repo - searching for changes - adding changesets - adding manifests - adding file changes - added 1 changesets with 1 changes to 1 files - new changesets c70aee6da07d - (run 'hg update' to get a working copy) - 'c70aee6da07d7cdb9897375473690df3a8563339' found remotely - 2 files updated, 0 files merged, 2 files removed, 0 files unresolved - -Trying to pull from bad path - $ hg strip -q tip - $ hg --config paths.default=badpath up c70aee6da07d7cdb9897375473690df3a8563339 - 'c70aee6da07d7cdb9897375473690df3a8563339' does not exist locally - looking for it remotely... - pulling from $TESTTMP/client2/badpath (glob) - pull failed: repository $TESTTMP/client2/badpath not found - abort: unknown revision 'c70aee6da07d7cdb9897375473690df3a8563339'! - [255] - -Strip commit and pull it using hg update with bookmark name - $ hg strip -q d8fde0ddfc96 - $ hg book -d scratch/mybranch - $ hg up scratch/mybranch - 'scratch/mybranch' does not exist locally - looking for it remotely... - pulling from ssh://user@dummy/repo - searching for changes - adding changesets - adding manifests - adding file changes - added 1 changesets with 1 changes to 2 files - new changesets d8fde0ddfc96 - (run 'hg update' to get a working copy) - 'scratch/mybranch' found remotely - 2 files updated, 0 files merged, 1 files removed, 0 files unresolved - (activating bookmark scratch/mybranch) - $ hg log -r scratch/mybranch -T '{node}' - d8fde0ddfc962183977f92d2bc52d303b8840f9d (no-eol) - -Test debugfillinfinitepushmetadata - $ cd ../repo - $ hg debugfillinfinitepushmetadata - abort: nodes are not specified - [255] - $ hg debugfillinfinitepushmetadata --node randomnode - abort: node randomnode is not found - [255] - $ hg debugfillinfinitepushmetadata --node d8fde0ddfc962183977f92d2bc52d303b8840f9d - $ cat .hg/scratchbranches/index/nodemetadatamap/d8fde0ddfc962183977f92d2bc52d303b8840f9d - {"changed_files": {"testpullbycommithash2": {"adds": 1, "isbinary": false, "removes": 0, "status": "added"}}} (no-eol) - - $ cd ../client - $ hg up d8fde0ddfc962183977f92d2bc52d303b8840f9d - 'd8fde0ddfc962183977f92d2bc52d303b8840f9d' does not exist locally - looking for it remotely... - pulling from ssh://user@dummy/repo - searching for changes - adding changesets - adding manifests - adding file changes - added 2 changesets with 2 changes to 2 files (+1 heads) - new changesets 33910bfe6ffe:d8fde0ddfc96 - (run 'hg heads .' to see heads, 'hg merge' to merge) - 'd8fde0ddfc962183977f92d2bc52d303b8840f9d' found remotely - 2 files updated, 0 files merged, 1 files removed, 0 files unresolved - $ echo file > file - $ hg add file - $ hg rm testpullbycommithash2 - $ hg ci -m 'add and rm files' - $ hg log -r . -T '{node}\n' - 3edfe7e9089ab9f728eb8e0d0c62a5d18cf19239 - $ hg cp file cpfile - $ hg mv file mvfile - $ hg ci -m 'cpfile and mvfile' - $ hg log -r . -T '{node}\n' - c7ac39f638c6b39bcdacf868fa21b6195670f8ae - $ hg push -r . --bundle-store - pushing to ssh://user@dummy/repo - searching for changes - remote: pushing 4 commits: - remote: 33910bfe6ffe testpullbycommithash1 - remote: d8fde0ddfc96 testpullbycommithash2 - remote: 3edfe7e9089a add and rm files - remote: c7ac39f638c6 cpfile and mvfile - $ cd ../repo - $ hg debugfillinfinitepushmetadata --node 3edfe7e9089ab9f728eb8e0d0c62a5d18cf19239 --node c7ac39f638c6b39bcdacf868fa21b6195670f8ae - $ cat .hg/scratchbranches/index/nodemetadatamap/3edfe7e9089ab9f728eb8e0d0c62a5d18cf19239 - {"changed_files": {"file": {"adds": 1, "isbinary": false, "removes": 0, "status": "added"}, "testpullbycommithash2": {"adds": 0, "isbinary": false, "removes": 1, "status": "removed"}}} (no-eol) - $ cat .hg/scratchbranches/index/nodemetadatamap/c7ac39f638c6b39bcdacf868fa21b6195670f8ae - {"changed_files": {"cpfile": {"adds": 1, "copies": "file", "isbinary": false, "removes": 0, "status": "added"}, "file": {"adds": 0, "isbinary": false, "removes": 1, "status": "removed"}, "mvfile": {"adds": 1, "copies": "file", "isbinary": false, "removes": 0, "status": "added"}}} (no-eol) - -Test infinitepush.metadatafilelimit number - $ cd ../client - $ echo file > file - $ hg add file - $ echo file1 > file1 - $ hg add file1 - $ echo file2 > file2 - $ hg add file2 - $ hg ci -m 'add many files' - $ hg log -r . -T '{node}' - 09904fb20c53ff351bd3b1d47681f569a4dab7e5 (no-eol) - $ hg push -r . --bundle-store - pushing to ssh://user@dummy/repo - searching for changes - remote: pushing 5 commits: - remote: 33910bfe6ffe testpullbycommithash1 - remote: d8fde0ddfc96 testpullbycommithash2 - remote: 3edfe7e9089a add and rm files - remote: c7ac39f638c6 cpfile and mvfile - remote: 09904fb20c53 add many files - - $ cd ../repo - $ hg debugfillinfinitepushmetadata --node 09904fb20c53ff351bd3b1d47681f569a4dab7e5 --config infinitepush.metadatafilelimit=2 - $ cat .hg/scratchbranches/index/nodemetadatamap/09904fb20c53ff351bd3b1d47681f569a4dab7e5 - {"changed_files": {"file": {"adds": 1, "isbinary": false, "removes": 0, "status": "added"}, "file1": {"adds": 1, "isbinary": false, "removes": 0, "status": "added"}}, "changed_files_truncated": true} (no-eol) - -Test infinitepush.fillmetadatabranchpattern - $ cd ../repo - $ cat >> .hg/hgrc << EOF - > [infinitepush] - > fillmetadatabranchpattern=re:scratch/fillmetadata/.* - > EOF - $ cd ../client - $ mkcommit tofillmetadata - $ hg log -r . -T '{node}\n' - d2b0410d4da084bc534b1d90df0de9eb21583496 - $ hg push -r . --to scratch/fillmetadata/fill --create - pushing to ssh://user@dummy/repo - searching for changes - remote: pushing 6 commits: - remote: 33910bfe6ffe testpullbycommithash1 - remote: d8fde0ddfc96 testpullbycommithash2 - remote: 3edfe7e9089a add and rm files - remote: c7ac39f638c6 cpfile and mvfile - remote: 09904fb20c53 add many files - remote: d2b0410d4da0 tofillmetadata - -Make sure background process finished - $ sleep 3 - $ cd ../repo - $ cat .hg/scratchbranches/index/nodemetadatamap/d2b0410d4da084bc534b1d90df0de9eb21583496 - {"changed_files": {"tofillmetadata": {"adds": 1, "isbinary": false, "removes": 0, "status": "added"}}} (no-eol) diff --git a/tests/test-infinitepush-delscratchbookmarks.t b/tests/test-infinitepush-delscratchbookmarks.t deleted file mode 100644 --- a/tests/test-infinitepush-delscratchbookmarks.t +++ /dev/null @@ -1,195 +0,0 @@ - $ . $TESTDIR/require-ext.sh remotenames - $ cat >> $HGRCPATH << EOF - > [extensions] - > infinitepush=$TESTDIR/../infinitepush - > remotenames= - > [infinitepush] - > branchpattern=re:scratch/.+ - > [ui] - > ssh = python "$TESTDIR/dummyssh" - > EOF - $ mkcommit() { - > echo "$1" > "$1" - > hg add "$1" - > hg ci -m "$1" - > } - -Create server repo - $ hg init repo - $ cd repo - $ hg branch scratch/serverbranch - marked working directory as branch scratch/serverbranch - (branches are permanent and global, did you want a bookmark?) - $ mkcommit servercommit - $ cat >> .hg/hgrc << EOF - > [infinitepush] - > server=yes - > indextype=disk - > storetype=disk - > EOF - $ cd .. - -Create second server repo - $ hg init repo2 - $ cd repo2 - $ hg branch scratch/serverbranch2 - marked working directory as branch scratch/serverbranch2 - (branches are permanent and global, did you want a bookmark?) - $ mkcommit servercommit2 - $ cat >> .hg/hgrc << EOF - > [infinitepush] - > server=yes - > indextype=disk - > storetype=disk - > EOF - $ cd .. - -Clone server - $ hg clone ssh://user@dummy/repo --config extensions.remotenames= client -q - $ cd client - -Ensure no bookmarks - $ hg book --remote - $ hg book - no bookmarks set - -Push scratch bookmark - $ mkcommit scratchcommit1 - $ hg push default -r . --to scratch/test1 --create - pushing to ssh://user@dummy/repo - searching for changes - remote: pushing 1 commit: - remote: 1c50a5acb795 scratchcommit1 - $ hg book --remote - default/scratch/test1 1:1c50a5acb795 - -Delete scratch bookmark - $ hg book -d scratch/test1 - $ hg book --remote - -Check regular deletion still works - $ hg book testlocal1 - $ hg book - * testlocal1 1:1c50a5acb795 - $ hg book -d testlocal1 - $ hg book - no bookmarks set - -Test deleting both regular and scratch - $ hg push default -r . --to scratch/test2 --create - pushing to ssh://user@dummy/repo - searching for changes - remote: pushing 1 commit: - remote: 1c50a5acb795 scratchcommit1 - $ hg book testlocal2 - $ hg book -a - * testlocal2 1:1c50a5acb795 - default/scratch/test2 1:1c50a5acb795 - $ hg book -d testlocal2 scratch/test2 - $ hg book -a - no bookmarks set - -Test deleting nonexistent bookmarks - $ hg book -d scratch/nonexistent1 - abort: infinitepush bookmark 'scratch/nonexistent1' does not exist in path 'default' - [255] - $ hg book -d localnonexistent1 - abort: bookmark 'localnonexistent1' does not exist - [255] - $ hg book -d scratch/nonexistent2 localnonexistent2 - abort: infinitepush bookmark 'scratch/nonexistent2' does not exist in path 'default' - [255] - -Test deleting a nonexistent bookmark with an existing branch that has the right name - $ hg branches --remote - default/scratch/serverbranch 0:022f0916e8d2 - $ hg book --remote - $ hg book -d scratch/serverbranch - abort: infinitepush bookmark 'scratch/serverbranch' does not exist in path 'default' - [255] - $ hg branches --remote - default/scratch/serverbranch 0:022f0916e8d2 - $ hg book --remote - -Test deleting a local bookmark that has a scratch-like name - $ hg book scratch/thisisalocalbm - $ hg book - * scratch/thisisalocalbm 1:1c50a5acb795 - $ hg book --remote - $ hg book -d scratch/thisisalocalbm - $ hg book - no bookmarks set - $ hg book --remote - -Prepare client to be pushed to for next tests - $ cat >> .hg/hgrc << EOF - > [infinitepush] - > server=yes - > indextype=disk - > storetype=disk - > EOF - -Test scratch bookmarks still pullable - $ cd .. - $ hg clone ssh://user@dummy/repo --config extensions.remotenames= client2 -q - $ cd client2 - $ hg book -a - no bookmarks set - $ hg pull -B scratch/test1 - pulling from ssh://user@dummy/repo - searching for changes - adding changesets - adding manifests - adding file changes - added 1 changesets with 1 changes to 1 files - new changesets 1c50a5acb795 - (run 'hg update' to get a working copy) - $ hg book -a - no bookmarks set - default/scratch/test1 1:1c50a5acb795 - $ hg up scratch/test1 - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ ls -a - . - .. - .hg - scratchcommit1 - servercommit - -Add a second remote - $ cat >> .hg/hgrc << EOF - > [paths] - > remote2 = ssh://user@dummy/client - > EOF - -Create some bookmarks on remote2 - $ mkcommit r2c - $ hg push remote2 -r . --to scratch/realscratch2 --create - pushing to ssh://user@dummy/client - searching for changes - remote: pushing 1 commit: - remote: 469a99aa48d4 r2c - $ hg book local2 - $ hg book -a - * local2 2:469a99aa48d4 - default/scratch/test1 1:1c50a5acb795 - remote2/scratch/realscratch2 2:469a99aa48d4 - -Delete all the things ! - $ hg book -d --remote-path default scratch/test1 - $ hg book -a - * local2 2:469a99aa48d4 - remote2/scratch/realscratch2 2:469a99aa48d4 - $ hg book -d --remote-path nosuchremote scratch/realscratch2 - abort: repository nosuchremote does not exist! - [255] - $ hg book -a - * local2 2:469a99aa48d4 - remote2/scratch/realscratch2 2:469a99aa48d4 - $ hg book -d --remote-path remote2 scratch/realscratch2 - $ hg book -a - * local2 2:469a99aa48d4 - $ hg book -d local2 - $ hg book -a - no bookmarks set - diff --git a/tests/test-infinitepush-lfs.t b/tests/test-infinitepush-lfs.t deleted file mode 100644 --- a/tests/test-infinitepush-lfs.t +++ /dev/null @@ -1,64 +0,0 @@ - -Setup common infinitepush - $ . "$TESTDIR/library.sh" - $ . "$TESTDIR/library-infinitepush.sh" - $ setupcommon - -Setup lfs - $ cat >> $HGRCPATH << EOF - > [experimental] - > changegroup3=True - > [extensions] - > lfs=$TESTDIR/../hgext3rd/lfs/ - > [lfs] - > threshold=10B - > url=file:$TESTTMP/dummy-remote/ - > EOF - -Setup server repo - $ hg init repo - $ cd repo - $ setupserver - $ echo 1 > 1 - $ hg add 1 - $ hg ci -m initial - -Setup client - $ cd .. - $ hg clone ssh://user@dummy/repo client -q - $ cd client - $ echo aaaaaaaaaaa > largefile - $ hg ci -Aqm commit - $ hg debugdata largefile 0 - version https://git-lfs.github.com/spec/v1 - oid sha256:ab483e1d855ad0ea27a68eeea02a04c1de6ccd2dc2c05e3a48c9a1ebb8af5f99 - size 12 - x-is-binary 0 - - $ hg push -r . --to scratch/lfscommit --create - pushing to ssh://user@dummy/repo - searching for changes - remote: pushing 1 commit: - remote: 0da81a72db1a commit - - $ scratchbookmarks - scratch/lfscommit 0da81a72db1a2d8256845e3808971f33e73d24c4 - - $ cd .. - -Setup another client - $ hg clone ssh://user@dummy/repo client2 -q - $ cd client2 - $ hg update scratch/lfscommit - 'scratch/lfscommit' does not exist locally - looking for it remotely... - pulling from ssh://user@dummy/repo - searching for changes - adding changesets - adding manifests - adding file changes - added 1 changesets with 1 changes to 1 files - new changesets 0da81a72db1a - (run 'hg update' to get a working copy) - 'scratch/lfscommit' found remotely - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - (activating bookmark scratch/lfscommit) diff --git a/tests/test-infinitepush-logging.t b/tests/test-infinitepush-logging.t deleted file mode 100644 --- a/tests/test-infinitepush-logging.t +++ /dev/null @@ -1,77 +0,0 @@ - $ . "$TESTDIR/library.sh" - $ . "$TESTDIR/library-infinitepush.sh" - $ setupcommon - $ hg init repo - $ cd repo - $ setupserver - $ cd .. - $ hg clone ssh://user@dummy/repo client -q - - $ cat >> $TESTTMP/uilog.py < from mercurial import extensions - > from mercurial import ui as uimod - > def uisetup(ui): - > extensions.wrapfunction(uimod.ui, 'log', mylog) - > def mylog(orig, self, service, *msg, **opts): - > if service in ['infinitepush']: - > kwstr = ", ".join("%s=%s" % (k, v) for k, v in - > sorted(opts.iteritems())) - > msgstr = msg[0] % msg[1:] - > self.warn('%s: %s (%s)\n' % (service, msgstr, kwstr)) - > return orig(self, service, *msg, **opts) - > EOF - $ cat >> repo/.hg/hgrc < [extensions] - > uilog=$TESTTMP/uilog.py - > EOF - - $ cd client - $ mkcommit commit - $ hg push -r . --to scratch/scratchpush --create - pushing to ssh://user@dummy/repo - searching for changes - remote: infinitepush: b2x:infinitepush \(eventtype=start, hostname=.+, requestid=\d+, user=\w+\) (re) - remote: pushing 1 commit: - remote: 7e6a6fd9c7c8 commit - remote: infinitepush: bundlestore \(bundlesize=546, eventtype=start, hostname=.+, reponame=babar, requestid=\d+, user=\w+\) (re) - remote: infinitepush: bundlestore \(bundlesize=546, elapsedms=.+, eventtype=success, hostname=.+, reponame=babar, requestid=\d+, user=\w+\) (re) - remote: infinitepush: index \(eventtype=start, hostname=.+, newheadscount=1, reponame=babar, requestid=\d+, user=\w+\) (re) - remote: infinitepush: index \(elapsedms=.+, eventtype=success, hostname=.+, newheadscount=1, reponame=babar, requestid=\d+, user=\w+\) (re) - remote: infinitepush: b2x:infinitepush \(elapsedms=.+, eventtype=success, hostname=.+, reponame=babar, requestid=\d+, user=\w+\) (re) - $ cd .. - -Check that logging works for b2x:infinitepushscratchbookmarks part - $ cd client - $ hg pushbackup - starting backup .* (re) - searching for changes - remote: infinitepush: b2x:infinitepush \(eventtype=start, hostname=.+, reponame=babar, requestid=\d+, user=\w+\) (re) - remote: pushing 1 commit: - remote: 7e6a6fd9c7c8 commit - remote: infinitepush: index \(eventtype=start, hostname=.+, newheadscount=0, reponame=babar, requestid=\d+, user=\w+\) (re) - remote: infinitepush: index \(elapsedms=.+, eventtype=success, hostname=.+, newheadscount=0, reponame=babar, requestid=\d+, user=\w+\) (re) - remote: infinitepush: b2x:infinitepush \(elapsedms=.+, eventtype=success, hostname=.+, reponame=babar, requestid=\d+, user=\w+\) (re) - remote: infinitepush: b2x:infinitepushscratchbookmarks \(eventtype=start, hostname=.+, reponame=babar, requestid=\d+, user=\w+\) (re) - remote: infinitepush: b2x:infinitepushscratchbookmarks \(elapsedms=.+, eventtype=success, hostname=.+, reponame=babar, requestid=\d+, user=\w+\) (re) - finished in \d+\.(\d+)? seconds (re) - $ cd .. - -Make upload to bundlestore fail - $ cat >> repo/.hg/hgrc < [scratchbranch] - > storepath=/dev/null - > EOF - $ cd client - $ mkcommit failpushcommit - $ hg push -r . --to scratch/scratchpush 2> /dev/null - pushing to ssh://user@dummy/repo - searching for changes - remote: infinitepush: b2x:infinitepush \(eventtype=start, hostname=.+, requestid=\d+, user=\w+\) (re) - remote: pushing 2 commits: - remote: 7e6a6fd9c7c8 commit - remote: bba29d9d577a failpushcommit - remote: infinitepush: bundlestore \(bundlesize=1067, eventtype=start, hostname=.+, requestid=\d+, user=\w+\) (re) - remote: infinitepush: bundlestore \(bundlesize=1067, elapsedms=[-+0-9.e]+, errormsg=\[Errno 20\] Not a directory: '/dev/null/\d+', eventtype=failure, hostname=.+, reponame=babar, requestid=\d+, user=\w+\) (re) - remote: infinitepush: b2x:infinitepush \(elapsedms=[-+0-9.e]+, errormsg=\[Errno 20\] Not a directory: '/dev/null/\d+', eventtype=failure, hostname=.+, reponame=babar, requestid=\d+, user=\w+\) (re) - remote: abort: Not a directory: '/dev/null/\d+' (re) - [255] diff --git a/tests/test-infinitepush-pullback-obsolete.t b/tests/test-infinitepush-pullback-obsolete.t deleted file mode 100644 --- a/tests/test-infinitepush-pullback-obsolete.t +++ /dev/null @@ -1,64 +0,0 @@ - - $ setup() { - > cat << EOF >> .hg/hgrc - > [extensions] - > fbamend=$TESTDIR/../hgext3rd/fbamend - > [experimental] - > evolution=createmarkers - > EOF - > } - - $ . "$TESTDIR/library.sh" - $ . "$TESTDIR/library-infinitepush.sh" - $ setupcommon - -Setup server - $ hg init repo - $ cd repo - $ setupserver - $ cd .. - -Setup backupsource - $ hg clone ssh://user@dummy/repo backupsource -q - $ cd backupsource - $ setup - -Do a normal backup - $ mkcommit first - $ hg pushbackup - starting backup .* (re) - searching for changes - remote: pushing 1 commit: - remote: b75a450e74d5 first - finished in \d+\.(\d+)? seconds (re) - -Make a commit, than prune a commit, than create a bookmark on top of it. -Do a backup and try to restore. Make sure it doesn't fail - $ hg up -q null - $ mkcommit tobepruned - created new head - $ hg log -r . -T '{node}\n' - edb281c9cc7e2e51c382b6f254d1967fdfa5e6ff - $ hg prune . - advice: 'hg hide' provides a better UI for hiding commits - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - working directory now at 000000000000 - 1 changesets pruned - $ hg --hidden book -r edb281c9cc7e2e51c382b6f254d1967fdfa5e6ff newbookonpruned - $ hg pushbackup - starting backup .* (re) - nothing to backup - finished in \d+\.(\d+)? seconds (re) - -Restore the repo - $ cd .. - $ hg clone ssh://user@dummy/repo restored -q - $ cd restored - $ hg pullbackup - pulling from ssh://user@dummy/repo - adding changesets - adding manifests - adding file changes - added 1 changesets with 1 changes to 1 files - new changesets b75a450e74d5 - (run 'hg update' to get a working copy) diff --git a/tests/test-infinitepush-pullbackup.t b/tests/test-infinitepush-pullbackup.t deleted file mode 100644 --- a/tests/test-infinitepush-pullbackup.t +++ /dev/null @@ -1,282 +0,0 @@ - - $ . "$TESTDIR/library.sh" - $ . "$TESTDIR/library-infinitepush.sh" - $ setupcommon - $ cat >> $HGRCPATH << EOF - > [infinitepushbackup] - > logdir=$TESTTMP/logs - > EOF - -Setup server - $ hg init repo - $ cd repo - $ setupserver - $ cd .. - -Create backup source - $ hg clone ssh://user@dummy/repo backupsource -q - -Create restore target - $ hg clone ssh://user@dummy/repo restored -q - -Backup - $ cd backupsource - $ mkcommit firstcommit - $ hg book abook - -Actually do a backup, make sure that backup check doesn't fail for empty backup state - $ hg pushbackup - starting backup .* (re) - searching for changes - remote: pushing 1 commit: - remote: 89ecc969c0ac firstcommit - finished in \d+\.(\d+)? seconds (re) - $ cd .. - -Create logdir - $ setuplogdir - -Restore - $ cd restored - $ hg pullbackup --config infinitepushbackup.autobackup=True - pulling from ssh://user@dummy/repo - adding changesets - adding manifests - adding file changes - added 1 changesets with 1 changes to 1 files - new changesets 89ecc969c0ac - (run 'hg update' to get a working copy) - $ waitbgbackup - $ hg log --graph -T '{desc}' - o firstcommit - - $ hg book - abook 0:89ecc969c0ac - $ cd .. -Check that autobackup doesn't happen on pullbackup. Logs should be empty and backupstate should be correct - $ test -f $TESTTMP/logs/test/* - [1] - $ python -c "import sys; import json; bst = json.loads(sys.stdin.read()); print(bst['bookmarks'], bst['heads'])" < restored/.hg/infinitepushbackupstate - ({u'abook': u'89ecc969c0ac7d7344728f1255250df7c54a56af'}, [u'89ecc969c0ac7d7344728f1255250df7c54a56af']) - - -Create second backup source - $ hg clone ssh://user@dummy/repo backupsource2 -q - $ cd backupsource2 - $ mkcommit secondcommit - $ hg book secondbook - $ hg pushbackup - starting backup .* (re) - searching for changes - remote: pushing 1 commit: - remote: c1bfda8efb6e secondcommit - finished in \d+\.(\d+)? seconds (re) - $ cd .. - -Restore with ambiguous repo root - $ rm -rf restored - $ hg clone ssh://user@dummy/repo restored -q - $ cd restored - $ hg pullbackup - abort: ambiguous repo root to restore: ['$TESTTMP/backupsource', '$TESTTMP/backupsource2'] - (set --reporoot to disambiguate) - [255] - $ hg pullbackup --reporoot $TESTTMP/backupsource2 - pulling from ssh://user@dummy/repo - adding changesets - adding manifests - adding file changes - added 1 changesets with 1 changes to 1 files - new changesets c1bfda8efb6e - (run 'hg update' to get a working copy) - $ hg log --graph -T '{desc}' - o secondcommit - - $ cd .. - -Check bookmarks escaping - $ cd backupsource - $ hg book book/bookmarks/somebook - $ hg book book/bookmarksbookmarks/somebook - $ hg pushbackup - starting backup .* (re) - finished in \d+\.(\d+)? seconds (re) - $ cd ../restored - $ hg pullbackup --reporoot $TESTTMP/backupsource - pulling from ssh://user@dummy/repo - searching for changes - adding changesets - adding manifests - adding file changes - added 1 changesets with 1 changes to 1 files (+1 heads) - new changesets 89ecc969c0ac - (run 'hg heads' to see heads, 'hg merge' to merge) - $ hg book - abook 1:89ecc969c0ac - book/bookmarks/somebook 1:89ecc969c0ac - book/bookmarksbookmarks/somebook 1:89ecc969c0ac - secondbook 0:c1bfda8efb6e - $ cd .. - -Create a repo with `/bookmarks/` in path - $ mkdir bookmarks - $ cd bookmarks - $ hg clone ssh://user@dummy/repo backupsource3 -q - $ cd backupsource3 - $ mkcommit commitinweirdrepo - $ hg book bookbackupsource3 - $ hg pushbackup - starting backup .* (re) - searching for changes - remote: pushing 1 commit: - remote: a2a9ae518b62 commitinweirdrepo - finished in \d+\.(\d+)? seconds (re) - $ cd ../../restored - $ hg pullbackup --reporoot $TESTTMP/bookmarks/backupsource3 - pulling from ssh://user@dummy/repo - searching for changes - adding changesets - adding manifests - adding file changes - added 1 changesets with 1 changes to 1 files (+1 heads) - new changesets a2a9ae518b62 - (run 'hg heads .' to see heads, 'hg merge' to merge) - $ hg book - abook 1:89ecc969c0ac - book/bookmarks/somebook 1:89ecc969c0ac - book/bookmarksbookmarks/somebook 1:89ecc969c0ac - bookbackupsource3 2:a2a9ae518b62 - secondbook 0:c1bfda8efb6e - -Check that correct path is used in pushbackup - $ cd ../backupsource - $ hg --config paths.default=badpath --config paths.anotherpath=ssh://user@dummy/repo pushbackup - starting backup .* (re) - abort: repository $TESTTMP/backupsource/badpath not found! - [255] - $ hg pushbackup anotherpath --config paths.default=badpath --config paths.anotherpath=ssh://user@dummy/repo - starting backup .* (re) - nothing to backup - finished in \d+\.(\d+)? seconds (re) - $ cd ../restored - -Check that correct path is used in pullbackup - $ hg pullbackup --config paths.default=badpath --config paths.anotherpath=ssh://user@dummy/repo --reporoot $TESTTMP/bookmarks/backupsource3 - abort: repository $TESTTMP/restored/badpath not found! - [255] - $ hg pullbackup anotherpath --config paths.default=badpath --config paths.anotherpath=ssh://user@dummy/repo --reporoot $TESTTMP/bookmarks/backupsource3 - pulling from ssh://user@dummy/repo - no changes found - adding changesets - adding manifests - adding file changes - added 0 changesets with 0 changes to 1 files - - $ cd .. - -Backup and restore two commits - $ cd backupsource - $ mkcommit firstinbatch - $ hg up 0 - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - (leaving bookmark book/bookmarksbookmarks/somebook) - $ mkcommit secondinbatch - created new head - $ hg pushbackup - starting backup .* (re) - searching for changes - remote: pushing 3 commits: - remote: 89ecc969c0ac firstcommit - remote: 33c1c9df81e9 firstinbatch - remote: 0e1a088ff282 secondinbatch - finished in \d+\.(\d+)? seconds (re) - $ cd ../restored - -Install server-side extension that will print message every time when bundlerepo -is created - $ cd ../repo - $ printf "\n[extensions]\nbundlerepologger=$TESTDIR/bundlerepologger.py" >> .hg/hgrc - $ hg st - $ cd ../restored - -Pull the backup and check bundlerepo was created only once - $ hg pullbackup --reporoot $TESTTMP/backupsource | grep 'creating bundlerepo' - remote: creating bundlerepo - $ cd ../repo - $ printf "\n[extensions]\nbundlerepologger=!" >> .hg/hgrc - $ cd ../restored - -Make sure that commits were restored - $ hg log -r '33c1c9df81e9 + 0e1a088ff282' > /dev/null - -Backup as another user, then restore it - $ cd ../backupsource - $ mkcommit backupasanotheruser - $ hg log -r . -T '{node}\n' - e0230a60975b38a9014f098fb973199efd25c46f - $ HGUSER=anotheruser hg pushbackup - starting backup .* (re) - searching for changes - remote: pushing 3 commits: - remote: 89ecc969c0ac firstcommit - remote: 0e1a088ff282 secondinbatch - remote: e0230a60975b backupasanotheruser - finished in \d+\.(\d+)? seconds (re) - $ cd ../restored - -Make sure commit was pulled by checking that commit is present - $ hg log -r e0230a60975b38a9014f098fb973199efd25c46f -T '{node}\n' - abort: unknown revision 'e0230a60975b38a9014f098fb973199efd25c46f'! - [255] - $ hg pullbackup --user anotheruser --reporoot $TESTTMP/backupsource > /dev/null - $ hg log -r tip -T '{node}\n' - e0230a60975b38a9014f098fb973199efd25c46f - -Test debugcheckbackup - $ hg debugcheckbackup - abort: ambiguous repo root to restore: ['$TESTTMP', '$TESTTMP/backupsource', '$TESTTMP/backupsource2', '$TESTTMP/bookmarks/backupsource3'] - (set --reporoot to disambiguate) - [255] - $ hg debugcheckbackup --user anotheruser --reporoot $TESTTMP/backupsource - checking \$TESTTMP/backupsource on .* (re) - $ hg debugcheckbackup --all 2>&1 | sort - checking \$TESTTMP on .* (re) - checking \$TESTTMP/backupsource on .* (re) - checking \$TESTTMP/backupsource2 on .* (re) - checking \$TESTTMP/bookmarks/backupsource3 on .* (re) - $ rm ../repo/.hg/scratchbranches/index/nodemap/e0230a60975b38a9014f098fb973199efd25c46f - $ hg debugcheckbackup --user anotheruser --reporoot $TESTTMP/backupsource - checking \$TESTTMP/backupsource on .* (re) - unknown revision 'e0230a60975b38a9014f098fb973199efd25c46f' - [255] - -Make another backup from backupsource2 and run `hg debugcheckbackup --all` again. -Make sure that both repos were checked even though check for one of them fails - $ cd ../backupsource2 - $ mkcommit newcommit - $ HGUSER=anotheruser hg pushbackup - starting backup .* (re) - searching for changes - remote: pushing 2 commits: - remote: c1bfda8efb6e secondcommit - remote: c03baa769a20 newcommit - finished in \d+\.(\d+)? seconds (re) - $ cd ../backupsource - $ hg debugcheckbackup --user anotheruser --all 2>&1 | sort - checking \$TESTTMP/backupsource on .* (re) - checking \$TESTTMP/backupsource2 on .* (re) - unknown revision 'e0230a60975b38a9014f098fb973199efd25c46f' - -Test getavailablebackups command - $ hg getavailablebackups - user test has 4 available backups: - \$TESTTMP on .* (re) - \$TESTTMP/backupsource on .* (re) - \$TESTTMP/backupsource2 on .* (re) - \$TESTTMP/bookmarks/backupsource3 on .* (re) - $ hg getavailablebackups --user anotheruser - user anotheruser has 2 available backups: - \$TESTTMP/backupsource on .* (re) - \$TESTTMP/backupsource2 on .* (re) - $ hg getavailablebackups --json - {".*": \["\$TESTTMP", "\$TESTTMP/backupsource", "\$TESTTMP/backupsource2", "\$TESTTMP/bookmarks/backupsource3"\]} (re) diff --git a/tests/test-infinitepush-remotefilelog.t b/tests/test-infinitepush-remotefilelog.t deleted file mode 100644 --- a/tests/test-infinitepush-remotefilelog.t +++ /dev/null @@ -1,112 +0,0 @@ - $ . "$TESTDIR/library.sh" - - $ mkcommit() { - > echo "$1" > "$1" - > hg add "$1" - > hg ci -m "$1" - > } - -Create server - $ hginit master - $ cd master - $ cat >> .hg/hgrc < [extensions] - > infinitepush=$TESTDIR/../infinitepush - > [remotefilelog] - > server=True - > [infinitepush] - > server=True - > branchpattern=re:scratch/.+ - > indextype=disk - > storetype=disk - > EOF - $ cd .. - -Create first client - $ hgcloneshallow ssh://user@dummy/master shallow1 - streaming all changes - 0 files to transfer, 0 bytes of data - transferred 0 bytes in * seconds (0 bytes/sec) (glob) - no changes found - updating to branch default - 0 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ cd shallow1 - $ cat >> .hg/hgrc < [extensions] - > infinitepush=$TESTDIR/../infinitepush - > [infinitepush] - > server=False - > branchpattern=re:scratch/.+ - > EOF - $ cd .. - -Create second client - $ hgcloneshallow ssh://user@dummy/master shallow2 -q - $ cd shallow2 - $ cat >> .hg/hgrc < [extensions] - > infinitepush=$TESTDIR/../infinitepush - > [infinitepush] - > server=False - > branchpattern=re:scratch/.+ - > EOF - $ cd .. - -First client: make commit and push to scratch branch - $ cd shallow1 - $ mkcommit scratchcommit - $ hg push -r . --to scratch/newscratch --create - pushing to ssh://user@dummy/master - searching for changes - remote: pushing 1 commit: - remote: 2d9cfa751213 scratchcommit - $ cd .. - -Second client: pull scratch commit and update to it - $ cd shallow2 - $ hg pull -B scratch/newscratch - pulling from ssh://user@dummy/master - adding changesets - adding manifests - adding file changes - added 1 changesets with 1 changes to 1 files - new changesets 2d9cfa751213 - (run 'hg update' to get a working copy) - $ hg up 2d9cfa751213 - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ cd .. - -First client: make commits with file modification and file deletion - $ cd shallow1 - $ echo 1 > 1 - $ echo 2 > 2 - $ mkdir dir - $ echo fileindir > dir/file - $ echo toremove > dir/toremove - $ hg ci -Aqm 'scratch commit with many files' - $ hg rm dir/toremove - $ hg ci -Aqm 'scratch commit with deletion' - $ hg push -r . --to scratch/newscratch - pushing to ssh://user@dummy/master - searching for changes - remote: pushing 3 commits: - remote: 2d9cfa751213 scratchcommit - remote: 1c2153299e05 scratch commit with many files - remote: 2db33e8c1f93 scratch commit with deletion - $ cd .. - -Second client: pull new scratch commits and update to all of them - $ cd shallow2 - $ hg pull --config remotefilelog.excludepattern=somefile -B scratch/newscratch - pulling from ssh://user@dummy/master - searching for changes - adding changesets - adding manifests - adding file changes - added 2 changesets with 5 changes to 5 files - new changesets 1c2153299e05:2db33e8c1f93 - (run 'hg update' to get a working copy) - $ hg up 1c2153299e05 - 4 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ hg up 2db33e8c1f93 - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved diff --git a/tests/test-infinitepush-remotenames-public.t b/tests/test-infinitepush-remotenames-public.t deleted file mode 100644 --- a/tests/test-infinitepush-remotenames-public.t +++ /dev/null @@ -1,119 +0,0 @@ -Remotenames extension has a shortcut that makes heads discovery work faster. -Unfortunately that may result in sending public commits to the server. This -test covers the issue. - - $ . $TESTDIR/require-ext.sh remotenames - $ . $TESTDIR/library.sh - $ . $TESTDIR/library-infinitepush.sh - $ cat >> $HGRCPATH << EOF - > [extensions] - > infinitepush=$TESTDIR/../infinitepush - > [infinitepush] - > branchpattern=re:scratch/.+ - > [ui] - > ssh = python "$TESTDIR/dummyssh" - > EOF - $ mkcommit() { - > echo "$1" > "$1" - > hg add "$1" - > hg ci -m "$1" - > } - $ scratchnodes() { - > for node in `find ../repo/.hg/scratchbranches/index/nodemap/* | sort`; do - > echo ${node##*/} - > done - > } - $ scratchbookmarks() { - > for bookmark in `find ../repo/.hg/scratchbranches/index/bookmarkmap/* -type f | sort`; do - > echo "${bookmark##*/bookmarkmap/} `cat $bookmark`" - > done - > } - $ enableremotenames() { - > printf '[extensions]\nremotenames=\n' >> .hg/hgrc - > } - -Setup server with a few commits and one remote bookmark. This remotebookmark -may be used by remotenames extension in fastheaddiscovery heuristic - $ hg init repo - $ cd repo - $ setupserver - $ mkcommit first - $ hg book remotebook - $ hg up -q . - $ mkcommit second - $ mkcommit third - $ mkcommit fourth - $ cd .. - -Create new client - $ hg clone ssh://user@dummy/repo --config extensions.remotenames= client -q - $ cd client - $ enableremotenames - -Create scratch commit and back it up. - $ hg up -q -r 'desc(third)' - $ mkcommit scratch - created new head - $ hg log -r . -T '{node}\n' - ce87a066ebc28045311cd1272f5edc0ed80d5b1c - $ hg log --graph -T '{desc}' - @ scratch - | - | o fourth - |/ - o third - | - o second - | - o first - - $ hg pushbackup - starting backup * (glob) - searching for changes - remote: pushing 1 commit: - remote: ce87a066ebc2 scratch - finished in * (glob) - $ cd .. - -Create second client - $ hg clone ssh://user@dummy/repo --config extensions.remotenames= client2 -q - $ cd client2 - $ enableremotenames - -Pull to get remote names - $ hg pull - pulling from ssh://user@dummy/repo - searching for changes - no changes found - $ hg book --remote - default/remotebook 0:b75a450e74d5 - -Strip public commits from the repo, otherwise fastheaddiscovery heuristic will -be skipped - $ hg strip -q -r '1:' - $ hg log --graph -T '{desc}' - @ first - -Download scratch commit. It also downloads a few public commits - $ hg up -q ce87a066ebc28045311cd1272f5edc0ed80d5b1c - 'ce87a066ebc28045311cd1272f5edc0ed80d5b1c' does not exist locally - looking for it remotely... - 'ce87a066ebc28045311cd1272f5edc0ed80d5b1c' found remotely - $ hg log --graph -T '{desc}' - @ scratch - | - o third - | - o second - | - o first - - $ hg book --remote - default/remotebook 0:b75a450e74d5 - -Run pushbackup and make sure only scratch commit is backed up. - $ hg pushbackup - starting backup * (glob) - searching for changes - remote: pushing 1 commit: - remote: ce87a066ebc2 scratch - finished in * (glob) diff --git a/tests/test-infinitepush-remotenames.t b/tests/test-infinitepush-remotenames.t deleted file mode 100644 --- a/tests/test-infinitepush-remotenames.t +++ /dev/null @@ -1,228 +0,0 @@ - $ . $TESTDIR/require-ext.sh remotenames - $ cat >> $HGRCPATH << EOF - > [extensions] - > infinitepush=$TESTDIR/../infinitepush - > [infinitepush] - > branchpattern=re:scratch/.+ - > [ui] - > ssh = python "$TESTDIR/dummyssh" - > EOF - $ mkcommit() { - > echo "$1" > "$1" - > hg add "$1" - > hg ci -m "$1" - > } - $ scratchnodes() { - > for node in `find ../repo/.hg/scratchbranches/index/nodemap/* | sort`; do - > echo ${node##*/} - > done - > } - $ scratchbookmarks() { - > for bookmark in `find ../repo/.hg/scratchbranches/index/bookmarkmap/* -type f | sort`; do - > echo "${bookmark##*/bookmarkmap/} `cat $bookmark`" - > done - > } - $ enableremotenames() { - > printf '[extensions]\nremotenames=\n' >> .hg/hgrc - > } - -Create server repo with one commit and one remote bookmark - $ hg init repo - $ cd repo - $ cat >> .hg/hgrc << EOF - > [infinitepush] - > server=yes - > indextype=disk - > storetype=disk - > EOF - $ mkcommit servercommit -Let's make server bookmark to match scratch pattern and -check that it won't be handled like scratch bookmark - $ hg book scratch/serverbook - $ cd .. - -Clone server and enable remotenames - $ hg clone ssh://user@dummy/repo --config extensions.remotenames= client -q - $ cd client - $ enableremotenames - $ hg book --remote - default/scratch/serverbook 0:ac312cb08db5 - -Push scratch commit and scratch bookmark - $ mkcommit scratchcommitwithremotenames - $ hg push --config extensions.remotenames= -r . --to scratch/mybranch --create - pushing to ssh://user@dummy/repo - searching for changes - remote: pushing 1 commit: - remote: 620472ff5c0c scratchcommitwithremotenames - $ hg book --remote - default/scratch/mybranch 1:620472ff5c0c - default/scratch/serverbook 0:ac312cb08db5 - $ hg book - no bookmarks set - $ hg -R ../repo log -G - @ changeset: 0:ac312cb08db5 - bookmark: scratch/serverbook - tag: tip - user: test - date: Thu Jan 01 00:00:00 1970 +0000 - summary: servercommit - - $ scratchnodes - 620472ff5c0c4a560a3ffd98c07f0c9ecad33f64 - $ scratchbookmarks - scratch/mybranch 620472ff5c0c4a560a3ffd98c07f0c9ecad33f64 - $ cd .. - -Clone server one more time and pull scratch bookmark. Make sure it is remote - $ hg clone ssh://user@dummy/repo --config extensions.remotenames= client2 -q - $ cd client2 - $ enableremotenames - $ hg book --remote - default/scratch/serverbook 0:ac312cb08db5 - $ hg pull -B scratch/mybranch - pulling from ssh://user@dummy/repo - searching for changes - adding changesets - adding manifests - adding file changes - added 1 changesets with 1 changes to 1 files - new changesets 620472ff5c0c - (run 'hg update' to get a working copy) - $ hg book --remote - default/scratch/mybranch 1:620472ff5c0c - default/scratch/serverbook 0:ac312cb08db5 - $ hg book - no bookmarks set - -Make sure that next non-scratch pull doesn't override remote scratch bookmarks - $ hg pull - pulling from ssh://user@dummy/repo - searching for changes - no changes found - $ hg book --remote - default/scratch/mybranch 1:620472ff5c0c - default/scratch/serverbook 0:ac312cb08db5 - $ cd .. - -Create one more branch head on the server - $ cd repo - $ mkcommit head1 - $ hg up ac312cb08db5 - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved - (leaving bookmark scratch/serverbook) - $ mkcommit head2 - created new head - $ hg log -G - @ changeset: 2:dc4b2ecb723b - | tag: tip - | parent: 0:ac312cb08db5 - | user: test - | date: Thu Jan 01 00:00:00 1970 +0000 - | summary: head2 - | - | o changeset: 1:64d557aa86fd - |/ bookmark: scratch/serverbook - | user: test - | date: Thu Jan 01 00:00:00 1970 +0000 - | summary: head1 - | - o changeset: 0:ac312cb08db5 - user: test - date: Thu Jan 01 00:00:00 1970 +0000 - summary: servercommit - - $ cd .. - -Go back to client, make pull and make sure that we pulled remote branches - $ cd client - $ cat .hg/remotenames | sort - 620472ff5c0c4a560a3ffd98c07f0c9ecad33f64 bookmarks default/scratch/mybranch - ac312cb08db5366e622a01fd001e583917eb9f1c bookmarks default/scratch/serverbook - ac312cb08db5366e622a01fd001e583917eb9f1c branches default/default - $ hg pull - pulling from ssh://user@dummy/repo - searching for changes - adding changesets - adding manifests - adding file changes - added 2 changesets with 2 changes to 2 files (+2 heads) - new changesets 64d557aa86fd:dc4b2ecb723b - (run 'hg heads' to see heads, 'hg merge' to merge) - $ cat .hg/remotenames | sort - 620472ff5c0c4a560a3ffd98c07f0c9ecad33f64 bookmarks default/scratch/mybranch - 64d557aa86fdc42384b193f7eab99059da84f1f0 bookmarks default/scratch/serverbook - 64d557aa86fdc42384b193f7eab99059da84f1f0 branches default/default - dc4b2ecb723bbf077b2d1ecab0dace0b001eb5b8 branches default/default - $ cd .. - -Push from another client, make sure that push doesn't override scratch bookmarks - $ cd client2 - $ hg up scratch/serverbook - 0 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ mkcommit newscratch - created new head - $ hg push -r . --to scratch/secondbranch --create - pushing to ssh://user@dummy/repo - searching for changes - remote: pushing 1 commit: - remote: 36667a3f76e4 newscratch - $ hg book --remote - default/scratch/mybranch 1:620472ff5c0c - default/scratch/secondbranch 2:36667a3f76e4 - default/scratch/serverbook 0:ac312cb08db5 - $ hg book - no bookmarks set - -Try to push with remotebookmarks disabled - $ hg push --config remotenames.bookmarks=False -r . --to scratch/secondbranch - pushing to ssh://user@dummy/repo - searching for changes - remote: pushing 1 commit: - remote: 36667a3f76e4 newscratch - $ hg book - scratch/secondbranch 2:36667a3f76e4 - -Create new bookmark and try to pull it - $ mkcommit newcommittoupdate1 - $ hg push -q -r . --to scratch/branchtoupdateto1 --create - $ hg up -q ".^" - $ mkcommit newcommittoupdate2 - created new head - $ hg push -q -r . --to scratch/branchtoupdateto2 --create - $ hg up -q ".^" - $ mkcommit newcommittopull - created new head - $ hg push -q -r . --to scratch/branchtopull --create - $ cd ../client - $ hg up default/scratch/branchtoupdateto1 - 'scratch/branchtoupdateto1' does not exist locally - looking for it remotely... - pulling from ssh://user@dummy/repo - searching for changes - adding changesets - adding manifests - adding file changes - added 2 changesets with 2 changes to 2 files (+1 heads) - new changesets 36667a3f76e4:2885148f6198 - (run 'hg heads .' to see heads, 'hg merge' to merge) - 'scratch/branchtoupdateto1' found remotely - 2 files updated, 0 files merged, 1 files removed, 0 files unresolved - - $ cat >> $HGRCPATH << EOF - > [remotenames] - > hoist=remote - > rename.default=remote - > EOF - - $ hg up remote/scratch/branchtoupdateto2 - 'scratch/branchtoupdateto2' does not exist locally - looking for it remotely... - pulling from ssh://user@dummy/repo - searching for changes - adding changesets - adding manifests - adding file changes - added 1 changesets with 1 changes to 2 files (+1 heads) - new changesets 1f558bd20eaa - (run 'hg heads .' to see heads, 'hg merge' to merge) - 'scratch/branchtoupdateto2' found remotely - 1 files updated, 0 files merged, 1 files removed, 0 files unresolved diff --git a/tests/test-infinitepush-sql.t b/tests/test-infinitepush-sql.t deleted file mode 100644 --- a/tests/test-infinitepush-sql.t +++ /dev/null @@ -1,80 +0,0 @@ -#if no-osx - $ mkcommit() { - > echo "$1" > "$1" - > hg add "$1" - > hg ci -d "0 0" -m "$1" - > } - $ . "$TESTDIR/library-infinitepush.sh" - $ setupcommon - -With no configuration it should abort - $ hg init server - $ cd server - $ setupsqlserverhgrc babar - $ hg st - abort: please set infinitepush.sqlhost - [255] - $ setupdb - $ cd .. - $ hg clone -q ssh://user@dummy/server client1 - $ hg clone -q ssh://user@dummy/server client2 - $ cd client1 - $ setupsqlclienthgrc - $ cd ../client2 - $ setupsqlclienthgrc - $ cd ../client1 - $ mkcommit scratchcommit - - $ hg push -r . --to scratch/book --create - pushing to ssh://user@dummy/server - searching for changes - remote: pushing 1 commit: - remote: 2d9cfa751213 scratchcommit - -Make pull and check that scratch commit is not pulled - $ cd ../client2 - $ hg pull - pulling from ssh://user@dummy/server - no changes found - $ hg log -r scratch/book - abort: unknown revision 'scratch/book'! - [255] - -Pull scratch commit from the second client - $ hg pull -B scratch/book - pulling from ssh://user@dummy/server - adding changesets - adding manifests - adding file changes - added 1 changesets with 1 changes to 1 files - new changesets 2d9cfa751213 - (run 'hg update' to get a working copy) - $ hg up scratch/book - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - (activating bookmark scratch/book) - $ hg log -G - @ changeset: 0:2d9cfa751213 - bookmark: scratch/book - tag: tip - user: test - date: Thu Jan 01 00:00:00 1970 +0000 - summary: scratchcommit - - $ cd ../server - $ hg book scratch/%erversidebook - $ hg book serversidebook - $ cd ../client1 - $ hg book --list-remote 'scratch/*' - scratch/%erversidebook 0000000000000000000000000000000000000000 - scratch/book 2d9cfa7512136a84a6edb6a7c288145229c2ef7f - $ hg book --list-remote 'scratch/%*' - scratch/%erversidebook 0000000000000000000000000000000000000000 - $ mysql -h $DBHOST -P $DBPORT -D $DBNAME -u $DBUSER $DBPASSOPT -e 'select * from nodesmetadata' - node message p1 p2 author committer author_date committer_date reponame optional_json_metadata - 2d9cfa7512136a84a6edb6a7c288145229c2ef7f scratchcommit 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 test test 0 0 babar NULL - $ cd ../server - $ hg debugfillinfinitepushmetadata --node 2d9cfa7512136a84a6edb6a7c288145229c2ef7f - $ mysql -h $DBHOST -P $DBPORT -D $DBNAME -u $DBUSER $DBPASSOPT -e 'select * from nodesmetadata' - node message p1 p2 author committer author_date committer_date reponame optional_json_metadata - 2d9cfa7512136a84a6edb6a7c288145229c2ef7f scratchcommit 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 test test 0 0 babar {"changed_files": {"scratchcommit": {"adds": 1, "isbinary": false, "removes": 0, "status": "added"}}} -#endif diff --git a/tests/test-infinitepush-update.t b/tests/test-infinitepush-update.t deleted file mode 100644 diff --git a/tests/test-treemanifest-infinitepush.t b/tests/test-treemanifest-infinitepush.t deleted file mode 100644 --- a/tests/test-treemanifest-infinitepush.t +++ /dev/null @@ -1,219 +0,0 @@ - $ . "$TESTDIR/library.sh" - $ . "$TESTDIR/library-infinitepush.sh" - - $ PYTHONPATH=$TESTDIR/..:$PYTHONPATH - $ export PYTHONPATH - $ setupcommon - - $ hginit master - $ cd master - $ setupserver - $ cat >> .hg/hgrc < [extensions] - > pushrebase=$TESTDIR/../hgext3rd/pushrebase.py - > treemanifest=$TESTDIR/../treemanifest - > [remotefilelog] - > server=True - > [treemanifest] - > server=True - > EOF - $ echo x > x - $ hg commit -qAm 'add x' - $ cd .. - -Push a non-tree scratch branch from one client - - $ hgcloneshallow ssh://user@dummy/master normal-client -q - 1 files fetched over 1 fetches - (1 misses, 0.00% hit ratio) over * (glob) - $ cd normal-client - $ mkdir bar - $ echo >> bar/car - $ hg commit -qAm 'add bar/car' - $ cat >> .hg/hgrc < [extensions] - > treemanifest=$TESTDIR/../treemanifest - > fastmanifest=$TESTDIR/../fastmanifest - > - > [remotefilelog] - > usefastdatapack=True - > - > [fastmanifest] - > usecache=False - > usetree=True - > EOF - $ hg push --to scratch/nontree --create - pushing to ssh://user@dummy/master - searching for changes - 1 trees fetched over * (glob) - remote: pushing 1 commit: - remote: 42ec76eb772a add bar/car - $ clearcache - $ cd .. - -Push a tree-only scratch branch from another client - $ hgcloneshallow ssh://user@dummy/master client1 -q --config extensions.treemanifest=$TESTDIR/../treemanifest --config treemanifest.treeonly=True - 1 trees fetched over * (glob) - 1 trees fetched over * (glob) - 1 files fetched over 1 fetches - (1 misses, 0.00% hit ratio) over * (glob) - $ cd client1 - $ cat >> .hg/hgrc < [extensions] - > treemanifest=$TESTDIR/../treemanifest - > - > [remotefilelog] - > usefastdatapack=True - > - > [treemanifest] - > treeonly=True - > EOF - - $ mkdir subdir - $ echo "my change" >> subdir/a - $ hg commit -qAm 'add subdir/a' - $ hg push --to scratch/foo --create - pushing to ssh://user@dummy/master - searching for changes - remote: pushing 1 commit: - remote: 02c12aef64ff add subdir/a - $ cd .. - -Pull a non-tree scratch branch into a normal client - - $ hgcloneshallow ssh://user@dummy/master normal-client2 -q - $ cd normal-client2 - $ cat >> .hg/hgrc < [extensions] - > treemanifest=$TESTDIR/../treemanifest - > fastmanifest=$TESTDIR/../fastmanifest - > - > [remotefilelog] - > usefastdatapack=True - > - > [fastmanifest] - > usecache=False - > usetree=True - > EOF - $ hg pull -r scratch/nontree - pulling from ssh://user@dummy/master - searching for changes - adding changesets - adding manifests - adding file changes - added 1 changesets with 1 changes to 1 files - new changesets 42ec76eb772a - (run 'hg update' to get a working copy) - $ hg log -r tip -vp - changeset: 1:42ec76eb772a - tag: tip - user: test - date: Thu Jan 01 00:00:00 1970 +0000 - files: bar/car - description: - add bar/car - - - diff -r 085784c01c08 -r 42ec76eb772a bar/car - --- /dev/null Thu Jan 01 00:00:00 1970 +0000 - +++ b/bar/car Thu Jan 01 00:00:00 1970 +0000 - @@ -0,0 +1,1 @@ - + - -Pull a treeonly scratch branch into a normal client - $ hg debugindex -m - rev offset length delta linkrev nodeid p1 p2 - 0 0 44 -1 0 bc0c2c938b92 000000000000 000000000000 - 1 44 59 0 1 bf0601d5cb94 bc0c2c938b92 000000000000 - $ hg pull -r scratch/foo - pulling from ssh://user@dummy/master - searching for changes - adding changesets - adding manifests - adding file changes - added 1 changesets with 1 changes to 1 files (+1 heads) - new changesets 02c12aef64ff - (run 'hg heads' to see heads, 'hg merge' to merge) -- Verify no new manifest revlog entry was written - $ hg debugindex -m - rev offset length delta linkrev nodeid p1 p2 - 0 0 44 -1 0 bc0c2c938b92 000000000000 000000000000 - 1 44 59 0 1 bf0601d5cb94 bc0c2c938b92 000000000000 -- ...but we can still read the manifest - $ hg log -r 02c12aef64ff --stat -T '{rev}\n' - 2 - subdir/a | 1 + - 1 files changed, 1 insertions(+), 0 deletions(-) - - $ cd .. - -Pull a treeonly scratch branch into a treeonly client - - $ hgcloneshallow ssh://user@dummy/master client2 -q --config extensions.treemanifest=$TESTDIR/../treemanifest --config treemanifest.treeonly=True - $ cd client2 - $ cat >> .hg/hgrc < [extensions] - > treemanifest=$TESTDIR/../treemanifest - > - > [remotefilelog] - > usefastdatapack=True - > - > [treemanifest] - > treeonly=True - > EOF - $ hg pull -r scratch/foo - pulling from ssh://user@dummy/master - searching for changes - adding changesets - adding manifests - adding file changes - added 1 changesets with 1 changes to 1 files - new changesets 02c12aef64ff - (run 'hg update' to get a working copy) - $ hg log -G - o changeset: 1:02c12aef64ff - | tag: tip - | user: test - | date: Thu Jan 01 00:00:00 1970 +0000 - | summary: add subdir/a - | - @ changeset: 0:085784c01c08 - user: test - date: Thu Jan 01 00:00:00 1970 +0000 - summary: add x - - $ hg cat -r tip subdir/a - my change - $ ls_l .hg/store - -rw-r--r-- 257 00changelog.i - -rw-r--r-- 108 00manifesttree.i - drwxr-xr-x data - drwxrwxr-x packs - -rw-r--r-- 43 phaseroots - -rw-r--r-- 18 undo - -rw-r--r-- 17 undo.backupfiles - -rw-r--r-- 0 undo.phaseroots - -Pull a normal scratch branch into a treeonly client - $ hg pull -r scratch/nontree - pulling from ssh://user@dummy/master - searching for changes - adding changesets - adding manifests - adding file changes - added 1 changesets with 1 changes to 1 files (+1 heads) - new changesets 42ec76eb772a - (run 'hg heads' to see heads, 'hg merge' to merge) - $ hg log -r 42ec76eb772a -T ' ' --stat - abort: unable to download the following trees from the server: - bf0601d5cb94247e00d0bdd1d8327f0dd36f54e9 - [255] - $ cd .. - -Verify its not on the server - $ cd master - $ hg log -G - @ changeset: 0:085784c01c08 - tag: tip - user: test - date: Thu Jan 01 00:00:00 1970 +0000 - summary: add x -