def find_user_password(self, realm, authuri): authinfo = urllib2.HTTPPasswordMgrWithDefaultRealm.find_user_password( self, realm, authuri) user, passwd = authinfo if user and passwd: self._writedebug(user, passwd) return (user, passwd) if not user or not passwd: res = httpconnectionmod.readauthforuri(self.ui, authuri, user) if res: group, auth = res user, passwd = auth.get('username'), auth.get('password') self.ui.debug("using auth.%s.* for authentication\n" % group) if not user or not passwd: u = util.url(authuri) u.query = None if not self.ui.interactive(): raise util.Abort(_('http authorization required for %s') % util.hidepassword(str(u))) self.ui.write(_("http authorization required for %s\n") % util.hidepassword(str(u))) self.ui.write(_("realm: %s\n") % realm) if user: self.ui.write(_("user: %s\n") % user) else: user = self.ui.prompt(_("user:"), default=None) if not passwd: passwd = self.ui.getpass() self.add_password(realm, authuri, user, passwd) self._writedebug(user, passwd) return (user, passwd)
def _incoming(displaychlist, subreporecurse, ui, repo, source, opts, buffered=False): """ Helper for incoming / gincoming. displaychlist gets called with (remoterepo, incomingchangesetlist, displayer) parameters, and is supposed to contain only code that can't be unified. """ source, branches = parseurl(ui.expandpath(source), opts.get('branch')) other = peer(repo, opts, source) ui.status(_('comparing with %s\n') % util.hidepassword(source)) revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev')) if revs: revs = [other.lookup(rev) for rev in revs] other, chlist, cleanupfn = bundlerepo.getremotechanges(ui, repo, other, revs, opts["bundle"], opts["force"]) try: if not chlist: ui.status(_("no changes found\n")) return subreporecurse() displayer = cmdutil.show_changeset(ui, other, opts, buffered) # XXX once graphlog extension makes it into core, # should be replaced by a if graph/else displaychlist(other, chlist, displayer) displayer.close() finally: cleanupfn() subreporecurse() return 0 # exit code is zero since we found incoming changes
def __init__(self, repo, remote, heads=None, force=False, bookmarks=()): # repo we pull into self.repo = repo # repo we pull from self.remote = remote # revision we try to pull (None is "all") self.heads = heads # bookmark pulled explicitly self.explicitbookmarks = bookmarks # do we force pull? self.force = force # the name the pull transaction self._trname = 'pull\n' + util.hidepassword(remote.url()) # hold the transaction once created self._tr = None # set of common changeset between local and remote before pull self.common = None # set of pulled head self.rheads = None # list of missing changeset to fetch remotely self.fetch = None # remote bookmarks data self.remotebookmarks = None # result of changegroup pulling (used as return code by pull) self.cgresult = None # list of step already done self.stepsdone = set()
def _incoming(displaychlist, subreporecurse, ui, repo, source, opts, buffered=False): """ Helper for incoming / gincoming. displaychlist gets called with (remoterepo, incomingchangesetlist, displayer) parameters, and is supposed to contain only code that can't be unified. """ source, branches = parseurl(ui.expandpath(source), opts.get('branch')) other = peer(repo, opts, source) ui.status(_('comparing with %s\n') % util.hidepassword(source)) revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev')) if revs: revs = [other.lookup(rev) for rev in revs] other, chlist, cleanupfn = bundlerepo.getremotechanges( ui, repo, other, revs, opts["bundle"], opts["force"]) try: if not chlist: ui.status(_("no changes found\n")) return subreporecurse() displayer = cmdutil.show_changeset(ui, other, opts, buffered) displaychlist(other, chlist, displayer) displayer.close() finally: cleanupfn() subreporecurse() return 0 # exit code is zero since we found incoming changes
def transaction(self): """Return an open transaction object, constructing if necessary""" if not self._tr: trname = '%s\n%s' % (self.source, util.hidepassword(self.url)) self._tr = self.repo.transaction(trname) self._tr.hookargs['source'] = self.source self._tr.hookargs['url'] = self.url return self._tr
def _outgoing(ui, repo, dest, opts): dest = ui.expandpath(dest or "default-push", dest or "default") dest, branches = parseurl(dest, opts.get("branch")) ui.status(_("comparing with %s\n") % util.hidepassword(dest)) revs, checkout = addbranchrevs(repo, repo, branches, opts.get("rev")) if revs: revs = [repo.lookup(rev) for rev in scmutil.revrange(repo, revs)] other = peer(repo, opts, dest) outgoing = discovery.findcommonoutgoing(repo.unfiltered(), other, revs, force=opts.get("force")) o = outgoing.missing if not o: scmutil.nochangesfound(repo.ui, repo, outgoing.excluded) return o, other
def find_user_password(self, realm, authuri): authinfo = urllib2.HTTPPasswordMgrWithDefaultRealm.find_user_password( self, realm, authuri) user, passwd = authinfo if user and passwd: self._writedebug(user, passwd) return (user, passwd) if not user or not passwd: res = httpconnectionmod.readauthforuri(self.ui, authuri, user) if res: group, auth = res user, passwd = auth.get('username'), auth.get('password') self.ui.debug("using auth.%s.* for authentication\n" % group) if not user or not passwd: u = util.url(authuri) u.query = None if not self.ui.interactive(): raise util.Abort( _('http authorization required for %s') % util.hidepassword(str(u))) self.ui.write( _("http authorization required for %s\n") % util.hidepassword(str(u))) self.ui.write(_("realm: %s\n") % realm) if user: self.ui.write(_("user: %s\n") % user) else: user = self.ui.prompt(_("user:"), default=None) if not passwd: passwd = self.ui.getpass() self.add_password(realm, authuri, user, passwd) self._writedebug(user, passwd) return (user, passwd)
def _outgoing(ui, repo, dest, opts): dest = ui.expandpath(dest or 'default-push', dest or 'default') dest, branches = parseurl(dest, opts.get('branch')) ui.status(_('comparing with %s\n') % util.hidepassword(dest)) revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev')) if revs: revs = [repo.lookup(rev) for rev in scmutil.revrange(repo, revs)] other = peer(repo, opts, dest) outgoing = discovery.findcommonoutgoing(repo.unfiltered(), other, revs, force=opts.get('force')) o = outgoing.missing if not o: scmutil.nochangesfound(repo.ui, repo, outgoing.excluded) return o, other
def _outgoing(ui, repo, dest, opts): dest = ui.expandpath(dest or 'default-push', dest or 'default') dest, branches = parseurl(dest, opts.get('branch')) ui.status(_('comparing with %s\n') % util.hidepassword(dest)) revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev')) if revs: revs = [repo.lookup(rev) for rev in revs] other = peer(repo, opts, dest) outgoing = discovery.findcommonoutgoing(repo, other, revs, force=opts.get('force')) o = outgoing.missing if not o: scmutil.nochangesfound(repo.ui, outgoing.excluded) return None return o
def _outgoing(ui, repo, dest, opts): dest = ui.expandpath(dest or 'default-push', dest or 'default') dest, branches = parseurl(dest, opts.get('branch')) ui.status(_('comparing with %s\n') % util.hidepassword(dest)) revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev')) if revs: revs = [repo.lookup(rev) for rev in revs] other = peer(repo, opts, dest) common, outheads = discovery.findcommonoutgoing(repo, other, revs, force=opts.get('force')) o = repo.changelog.findmissing(common, outheads) if not o: ui.status(_("no changes found\n")) return None return o
def __init__(self, repo, remote, heads=None, force=False): # repo we pull into self.repo = repo # repo we pull from self.remote = remote # revision we try to pull (None is "all") self.heads = heads # do we force pull? self.force = force # the name the pull transaction self._trname = 'pull\n' + util.hidepassword(remote.url()) # hold the transaction once created self._tr = None # set of common changeset between local and remote before pull self.common = None # set of pulled head self.rheads = None # list of missing changeset to fetch remotely self.fetch = None # result of changegroup pulling (used as return code by pull) self.cgresult = None # list of step remaining todo (related to future bundle2 usage) self.todosteps = set(['changegroup', 'phases', 'obsmarkers'])
def handleremotechangegroup(op, inpart): """apply a bundle10 on the repo, given an url and validation information All the information about the remote bundle to import are given as parameters. The parameters include: - url: the url to the bundle10. - size: the bundle10 file size. It is used to validate what was retrieved by the client matches the server knowledge about the bundle. - digests: a space separated list of the digest types provided as parameters. - digest:<digest-type>: the hexadecimal representation of the digest with that name. Like the size, it is used to validate what was retrieved by the client matches what the server knows about the bundle. When multiple digest types are given, all of them are checked. """ try: raw_url = inpart.params['url'] except KeyError: raise util.Abort(_('remote-changegroup: missing "%s" param') % 'url') parsed_url = util.url(raw_url) if parsed_url.scheme not in capabilities['b2x:remote-changegroup']: raise util.Abort( _('remote-changegroup does not support %s urls') % parsed_url.scheme) try: size = int(inpart.params['size']) except ValueError: raise util.Abort( _('remote-changegroup: invalid value for param "%s"') % 'size') except KeyError: raise util.Abort(_('remote-changegroup: missing "%s" param') % 'size') digests = {} for typ in inpart.params.get('digests', '').split(): param = 'digest:%s' % typ try: value = inpart.params[param] except KeyError: raise util.Abort( _('remote-changegroup: missing "%s" param') % param) digests[typ] = value real_part = util.digestchecker(url.open(op.ui, raw_url), size, digests) # Make sure we trigger a transaction creation # # The addchangegroup function will get a transaction object by itself, but # we need to make sure we trigger the creation of a transaction object used # for the whole processing scope. op.gettransaction() import exchange cg = exchange.readbundle(op.repo.ui, real_part, raw_url) if not isinstance(cg, changegroup.cg1unpacker): raise util.Abort( _('%s: not a bundle version 1.0') % util.hidepassword(raw_url)) ret = changegroup.addchangegroup(op.repo, cg, 'bundle2', 'bundle2') op.records.add('changegroup', {'return': ret}) if op.reply is not None: # This is definitly not the final form of this # return. But one need to start somewhere. part = op.reply.newpart('b2x:reply:changegroup') part.addparam('in-reply-to', str(inpart.id), mandatory=False) part.addparam('return', '%i' % ret, mandatory=False) try: real_part.validate() except util.Abort, e: raise util.Abort( _('bundle at %s is corrupted:\n%s') % (util.hidepassword(raw_url), str(e)))
def addchangegroup(repo, source, srctype, url, emptyok=False): """Add the changegroup returned by source.read() to this repo. srctype is a string like 'push', 'pull', or 'unbundle'. url is the URL of the repo where this changegroup is coming from. Return an integer summarizing the change to this repo: - nothing changed or no source: 0 - more heads than before: 1+added heads (2..n) - fewer heads than before: -1-removed heads (-2..-n) - number of heads stays the same: 1 """ repo = repo.unfiltered() def csmap(x): repo.ui.debug("add changeset %s\n" % short(x)) return len(cl) def revmap(x): return cl.rev(x) if not source: return 0 repo.hook('prechangegroup', throw=True, source=srctype, url=url) changesets = files = revisions = 0 efiles = set() # write changelog data to temp files so concurrent readers will not see # inconsistent view cl = repo.changelog cl.delayupdate() oldheads = cl.heads() tr = repo.transaction("\n".join([srctype, util.hidepassword(url)])) try: trp = weakref.proxy(tr) # pull off the changeset group repo.ui.status(_("adding changesets\n")) clstart = len(cl) class prog(object): step = _('changesets') count = 1 ui = repo.ui total = None def __call__(repo): repo.ui.progress(repo.step, repo.count, unit=_('chunks'), total=repo.total) repo.count += 1 pr = prog() source.callback = pr source.changelogheader() srccontent = cl.addgroup(source, csmap, trp) if not (srccontent or emptyok): raise util.Abort(_("received changelog group is empty")) clend = len(cl) changesets = clend - clstart for c in xrange(clstart, clend): efiles.update(repo[c].files()) efiles = len(efiles) repo.ui.progress(_('changesets'), None) # pull off the manifest group repo.ui.status(_("adding manifests\n")) pr.step = _('manifests') pr.count = 1 pr.total = changesets # manifests <= changesets # no need to check for empty manifest group here: # if the result of the merge of 1 and 2 is the same in 3 and 4, # no new manifest will be created and the manifest group will # be empty during the pull source.manifestheader() repo.manifest.addgroup(source, revmap, trp) repo.ui.progress(_('manifests'), None) needfiles = {} if repo.ui.configbool('server', 'validate', default=False): # validate incoming csets have their manifests for cset in xrange(clstart, clend): mfest = repo.changelog.read(repo.changelog.node(cset))[0] mfest = repo.manifest.readdelta(mfest) # store file nodes we must see for f, n in mfest.iteritems(): needfiles.setdefault(f, set()).add(n) # process the files repo.ui.status(_("adding file changes\n")) pr.step = _('files') pr.count = 1 pr.total = efiles source.callback = None newrevs, newfiles = addchangegroupfiles(repo, source, revmap, trp, pr, needfiles) revisions += newrevs files += newfiles dh = 0 if oldheads: heads = cl.heads() dh = len(heads) - len(oldheads) for h in heads: if h not in oldheads and repo[h].closesbranch(): dh -= 1 htext = "" if dh: htext = _(" (%+d heads)") % dh repo.ui.status(_("added %d changesets" " with %d changes to %d files%s\n") % (changesets, revisions, files, htext)) repo.invalidatevolatilesets() if changesets > 0: p = lambda: cl.writepending() and repo.root or "" if 'node' not in tr.hookargs: tr.hookargs['node'] = hex(cl.node(clstart)) repo.hook('pretxnchangegroup', throw=True, source=srctype, url=url, pending=p, **tr.hookargs) added = [cl.node(r) for r in xrange(clstart, clend)] publishing = repo.ui.configbool('phases', 'publish', True) if srctype in ('push', 'serve'): # Old servers can not push the boundary themselves. # New servers won't push the boundary if changeset already # exists locally as secret # # We should not use added here but the list of all change in # the bundle if publishing: phases.advanceboundary(repo, phases.public, srccontent) else: phases.advanceboundary(repo, phases.draft, srccontent) phases.retractboundary(repo, phases.draft, added) elif srctype != 'strip': # publishing only alter behavior during push # # strip should not touch boundary at all phases.retractboundary(repo, phases.draft, added) # make changelog see real files again cl.finalize(trp) tr.close() if changesets > 0: if srctype != 'strip': # During strip, branchcache is invalid but coming call to # `destroyed` will repair it. # In other case we can safely update cache on disk. branchmap.updatecache(repo.filtered('served')) def runhooks(): # These hooks run when the lock releases, not when the # transaction closes. So it's possible for the changelog # to have changed since we last saw it. if clstart >= len(repo): return # forcefully update the on-disk branch cache repo.ui.debug("updating the branch cache\n") repo.hook("changegroup", source=srctype, url=url, **tr.hookargs) for n in added: repo.hook("incoming", node=hex(n), source=srctype, url=url) newheads = [h for h in repo.heads() if h not in oldheads] repo.ui.log("incoming", "%s incoming changes - new heads: %s\n", len(added), ', '.join([hex(c[:6]) for c in newheads])) repo._afterlock(runhooks) finally: tr.release() # never return 0 here: if dh < 0: return dh - 1 else: return dh + 1
# this only happens with Python 2.3, later versions raise URLError raise util.Abort(_('http error, possibly caused by proxy setting')) # record the url we got redirected to resp_url = resp.geturl() if resp_url.endswith(qs): resp_url = resp_url[:-len(qs)] if self._url.rstrip('/') != resp_url.rstrip('/'): if not self.ui.quiet: self.ui.warn(_('real URL is %s\n') % resp_url) self._url = resp_url try: proto = resp.getheader('content-type') except AttributeError: proto = resp.headers.get('content-type', '') safeurl = util.hidepassword(self._url) if proto.startswith('application/hg-error'): raise error.OutOfBandError(resp.read()) # accept old "text/plain" and "application/hg-changegroup" for now if not (proto.startswith('application/mercurial-') or (proto.startswith('text/plain') and not resp.headers.get('content-length')) or proto.startswith('application/hg-changegroup')): self.ui.debug("requested URL: '%s'\n" % util.hidepassword(cu)) raise error.RepoError( _("'%s' does not appear to be an hg repository:\n" "---%%<--- (%s)\n%s\n---%%<---\n") % (safeurl, proto or 'no content-type', resp.read(1024))) if proto.startswith('application/mercurial-'): try:
def handleremotechangegroup(op, inpart): """apply a bundle10 on the repo, given an url and validation information All the information about the remote bundle to import are given as parameters. The parameters include: - url: the url to the bundle10. - size: the bundle10 file size. It is used to validate what was retrieved by the client matches the server knowledge about the bundle. - digests: a space separated list of the digest types provided as parameters. - digest:<digest-type>: the hexadecimal representation of the digest with that name. Like the size, it is used to validate what was retrieved by the client matches what the server knows about the bundle. When multiple digest types are given, all of them are checked. """ try: raw_url = inpart.params['url'] except KeyError: raise util.Abort(_('remote-changegroup: missing "%s" param') % 'url') parsed_url = util.url(raw_url) if parsed_url.scheme not in capabilities['b2x:remote-changegroup']: raise util.Abort(_('remote-changegroup does not support %s urls') % parsed_url.scheme) try: size = int(inpart.params['size']) except ValueError: raise util.Abort(_('remote-changegroup: invalid value for param "%s"') % 'size') except KeyError: raise util.Abort(_('remote-changegroup: missing "%s" param') % 'size') digests = {} for typ in inpart.params.get('digests', '').split(): param = 'digest:%s' % typ try: value = inpart.params[param] except KeyError: raise util.Abort(_('remote-changegroup: missing "%s" param') % param) digests[typ] = value real_part = util.digestchecker(url.open(op.ui, raw_url), size, digests) # Make sure we trigger a transaction creation # # The addchangegroup function will get a transaction object by itself, but # we need to make sure we trigger the creation of a transaction object used # for the whole processing scope. op.gettransaction() import exchange cg = exchange.readbundle(op.repo.ui, real_part, raw_url) if not isinstance(cg, changegroup.cg1unpacker): raise util.Abort(_('%s: not a bundle version 1.0') % util.hidepassword(raw_url)) ret = changegroup.addchangegroup(op.repo, cg, 'bundle2', 'bundle2') op.records.add('changegroup', {'return': ret}) if op.reply is not None: # This is definitly not the final form of this # return. But one need to start somewhere. part = op.reply.newpart('b2x:reply:changegroup') part.addparam('in-reply-to', str(inpart.id), mandatory=False) part.addparam('return', '%i' % ret, mandatory=False) try: real_part.validate() except util.Abort, e: raise util.Abort(_('bundle at %s is corrupted:\n%s') % (util.hidepassword(raw_url), str(e)))
def addchangegroup(repo, source, srctype, url, emptyok=False, targetphase=phases.draft, expectedtotal=None): """Add the changegroup returned by source.read() to this repo. srctype is a string like 'push', 'pull', or 'unbundle'. url is the URL of the repo where this changegroup is coming from. Return an integer summarizing the change to this repo: - nothing changed or no source: 0 - more heads than before: 1+added heads (2..n) - fewer heads than before: -1-removed heads (-2..-n) - number of heads stays the same: 1 """ repo = repo.unfiltered() def csmap(x): repo.ui.debug("add changeset %s\n" % short(x)) return len(cl) def revmap(x): return cl.rev(x) if not source: return 0 changesets = files = revisions = 0 efiles = set() tr = repo.transaction("\n".join([srctype, util.hidepassword(url)])) # The transaction could have been created before and already carries source # information. In this case we use the top level data. We overwrite the # argument because we need to use the top level value (if they exist) in # this function. srctype = tr.hookargs.setdefault('source', srctype) url = tr.hookargs.setdefault('url', url) # write changelog data to temp files so concurrent readers will not see # inconsistent view cl = repo.changelog cl.delayupdate(tr) oldheads = cl.heads() try: repo.hook('prechangegroup', throw=True, **tr.hookargs) trp = weakref.proxy(tr) # pull off the changeset group repo.ui.status(_("adding changesets\n")) clstart = len(cl) class prog(object): def __init__(self, step, total): self._step = step self._total = total self._count = 1 def __call__(self): repo.ui.progress(self._step, self._count, unit=_('chunks'), total=self._total) self._count += 1 source.callback = prog(_('changesets'), expectedtotal) source.changelogheader() srccontent = cl.addgroup(source, csmap, trp) if not (srccontent or emptyok): raise util.Abort(_("received changelog group is empty")) clend = len(cl) changesets = clend - clstart for c in xrange(clstart, clend): efiles.update(repo[c].files()) efiles = len(efiles) repo.ui.progress(_('changesets'), None) # pull off the manifest group repo.ui.status(_("adding manifests\n")) # manifests <= changesets source.callback = prog(_('manifests'), changesets) # no need to check for empty manifest group here: # if the result of the merge of 1 and 2 is the same in 3 and 4, # no new manifest will be created and the manifest group will # be empty during the pull source.manifestheader() repo.manifest.addgroup(source, revmap, trp) repo.ui.progress(_('manifests'), None) needfiles = {} if repo.ui.configbool('server', 'validate', default=False): # validate incoming csets have their manifests for cset in xrange(clstart, clend): mfnode = repo.changelog.read(repo.changelog.node(cset))[0] mfest = repo.manifest.readdelta(mfnode) # store file nodes we must see for f, n in mfest.iteritems(): needfiles.setdefault(f, set()).add(n) # process the files repo.ui.status(_("adding file changes\n")) source.callback = None pr = prog(_('files'), efiles) newrevs, newfiles = addchangegroupfiles(repo, source, revmap, trp, pr, needfiles) revisions += newrevs files += newfiles dh = 0 if oldheads: heads = cl.heads() dh = len(heads) - len(oldheads) for h in heads: if h not in oldheads and repo[h].closesbranch(): dh -= 1 htext = "" if dh: htext = _(" (%+d heads)") % dh repo.ui.status( _("added %d changesets" " with %d changes to %d files%s\n") % (changesets, revisions, files, htext)) repo.invalidatevolatilesets() if changesets > 0: p = lambda: tr.writepending() and repo.root or "" if 'node' not in tr.hookargs: tr.hookargs['node'] = hex(cl.node(clstart)) hookargs = dict(tr.hookargs) else: hookargs = dict(tr.hookargs) hookargs['node'] = hex(cl.node(clstart)) repo.hook('pretxnchangegroup', throw=True, pending=p, **hookargs) added = [cl.node(r) for r in xrange(clstart, clend)] publishing = repo.publishing() if srctype in ('push', 'serve'): # Old servers can not push the boundary themselves. # New servers won't push the boundary if changeset already # exists locally as secret # # We should not use added here but the list of all change in # the bundle if publishing: phases.advanceboundary(repo, tr, phases.public, srccontent) else: # Those changesets have been pushed from the outside, their # phases are going to be pushed alongside. Therefor # `targetphase` is ignored. phases.advanceboundary(repo, tr, phases.draft, srccontent) phases.retractboundary(repo, tr, phases.draft, added) elif srctype != 'strip': # publishing only alter behavior during push # # strip should not touch boundary at all phases.retractboundary(repo, tr, targetphase, added) if changesets > 0: if srctype != 'strip': # During strip, branchcache is invalid but coming call to # `destroyed` will repair it. # In other case we can safely update cache on disk. branchmap.updatecache(repo.filtered('served')) def runhooks(): # These hooks run when the lock releases, not when the # transaction closes. So it's possible for the changelog # to have changed since we last saw it. if clstart >= len(repo): return # forcefully update the on-disk branch cache repo.ui.debug("updating the branch cache\n") repo.hook("changegroup", **hookargs) for n in added: args = hookargs.copy() args['node'] = hex(n) repo.hook("incoming", **args) newheads = [h for h in repo.heads() if h not in oldheads] repo.ui.log("incoming", "%s incoming changes - new heads: %s\n", len(added), ', '.join([hex(c[:6]) for c in newheads])) tr.addpostclose('changegroup-runhooks-%020i' % clstart, lambda tr: repo._afterlock(runhooks)) tr.close() finally: tr.release() repo.ui.flush() # never return 0 here: if dh < 0: return dh - 1 else: return dh + 1
def _callstream(self, cmd, **args): if cmd == 'pushkey': args['data'] = '' data = args.pop('data', None) size = 0 if util.safehasattr(data, 'length'): size = data.length elif data is not None: size = len(data) headers = args.pop('headers', {}) if data is not None and 'Content-Type' not in headers: headers['Content-Type'] = 'application/mercurial-0.1' if size and self.ui.configbool('ui', 'usehttp2', False): headers['Expect'] = '100-Continue' headers['X-HgHttp2'] = '1' self.ui.debug("sending %s command\n" % cmd) q = [('cmd', cmd)] headersize = 0 if len(args) > 0: httpheader = self.capable('httpheader') if httpheader: headersize = int(httpheader.split(',')[0]) if headersize > 0: # The headers can typically carry more data than the URL. encargs = urllib.urlencode(sorted(args.items())) headerfmt = 'X-HgArg-%s' contentlen = headersize - len(headerfmt % '000' + ': \r\n') headernum = 0 for i in xrange(0, len(encargs), contentlen): headernum += 1 header = headerfmt % str(headernum) headers[header] = encargs[i:i + contentlen] varyheaders = [headerfmt % str(h) for h in range(1, headernum + 1)] headers['Vary'] = ','.join(varyheaders) else: q += sorted(args.items()) qs = '?%s' % urllib.urlencode(q) cu = "%s%s" % (self._url, qs) req = self.requestbuilder(cu, data, headers) if data is not None: self.ui.debug("sending %s bytes\n" % size) req.add_unredirected_header('Content-Length', '%d' % size) try: resp = self.urlopener.open(req) except urllib2.HTTPError as inst: if inst.code == 401: raise util.Abort(_('authorization failed')) raise except httplib.HTTPException as inst: self.ui.debug('http error while sending %s command\n' % cmd) self.ui.traceback() raise IOError(None, inst) except IndexError: # this only happens with Python 2.3, later versions raise URLError raise util.Abort(_('http error, possibly caused by proxy setting')) # record the url we got redirected to resp_url = resp.geturl() if resp_url.endswith(qs): resp_url = resp_url[:-len(qs)] if self._url.rstrip('/') != resp_url.rstrip('/'): if not self.ui.quiet: self.ui.warn(_('real URL is %s\n') % resp_url) self._url = resp_url try: proto = resp.getheader('content-type') except AttributeError: proto = resp.headers.get('content-type', '') safeurl = util.hidepassword(self._url) if proto.startswith('application/hg-error'): raise error.OutOfBandError(resp.read()) # accept old "text/plain" and "application/hg-changegroup" for now if not (proto.startswith('application/mercurial-') or (proto.startswith('text/plain') and not resp.headers.get('content-length')) or proto.startswith('application/hg-changegroup')): self.ui.debug("requested URL: '%s'\n" % util.hidepassword(cu)) raise error.RepoError( _("'%s' does not appear to be an hg repository:\n" "---%%<--- (%s)\n%s\n---%%<---\n") % (safeurl, proto or 'no content-type', resp.read(1024))) if proto.startswith('application/mercurial-'): try: version = proto.split('-', 1)[1] version_info = tuple([int(n) for n in version.split('.')]) except ValueError: raise error.RepoError(_("'%s' sent a broken Content-Type " "header (%s)") % (safeurl, proto)) if version_info > (0, 1): raise error.RepoError(_("'%s' uses newer protocol %s") % (safeurl, version)) return resp