Changeset 35d64ba16cb6…
Parent bf74641d43fb…
by Benjamin Pollack <benjamin@fogcreek.com>
Changes to 12 files · Browse files at 35d64ba16cb6 Showing diff from parent bf74641d43fb Diff from another changeset...
@@ -21,8 +21,11 @@ '''
from mercurial import commands
+from mercurial import wireproto
+
import bfsetup
import bfcommands
+import bfproto
reposetup = bfsetup.reposetup
uisetup = bfsetup.uisetup
@@ -30,3 +33,14 @@commands.norepo += " kbfconvert"
cmdtable = bfcommands.cmdtable
+
+def extsetup(ui):
+ wireproto.commands['putbfile'] = (bfproto.putbfile, 'sha')
+ wireproto.commands['getbfile'] = (bfproto.getbfile, 'sha')
+ wireproto.commands['statbfile'] = (bfproto.statbfile, 'sha')
+ wireproto.commands['capabilities'] = (bfproto.capabilities, '')
+ wireproto.dispatch = bfproto.dispatch
+
+ wireproto.wirerepository.putbfile = bfproto.wirerepo_putbfile
+ wireproto.wirerepository.getbfile = bfproto.wirerepo_getbfile
+ wireproto.wirerepository.statbfile = bfproto.wirerepo_statbfile
|
@@ -20,9 +20,14 @@ self.detail = detail
def longmessage(self):
- return ("%s: %s\n"
- "(failed URL: %s)\n"
- % (self.filename, self.detail, self.url))
+ if self.url:
+ return ('%s: %s\n'
+ '(failed URL: %s)\n'
+ % (self.filename, self.detail, self.url))
+ else:
+ return ('%s: %s\n'
+ '(no default or default-push path set in hgrc)\n'
+ % (self.filename, self.detail))
def __str__(self):
return "%s: %s" % (self.url, self.detail)
@@ -74,6 +79,7 @@ try:
bhash = self._getfile(tmpfile, filename, hash)
except StoreError, err:
+ tmpfile.close()
ui.warn(err.longmessage())
os.remove(tmpfilename)
missing.append(filename)
@@ -157,12 +163,14 @@def _openstore(repo, path=None, put=False):
ui = repo.ui
if not path:
- path = ui.expandpath('default-push', 'default')
- # If 'default-push' and 'default' can't be expanded
- # they are just returned. In that case use the empty string which
- # use the filescheme.
- if path is 'default-push' or path is 'default':
- path = ''
+ path = getattr(repo, 'bfpullsource', None)
+ if not path:
+ path = ui.expandpath('default-push', 'default')
+ # If 'default-push' and 'default' can't be expanded
+ # they are just returned. In that case fail with an informative
+ # error message
+ if path in ('default-push', 'default'):
+ path = ''
# The path could be a scheme so use Mercurial's normal functionality
# to resolve the scheme to a repository and use its path
@@ -176,7 +184,7 @@ scheme = match.group(1)
try:
- (mod, klass) = _storeprovider[scheme]
+ mod, klass = _storeprovider[scheme]
except KeyError:
raise util.Abort(_('unsupported URL scheme %r') % scheme)
|
@@ -421,7 +421,7 @@
removed = 0
for bfile in bfdirstate:
- if filelist == None or bfile in filelist:
+ if filelist is None or bfile in filelist:
if not os.path.exists(repo.wjoin(bfutil.standin(bfile))):
if os.path.exists(repo.wjoin(bfile)):
os.unlink(repo.wjoin(bfile))
|
|
@@ -0,0 +1,57 @@ + import tempfile, shutil, os
+
+from mercurial.i18n import _
+from mercurial import wireproto, error, util
+
+import bfutil, bfsetup
+
+def putbfile(repo, proto, sha):
+ fd, tempname = tempfile.mkstemp(prefix='hg-putbfile-')
+ with os.fdopen(fd, 'wb+') as fp:
+ proto.getfile(fp)
+ bfutil.copytocacheabsolute(repo, tempname, sha)
+ return ''
+
+def getbfile(repo, proto, sha):
+ filename = bfutil.findfile(repo, sha)
+ if not filename:
+ raise util.Abort(_('requested bfile %s not present in cache') % sha)
+ f = open(filename, 'rb')
+ return wireproto.streamres(f)
+
+# '0' for OK, '1' for invalid checksum, '2' for missing
+def statbfile(repo, proto, sha):
+ filename = bfutil.findfile(repo, sha)
+ if not filename:
+ return '2\n'
+ with open(filename, 'rb') as f:
+ return '0\n' if bfutil.hexsha1(f) == sha else '1\n'
+
+def wirerepo_putbfile(self, sha, fd):
+ return self._callstream("putbfile", data=fd, sha=sha, headers={'content-type':'application/mercurial-0.1'})
+
+def wirerepo_getbfile(self, sha):
+ return self._callstream("getbfile", sha=sha)
+
+def wirerepo_statbfile(self, sha):
+ try:
+ return int(self._call("statbfile", sha=sha))
+ except:
+ return 2
+ # if the server returns something that's not an integer followed by a
+ # newline, it's not kbfiles-capable, so obviously it doesn't have the
+ # bfile; any other exception means _something_ went wrong, so tell the
+ # caller the bfile is missing
+
+def dispatch(repo, proto, command):
+ func, spec = wireproto.commands[command]
+ args = proto.getargs(spec)
+ if len(args) > 0 and isinstance(args[-1], dict):
+ if bfutil.listbfiles(repo) and command in affectedcommands and not args[-1].pop('kbfiles'):
+ return '0\n'
+ return func(repo, proto, *args)
+
+def capabilities(repo, proto):
+ return wireproto.capabilities(repo, proto) + ' bfilestore=serve'
+
+affectedcommands = [ 'changegroup', 'changegroupsubset', 'getbundle', 'unbundle', 'stream_out', 'pushkey' ]
|
@@ -88,7 +88,7 @@ return result
ctx.__class__ = bfiles_ctx
return ctx
-
+
# Figure out the status of big files and insert them into the
# appropriate list in the result. Also removes standin files from
# the listing. This function reverts to the original status if
@@ -233,7 +233,7 @@ try:
if getattr(repo, "_isrebasing", False):
# We have to take the time to pull down the new bfiles now. Otherwise
- # if we are rebasing, any bfiles that were modified in the changesets we
+ # if we are rebasing, any bfiles that were modified in the changesets we
# are rebasing on top of get overwritten either by the rebase or in the
# first commit after the rebase.
bfcommands.updatebfiles(repo.ui, repo)
@@ -942,7 +942,7 @@# When we rebase a repository with remotely changed bfiles, we need
# to take some extra care so that the bfiles are correctly updated
# in the working copy
-def override_pull(orig, ui, repo, source="default", **opts):
+def override_pull(orig, ui, repo, source=None, **opts):
if opts.get('rebase', False):
repo._isrebasing = True
try:
@@ -962,6 +962,9 @@ def _dummy(*args, **kwargs):
pass
commands.postincoming = _dummy
+ repo.bfpullsource = source
+ if not source:
+ source = 'default'
try:
result = commands.pull(ui, repo, source, **opts)
finally:
@@ -974,6 +977,9 @@ finally:
repo._isrebasing = False
else:
+ repo.bfpullsource = source
+ if not source:
+ source = 'default'
result = orig(ui, repo, source, **opts)
return result
@@ -1267,7 +1273,7 @@ bfdirstate.add(file)
bfdirstate.write()
return result
-
+
def uisetup(ui):
# Disable auto-status for some commands which assume that all
# files in the result are under Mercurial's control
|
@@ -1,10 +1,6 @@ '''bfiles utility code: must not import other modules in this package.'''
-import os
-import errno
-import inspect
-import shutil
-import stat
+import os, errno, inspect, shutil, stat, hashlib
from mercurial import \
util, dirstate, cmdutil, match as match_
@@ -63,7 +59,7 @@ repo[None].forget(list)
finally:
wlock.release()
-
+
return remove(list, unlink=unlink)
def repo_forget(repo, list):
@@ -247,12 +243,15 @@ hash = readstandin(repo, standin(file))
if incache(repo, hash):
return
+ copytocacheabsolute(repo, repo.wjoin(file), hash)
+
+def copytocacheabsolute(repo, file, hash):
createdir(os.path.dirname(cachepath(repo, hash)))
if insystemcache(repo.ui, hash):
link(systemcachepath(repo.ui, hash), cachepath(repo, hash))
else:
- shutil.copyfile(repo.wjoin(file), cachepath(repo, hash))
- os.chmod(cachepath(repo, hash), os.stat(repo.wjoin(file)).st_mode)
+ shutil.copyfile(file, cachepath(repo, hash))
+ os.chmod(cachepath(repo, hash), os.stat(file).st_mode)
createdir(os.path.dirname(systemcachepath(repo.ui, hash)))
link(cachepath(repo, hash), systemcachepath(repo.ui, hash))
@@ -287,7 +286,7 @@ except ImportError:
# Mercurial <= 1.8
match = cmdutil.match(repo, pats, opts)
-
+
if not showbad:
match.bad = lambda f, msg: None
return match
@@ -435,6 +434,12 @@ url = join(url, a)
return url
+def hexsha1(data):
+ h = hashlib.sha1()
+ for chunk in util.filechunkiter(data):
+ h.update(chunk)
+ return h.hexdigest()
+
# Convert a path to a unix style path. This is used to give a
# canonical path to the bfdirstate.
def unixpath(path):
|
|
|
@@ -1,10 +1,8 @@ '''HTTP-based store.'''
-import inspect
-import urlparse
-import urllib2
+import inspect, urlparse, urllib2
-from mercurial import util, url as url_
+from mercurial import util, hg, url as url_
from mercurial.i18n import _
try:
@@ -17,19 +15,27 @@class httpstore(basestore.basestore):
"""A store accessed via HTTP"""
def __init__(self, ui, repo, url):
- url = bfutil.urljoin(url, 'bfile')
+ store_type = None
+ try:
+ remoterepo = hg.peer(repo, dict([]), url)
+ store_type = remoterepo.capable('bfilestore')
+ except:
+ pass
+ if store_type == 'serve':
+ self.proto = hgservestoreproto(remoterepo, url)
+ elif store_type == 'kiln':
+ self.proto = kilnstoreproto(ui, url)
+ else:
+ ui.note(_('bfilestore capability not found; assuming %s is a Kiln repo') % url)
+ self.proto = kilnstoreproto(ui, url)
+ self.url = self.proto.url
+ self.rawurl, authinfo = urlparse.urlsplit(self.url)[1:3]
super(httpstore, self).__init__(ui, repo, url)
- self.rawurl, self.path = urlparse.urlsplit(self.url)[1:3]
- try:
- # Mercurial >= 1.9
- baseurl, authinfo = util.url(self.url).authinfo()
- except AttributeError:
- # Mercurial <= 1.8
- baseurl, authinfo = url_.getauthinfo(self.url)
- self.opener = url_.opener(self.ui, authinfo)
def put(self, source, hash):
self.sendfile(source, hash)
+ if not self._verify(hash):
+ raise util.Abort(_('could not put %s to remote store') % source)
self.ui.debug('put %s to remote store\n' % source)
def exists(self, hash):
@@ -40,13 +46,7 @@ return
self.ui.debug('httpstore.sendfile(%s, %s)\n' % (filename, hash))
- try:
- # Mercurial >= 1.9
- baseurl, authinfo = util.url(self.url).authinfo()
- except AttributeError:
- # Mercurial <= 1.8
- baseurl, authinfo = url_.getauthinfo(self.url)
- fd = None
+ Ffd = None
try:
try:
# Mercurial >= 1.9
@@ -58,10 +58,8 @@ else:
# Mercurial <= 1.7
fd = url_.httpsendfile(filename, 'rb')
- request = urllib2.Request(bfutil.urljoin(baseurl, hash), fd)
try:
- url = self.opener.open(request)
- self.ui.note(_('[OK] %s/%s\n') % (self.rawurl, url.geturl()))
+ url = self.proto.put(hash, fd)
except urllib2.HTTPError, e:
raise util.Abort(_('unable to POST: %s\n') % e.msg)
except Exception, e:
@@ -70,53 +68,27 @@ if fd: fd.close()
def _getfile(self, tmpfile, filename, hash):
+ stat = self.proto.stat(hash)
+ if stat:
+ raise util.Abort(_('bfile %s is %s') %
+ (hash, 'invalid' if stat == 1 else 'missing'))
try:
- # Mercurial >= 1.9
- baseurl, authinfo = util.url(self.url).authinfo()
- except AttributeError:
- # Mercurial <= 1.8
- baseurl, authinfo = url_.getauthinfo(self.url)
- url = bfutil.urljoin(baseurl, hash)
- try:
- request = urllib2.Request(url)
- infile = self.opener.open(request)
+ infile = self.proto.get(hash)
except urllib2.HTTPError, err:
detail = _("HTTP error: %s %s") % (err.code, err.msg)
- raise basestore.StoreError(filename, hash, url, detail)
+ raise basestore.StoreError(filename, hash, self.url, detail)
except urllib2.URLError, err:
# This usually indicates a connection problem, so don't
# keep trying with the other files... they will probably
# all fail too.
reason = err[0][1] # assumes err[0] is a socket.error
- raise util.Abort('%s: %s' % (baseurl, reason))
+ raise util.Abort('%s: %s' % (self.url, reason))
return bfutil.copyandhash(bfutil.blockstream(infile), tmpfile)
def _verify(self, hash):
- try:
- # Mercurial >= 1.9
- baseurl, authinfo = util.url(self.url).authinfo()
- except AttributeError:
- # Mercurial <= 1.8
- baseurl, authinfo = url_.getauthinfo(self.url)
- store_path = bfutil.urljoin(baseurl, hash)
- request = urllib2.Request(store_path)
- request.add_header('SHA1-Request', hash)
- try:
- url = self.opener.open(request)
- if 'Content-SHA1' in url.info() and hash == url.info()['Content-SHA1']:
- return True
- else:
- return False
- except:
- return False
+ return not self.proto.stat(hash)
def _verifyfile(self, cctx, cset, contents, standin, verified):
- try:
- # Mercurial >= 1.9
- baseurl, authinfo = util.url(self.url).authinfo()
- except AttributeError:
- # Mercurial <= 1.8
- baseurl, authinfo = url_.getauthinfo(self.url)
filename = bfutil.splitstandin(standin)
if not filename:
return False
@@ -126,33 +98,59 @@ return False
expect_hash = fctx.data()[0:40]
- store_path = bfutil.urljoin(baseurl, expect_hash)
verified.add(key)
- request = urllib2.Request(store_path)
- request.add_header('SHA1-Request',expect_hash)
+ stat = self.proto.stat(hash)
+ if not stat:
+ return False
+ elif stat == 1:
+ self.ui.warn(
+ _('changeset %s: %s: contents differ\n (%s)\n')
+ % (cset, filename, store_path))
+ return True # failed
+ elif stat == 2:
+ self.ui.warn(
+ _('changeset %s: %s missing\n (%s)\n')
+ % (cset, filename, store_path))
+ return True # failed
+ else:
+ raise util.Abort(_('check failed, unexpected response'
+ 'statbfile: %d') % stat)
+
+class kilnstoreproto(object):
+ def __init__(self, ui, url):
+ self.url = bfutil.urljoin(url, "bfile")
try:
- url = self.opener.open(request)
- if 'Content-SHA1' in url.info():
- rhash = url.info()['Content-SHA1']
- if rhash == expect_hash:
- return False
- else:
- self.ui.warn(
- _('changeset %s: %s: contents differ\n (%s)\n')
- % (cset, filename, store_path))
- return True # failed
- else:
- self.ui.warn(_('remote did not send a hash, '
- 'it probably does not understand this protocol\n'))
- return False
+ # Mercurial >= 1.9
+ baseurl, authinfo = util.url(self.url).authinfo()
+ except AttributeError:
+ # Mercurial <= 1.8
+ baseurl, authinfo = url_.getauthinfo(self.url)
+ self.opener = url_.opener(ui, authinfo)
+ def put(self, hash, fd):
+ req = urllib2.Request(bfutil.urljoin(self.url, hash), fd)
+ return self.opener.open(req)
+ def get(self, hash):
+ req = urllib2.Request(bfutil.urljoin(self.url, hash))
+ req.add_header('SHA1-Request', hash)
+ return self.opener.open(req)
+ # '0' for OK, '1' for invalid checksum, '2' for missing
+ def stat(self, hash):
+ try:
+ return 0 if hash == bfutil.hexsha1(self.get(hash)) else 1
except urllib2.HTTPError, e:
if e.code == 404:
- self.ui.warn(
- _('changeset %s: %s missing\n (%s)\n')
- % (cset, filename, store_path))
- return True # failed
+ return 2
else:
- raise util.Abort(_('check failed, unexpected response'
- 'status: %d: %s') % (e.code, e.msg))
+ raise
+class hgservestoreproto(object):
+ def __init__(self, repo, url):
+ self.repo = repo
+ self.url = url
+ def put(self, hash, fd):
+ return self.repo.putbfile(hash, fd)
+ def get(self, hash):
+ return self.repo.getbfile(hash)
+ def stat(self, hash):
+ return self.repo.statbfile(hash)
|
@@ -32,7 +32,7 @@ stdout, stderr = child.communicate()
versions = re.findall(r'\d+\.\d+(?:\.\d+)?', stdout)
parts = [re.match(r'\d+', v).group(0) for v in versions[0].split('.')]
-
+
version = [0, 0, 0]
for i, part in enumerate(map(int, parts)):
version[i] = part
|
@@ -5,6 +5,6 @@ # write access to that repo group.
KILNEXTPATH = '~/kiln/extensions'
-KILNURL = 'http://localhost/FogBugz/kiln'
+KILNURL = 'http://localhost/kiln'
USER = 'test'
PASSWORD = 'tester'
|
@@ -350,3 +350,41 @@ ''')
os.chdir('..')
common.checkrepos(hgt, 'repo1', 'repo2', [0, 3, 4, 5, 8, 11, 14, 17])
+
+hgt.announce('no default path error')
+os.mkdir('repo3')
+os.chdir('repo3')
+hgt.hg(['init'])
+hgt.hg(['pull', '../repo1'],
+ stdout='''pulling from ../repo1
+requesting all changes
+adding changesets
+adding manifests
+adding file changes
+added 18 changesets with 33 changes to 16 files
+(run 'hg update' to get a working copy)
+''')
+hgt.writefile('.hg/hgrc', '''[kilnbfiles]
+systemcache = .
+''')
+hgt.hg(['up'],
+ stdout='''13 files updated, 0 files merged, 0 files removed, 0 files unresolved
+getting changed bfiles
+0 big files updated, 0 removed
+''', stderr='''b1: Can't get file locally
+(no default or default-push path set in hgrc)
+dir/b1.foo: Can't get file locally
+(no default or default-push path set in hgrc)
+dir/b2: Can't get file locally
+(no default or default-push path set in hgrc)
+dir/b2222.foo: Can't get file locally
+(no default or default-push path set in hgrc)
+dir/b3.foo: Can't get file locally
+(no default or default-push path set in hgrc)
+dir/b3333.foo: Can't get file locally
+(no default or default-push path set in hgrc)
+dir/b4: Can't get file locally
+(no default or default-push path set in hgrc)
+dir/b4.foo: Can't get file locally
+(no default or default-push path set in hgrc)
+''')
|
@@ -68,3 +68,8 @@ hg commit -m merge
hg push ../repo1
hg up
+
+% no default path error
+hg init
+hg pull ../repo1
+hg up
|
@@ -84,7 +84,9 @@ if before != after:
try:
os.rename(self.__temporary_path, self.__original_path)
- except (IOError, WindowsError, OSError):
+ except WindowsError:
+ shutil.copyfile(self.__temporary_path, self.__original_path)
+ except (IOError, OSError):
pass
def get_cookiejar(ui):
|
Loading...