##// END OF EJS Templates
revset: use decorator to register a function as revset predicate...
revset: use decorator to register a function as revset predicate Using decorator can localize changes for adding (or removing) a revset predicate function in source code. It is also useful to pick predicates up for specific purpose. For example, subsequent patch marks predicates as "safe" by decorator. This patch defines 'parsefuncdecl()' in 'funcregistrar' class, because this implementation can be uesd by other decorator class for fileset predicate and template function.

File last commit:

r26587:56b2bcea default
r27584:fc7c8cac default
Show More
basestore.py
221 lines | 7.9 KiB | text/x-python | PythonLexer
various
hgext: add largefiles extension...
r15168 # Copyright 2009-2010 Gregory P. Ward
# Copyright 2009-2010 Intelerad Medical Systems Incorporated
# Copyright 2010-2011 Fog Creek Software
# Copyright 2010-2011 Unity Technologies
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
Greg Ward
largefiles: improve comments, internal docstrings...
r15252 '''base class for store implementations and store-related utility code'''
various
hgext: add largefiles extension...
r15168
import re
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 from mercurial import util, node, hg, error
various
hgext: add largefiles extension...
r15168 from mercurial.i18n import _
import lfutil
class StoreError(Exception):
'''Raised when there is a problem getting files from or putting
files to a central store.'''
def __init__(self, filename, hash, url, detail):
self.filename = filename
self.hash = hash
self.url = url
self.detail = detail
def longmessage(self):
Wagner Bruna
largefiles: enhance error message to make it more i18n-friendly
r18461 return (_("error getting id %s from url %s for file %s: %s\n") %
Mads Kiilerich
largefiles: hide passwords in URLs in ui messages
r19950 (self.hash, util.hidepassword(self.url), self.filename,
self.detail))
various
hgext: add largefiles extension...
r15168
def __str__(self):
Mads Kiilerich
largefiles: hide passwords in URLs in ui messages
r19950 return "%s: %s" % (util.hidepassword(self.url), self.detail)
various
hgext: add largefiles extension...
r15168
class basestore(object):
def __init__(self, ui, repo, url):
self.ui = ui
self.repo = repo
self.url = url
def put(self, source, hash):
Mads Kiilerich
largefiles: 'put' should store 'source' file in under 'hash', also in localstore
r19007 '''Put source file into the store so it can be retrieved by hash.'''
various
hgext: add largefiles extension...
r15168 raise NotImplementedError('abstract method')
Na'Tosha Bard
largefiles: batch statlfile requests when pushing a largefiles repo (issue3386)...
r17127 def exists(self, hashes):
Mads Kiilerich
largefiles: fold oddly named _verify into remotestore.exists
r18573 '''Check to see if the store contains the given hashes. Given an
iterable of hashes it returns a mapping from hash to bool.'''
various
hgext: add largefiles extension...
r15168 raise NotImplementedError('abstract method')
def get(self, files):
'''Get the specified largefiles from the store and write to local
files under repo.root. files is a list of (filename, hash)
Mads Kiilerich
fix trivial spelling errors
r17424 tuples. Return (success, missing), lists of files successfully
various
hgext: add largefiles extension...
r15168 downloaded and those not found in the store. success is a list
of (filename, hash) tuples; missing is a list of filenames that
we could not get. (The detailed error message will already have
been presented to the user, so missing is just supplied as a
summary.)'''
success = []
missing = []
ui = self.ui
at = 0
Mads Kiilerich
largefiles: stat all largefiles in one batch before downloading...
r19008 available = self.exists(set(hash for (_filename, hash) in files))
various
hgext: add largefiles extension...
r15168 for filename, hash in files:
ui.progress(_('getting largefiles'), at, unit='lfile',
total=len(files))
at += 1
ui.note(_('getting %s:%s\n') % (filename, hash))
Mads Kiilerich
largefiles: stat all largefiles in one batch before downloading...
r19008 if not available.get(hash):
ui.warn(_('%s: largefile %s not available from %s\n')
Mads Kiilerich
largefiles: hide passwords in URLs in ui messages
r19950 % (filename, hash, util.hidepassword(self.url)))
Mads Kiilerich
largefiles: stat all largefiles in one batch before downloading...
r19008 missing.append(filename)
continue
Mads Kiilerich
largefiles: refactor basestore, extract _gethash method
r19918 if self._gethash(filename, hash):
success.append((filename, hash))
else:
various
hgext: add largefiles extension...
r15168 missing.append(filename)
ui.progress(_('getting largefiles'), None)
return (success, missing)
Mads Kiilerich
largefiles: refactor basestore, extract _gethash method
r19918 def _gethash(self, filename, hash):
"""Get file with the provided hash and store it in the local repo's
store and in the usercache.
filename is for informational messages only.
"""
util.makedirs(lfutil.storepath(self.repo, ''))
storefilename = lfutil.storepath(self.repo, hash)
tmpname = storefilename + '.tmp'
tmpfile = util.atomictempfile(tmpname,
createmode=self.repo.store.createmode)
try:
gothash = self._getfile(tmpfile, filename, hash)
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except StoreError as err:
Mads Kiilerich
largefiles: refactor basestore, extract _gethash method
r19918 self.ui.warn(err.longmessage())
gothash = ""
tmpfile.close()
if gothash != hash:
if gothash != "":
self.ui.warn(_('%s: data corruption (expected %s, got %s)\n')
% (filename, hash, gothash))
util.unlink(tmpname)
return False
util.rename(tmpname, storefilename)
lfutil.linktousercache(self.repo, hash)
return True
various
hgext: add largefiles extension...
r15168 def verify(self, revs, contents=False):
'''Verify the existence (and, optionally, contents) of every big
file revision referenced by every changeset in revs.
Return 0 if all is well, non-zero on any errors.'''
failed = False
Mads Kiilerich
largefiles: verify status should be written as status, not as write...
r18546 self.ui.status(_('searching %d changesets for largefiles\n') %
len(revs))
various
hgext: add largefiles extension...
r15168 verified = set() # set of (filename, filenode) tuples
for rev in revs:
cctx = self.repo[rev]
cset = "%d:%s" % (cctx.rev(), node.short(cctx.node()))
Mads Kiilerich
largefiles: verify all files in each revision and report errors in any revision...
r18486 for standin in cctx:
if self._verifyfile(cctx, cset, contents, standin, verified):
failed = True
various
hgext: add largefiles extension...
r15168
Na'Tosha Bard
largefiles: remove use of underscores that breaks coding convention
r16247 numrevs = len(verified)
numlfiles = len(set([fname for (fname, fnode) in verified]))
various
hgext: add largefiles extension...
r15168 if contents:
Mads Kiilerich
largefiles: verify status should be written as status, not as write...
r18546 self.ui.status(
_('verified contents of %d revisions of %d largefiles\n')
% (numrevs, numlfiles))
various
hgext: add largefiles extension...
r15168 else:
Mads Kiilerich
largefiles: verify status should be written as status, not as write...
r18546 self.ui.status(
_('verified existence of %d revisions of %d largefiles\n')
% (numrevs, numlfiles))
various
hgext: add largefiles extension...
r15168 return int(failed)
def _getfile(self, tmpfile, filename, hash):
'''Fetch one revision of one file from the store and write it
to tmpfile. Compute the hash of the file on-the-fly as it
Mads Kiilerich
largefiles: refactoring - return hex from _getfile and copyandhash
r18999 downloads and return the hash. Close tmpfile. Raise
various
hgext: add largefiles extension...
r15168 StoreError if unable to download the file (e.g. it does not
exist in the store).'''
raise NotImplementedError('abstract method')
def _verifyfile(self, cctx, cset, contents, standin, verified):
'''Perform the actual verification of a file in the store.
Mads Kiilerich
largefiles: docstrings for verify methods
r18574 'cset' is only used in warnings.
'contents' controls verification of content hash.
'standin' is the standin path of the largefile to verify.
'verified' is maintained as a set of already verified files.
Returns _true_ if it is a standin and any problems are found!
various
hgext: add largefiles extension...
r15168 '''
raise NotImplementedError('abstract method')
import localstore, wirestore
_storeprovider = {
'file': [localstore.localstore],
'http': [wirestore.wirestore],
'https': [wirestore.wirestore],
'ssh': [wirestore.wirestore],
}
_scheme_re = re.compile(r'^([a-zA-Z0-9+-.]+)://')
# During clone this function is passed the src's ui object
# but it needs the dest's ui object so it can read out of
# the config file. Use repo.ui instead.
def _openstore(repo, remote=None, put=False):
ui = repo.ui
if not remote:
Na'Tosha Bard
largefiles: fix caching largefiles from an aliased repo (issue3212)
r15943 lfpullsource = getattr(repo, 'lfpullsource', None)
if lfpullsource:
path = ui.expandpath(lfpullsource)
Mads Kiilerich
largefiles: use 'default' path for pulling largefiles, not 'default-push'...
r23941 elif put:
path = ui.expandpath('default-push', 'default')
Na'Tosha Bard
largefiles: fix caching largefiles from an aliased repo (issue3212)
r15943 else:
Mads Kiilerich
largefiles: use 'default' path for pulling largefiles, not 'default-push'...
r23941 path = ui.expandpath('default')
Greg Ward
largefiles: improve comments, internal docstrings...
r15252
# ui.expandpath() leaves 'default-push' and 'default' alone if
# they cannot be expanded: fallback to the empty string,
# meaning the current directory.
various
hgext: add largefiles extension...
r15168 if path == 'default-push' or path == 'default':
path = ''
remote = repo
else:
Mads Kiilerich
largefiles: allow use of urls with #revision...
r18489 path, _branches = hg.parseurl(path)
various
hgext: add largefiles extension...
r15168 remote = hg.peer(repo, {}, path)
# The path could be a scheme so use Mercurial's normal functionality
# to resolve the scheme to a repository and use its path
Matt Mackall
largefile: fix up hasattr usage
r15169 path = util.safehasattr(remote, 'url') and remote.url() or remote.path
various
hgext: add largefiles extension...
r15168
match = _scheme_re.match(path)
if not match: # regular filesystem path
scheme = 'file'
else:
scheme = match.group(1)
try:
storeproviders = _storeprovider[scheme]
except KeyError:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_('unsupported URL scheme %r') % scheme)
various
hgext: add largefiles extension...
r15168
Na'Tosha Bard
largefiles: remove use of underscores that breaks coding convention
r16247 for classobj in storeproviders:
various
hgext: add largefiles extension...
r15168 try:
Na'Tosha Bard
largefiles: remove use of underscores that breaks coding convention
r16247 return classobj(ui, repo, remote)
various
hgext: add largefiles extension...
r15168 except lfutil.storeprotonotcapable:
pass
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_('%s does not appear to be a largefile store') %
Mads Kiilerich
largefiles: hide passwords in URLs in ui messages
r19950 util.hidepassword(path))