2013-06-21 21:14:29 +04:00
|
|
|
# shallowrepo.py - shallow repository that uses remote filelogs
|
2013-05-18 05:08:53 +04:00
|
|
|
#
|
|
|
|
# Copyright 2013 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.
|
|
|
|
|
2014-07-04 00:05:11 +04:00
|
|
|
from mercurial.node import hex, nullid, nullrev, bin
|
2013-05-18 05:08:53 +04:00
|
|
|
from mercurial.i18n import _
|
2014-07-04 00:05:11 +04:00
|
|
|
from mercurial import localrepo, context, util, match, scmutil
|
2013-08-30 22:09:19 +04:00
|
|
|
from mercurial.extensions import wrapfunction
|
2013-09-09 22:44:08 +04:00
|
|
|
import remotefilelog, remotefilectx, fileserverclient, shallowbundle, os
|
2013-05-21 02:03:07 +04:00
|
|
|
|
2013-11-26 04:36:44 +04:00
|
|
|
requirement = "remotefilelog"
|
|
|
|
|
2013-06-12 03:41:55 +04:00
|
|
|
def wraprepo(repo):
|
2013-05-21 02:03:07 +04:00
|
|
|
class shallowrepository(repo.__class__):
|
2013-08-15 22:00:51 +04:00
|
|
|
@util.propertycache
|
|
|
|
def name(self):
|
|
|
|
return self.ui.config('remotefilelog', 'reponame', '')
|
|
|
|
|
2013-05-21 02:03:07 +04:00
|
|
|
def file(self, f):
|
|
|
|
if f[0] == '/':
|
|
|
|
f = f[1:]
|
2013-09-26 21:46:06 +04:00
|
|
|
|
|
|
|
if self.shallowmatch(f):
|
|
|
|
return remotefilelog.remotefilelog(self.sopener, f, self)
|
|
|
|
else:
|
|
|
|
return super(shallowrepository, self).file(f)
|
2013-05-21 02:03:07 +04:00
|
|
|
|
|
|
|
def filectx(self, path, changeid=None, fileid=None):
|
2013-09-26 21:46:06 +04:00
|
|
|
if self.shallowmatch(path):
|
|
|
|
return remotefilectx.remotefilectx(self, path, changeid, fileid)
|
|
|
|
else:
|
|
|
|
return super(shallowrepository, self).filectx(path, changeid, fileid)
|
2013-05-21 02:03:07 +04:00
|
|
|
|
2013-09-26 21:46:06 +04:00
|
|
|
def pull(self, remote, *args, **kwargs):
|
2013-11-26 02:22:56 +04:00
|
|
|
# Hook into the callstream/getbundle to insert bundle capabilities
|
|
|
|
# during a pull.
|
|
|
|
def remotecallstream(orig, command, **opts):
|
|
|
|
if command == 'getbundle' and 'remotefilelog' in remote._capabilities():
|
|
|
|
bundlecaps = opts.get('bundlecaps')
|
|
|
|
if bundlecaps:
|
|
|
|
bundlecaps = [bundlecaps]
|
|
|
|
else:
|
|
|
|
bundlecaps = []
|
|
|
|
bundlecaps.append('remotefilelog')
|
|
|
|
if self.includepattern:
|
|
|
|
bundlecaps.append("includepattern=" + '\0'.join(self.includepattern))
|
|
|
|
if self.excludepattern:
|
|
|
|
bundlecaps.append("excludepattern=" + '\0'.join(self.excludepattern))
|
|
|
|
opts['bundlecaps'] = ','.join(bundlecaps)
|
|
|
|
return orig(command, **opts)
|
2013-09-26 21:46:06 +04:00
|
|
|
|
2013-11-26 02:22:56 +04:00
|
|
|
def localgetbundle(orig, source, heads=None, common=None, bundlecaps=None):
|
|
|
|
if not bundlecaps:
|
|
|
|
bundlecaps = []
|
|
|
|
bundlecaps.append('remotefilelog')
|
2014-04-05 02:55:06 +04:00
|
|
|
return orig(source, heads=heads, common=common, bundlecaps=bundlecaps)
|
2013-09-26 21:46:06 +04:00
|
|
|
|
2013-11-26 02:22:56 +04:00
|
|
|
if hasattr(remote, '_callstream'):
|
|
|
|
wrapfunction(remote, '_callstream', remotecallstream)
|
2013-12-13 00:34:39 +04:00
|
|
|
elif hasattr(remote, 'getbundle'):
|
2013-11-26 02:22:56 +04:00
|
|
|
wrapfunction(remote, 'getbundle', localgetbundle)
|
2013-09-26 21:46:06 +04:00
|
|
|
|
2014-08-07 05:50:57 +04:00
|
|
|
return super(shallowrepository, self).pull(remote, *args, **kwargs)
|
2014-07-04 00:05:11 +04:00
|
|
|
|
2014-08-19 20:33:31 +04:00
|
|
|
def prefetch(self, revs, base=None, pats=None, opts=None):
|
2014-07-04 00:05:11 +04:00
|
|
|
"""Prefetches all the necessary file revisions for the given revs
|
|
|
|
"""
|
2014-08-19 20:33:31 +04:00
|
|
|
mf = repo.manifest
|
|
|
|
if base is not None:
|
|
|
|
mfdict = mf.read(repo[base].manifestnode())
|
|
|
|
skip = set(mfdict.iteritems())
|
|
|
|
else:
|
|
|
|
skip = set()
|
|
|
|
|
|
|
|
# Copy the skip set to start large and avoid constant resizing,
|
|
|
|
# and since it's likely to be very similar to the prefetch set.
|
|
|
|
files = skip.copy()
|
2014-07-04 00:05:11 +04:00
|
|
|
visited = set()
|
|
|
|
visited.add(nullrev)
|
|
|
|
for rev in sorted(revs):
|
|
|
|
ctx = repo[rev]
|
|
|
|
if pats:
|
|
|
|
m = scmutil.match(ctx, pats, opts)
|
|
|
|
|
|
|
|
mfnode = ctx.manifestnode()
|
2014-08-07 05:50:57 +04:00
|
|
|
mfrev = mf.rev(mfnode)
|
2014-07-04 00:05:11 +04:00
|
|
|
|
|
|
|
# Decompressing manifests is expensive.
|
|
|
|
# When possible, only read the deltas.
|
2014-08-07 05:50:57 +04:00
|
|
|
p1, p2 = mf.parentrevs(mfrev)
|
2014-07-04 00:05:11 +04:00
|
|
|
if p1 in visited and p2 in visited:
|
|
|
|
mfdict = mf.readfast(mfnode)
|
|
|
|
else:
|
|
|
|
mfdict = mf.read(mfnode)
|
|
|
|
|
2014-08-19 20:33:31 +04:00
|
|
|
files.update(pf for pf in mfdict.iteritems()
|
|
|
|
if not pats or m(pf[0]))
|
2014-07-04 00:05:11 +04:00
|
|
|
|
2014-08-07 05:50:57 +04:00
|
|
|
visited.add(mfrev)
|
2014-07-04 00:05:11 +04:00
|
|
|
|
2014-08-19 20:33:31 +04:00
|
|
|
files.difference_update(skip)
|
|
|
|
results = [(path, hex(fnode)) for (path, fnode) in files]
|
|
|
|
repo.fileservice.prefetch(results)
|
2013-09-09 22:44:08 +04:00
|
|
|
|
2013-08-30 22:09:19 +04:00
|
|
|
# Wrap dirstate.status here so we can prefetch all file nodes in
|
|
|
|
# the lookup set before localrepo.status uses them.
|
|
|
|
def status(orig, match, subrepos, ignored, clean, unknown):
|
|
|
|
lookup, modified, added, removed, deleted, unknown, ignored, \
|
|
|
|
clean = orig(match, subrepos, ignored, clean, unknown)
|
|
|
|
|
|
|
|
if lookup:
|
|
|
|
files = []
|
2013-09-05 06:07:01 +04:00
|
|
|
parents = repo.parents()
|
2013-08-30 22:09:19 +04:00
|
|
|
for fname in lookup:
|
2013-09-05 06:07:01 +04:00
|
|
|
for ctx in parents:
|
|
|
|
if fname in ctx:
|
|
|
|
fnode = ctx.filenode(fname)
|
|
|
|
files.append((fname, hex(fnode)))
|
2013-08-30 22:09:19 +04:00
|
|
|
|
2014-02-12 02:41:56 +04:00
|
|
|
repo.fileservice.prefetch(files)
|
2013-08-30 22:09:19 +04:00
|
|
|
|
|
|
|
return (lookup, modified, added, removed, deleted, unknown, \
|
|
|
|
ignored, clean)
|
|
|
|
|
|
|
|
wrapfunction(repo.dirstate, 'status', status)
|
|
|
|
|
2013-05-21 02:03:07 +04:00
|
|
|
repo.__class__ = shallowrepository
|
2013-09-09 21:23:29 +04:00
|
|
|
|
2013-09-26 21:46:06 +04:00
|
|
|
repo.shallowmatch = match.always(repo.root, '')
|
2014-02-12 02:41:56 +04:00
|
|
|
repo.fileservice = fileserverclient.fileserverclient(repo)
|
2013-09-26 21:46:06 +04:00
|
|
|
|
|
|
|
repo.includepattern = repo.ui.configlist("remotefilelog", "includepattern", None)
|
|
|
|
repo.excludepattern = repo.ui.configlist("remotefilelog", "excludepattern", None)
|
|
|
|
if repo.includepattern or repo.excludepattern:
|
|
|
|
repo.shallowmatch = match.match(repo.root, '', None,
|
|
|
|
repo.includepattern, repo.excludepattern)
|
|
|
|
|
2013-09-09 21:23:29 +04:00
|
|
|
localpath = os.path.join(repo.sopener.vfs.base, 'data')
|
|
|
|
if not os.path.exists(localpath):
|
|
|
|
os.makedirs(localpath)
|