diff --git a/hgext/infinitepush/__init__.py b/hgext/infinitepush/__init__.py --- a/hgext/infinitepush/__init__.py +++ b/hgext/infinitepush/__init__.py @@ -704,16 +704,19 @@ if scratchbookmarks: other = hg.peer(repo, opts, source) - fetchedbookmarks = other.listkeyspatterns( - b'bookmarks', patterns=scratchbookmarks - ) - for bookmark in scratchbookmarks: - if bookmark not in fetchedbookmarks: - raise error.Abort( - b'remote bookmark %s not found!' % bookmark - ) - scratchbookmarks[bookmark] = fetchedbookmarks[bookmark] - revs.append(fetchedbookmarks[bookmark]) + try: + fetchedbookmarks = other.listkeyspatterns( + b'bookmarks', patterns=scratchbookmarks + ) + for bookmark in scratchbookmarks: + if bookmark not in fetchedbookmarks: + raise error.Abort( + b'remote bookmark %s not found!' % bookmark + ) + scratchbookmarks[bookmark] = fetchedbookmarks[bookmark] + revs.append(fetchedbookmarks[bookmark]) + finally: + other.close() opts[b'bookmark'] = bookmarks opts[b'rev'] = revs @@ -848,10 +851,13 @@ if common.isremotebooksenabled(ui): if bookmark and scratchpush: other = hg.peer(repo, opts, destpath) - fetchedbookmarks = other.listkeyspatterns( - b'bookmarks', patterns=[bookmark] - ) - remotescratchbookmarks.update(fetchedbookmarks) + try: + fetchedbookmarks = other.listkeyspatterns( + b'bookmarks', patterns=[bookmark] + ) + remotescratchbookmarks.update(fetchedbookmarks) + finally: + other.close() _saveremotebookmarks(repo, remotescratchbookmarks, destpath) if oldphasemove: exchange._localphasemove = oldphasemove diff --git a/hgext/narrow/narrowcommands.py b/hgext/narrow/narrowcommands.py --- a/hgext/narrow/narrowcommands.py +++ b/hgext/narrow/narrowcommands.py @@ -595,77 +595,83 @@ ui.status(_(b'comparing with %s\n') % util.hidepassword(url)) remote = hg.peer(repo, opts, url) - # check narrow support before doing anything if widening needs to be - # performed. In future we should also abort if client is ellipses and - # server does not support ellipses - if widening and wireprototypes.NARROWCAP not in remote.capabilities(): - raise error.Abort(_(b"server does not support narrow clones")) - - commoninc = discovery.findcommonincoming(repo, remote) - - if autoremoveincludes: - outgoing = discovery.findcommonoutgoing( - repo, remote, commoninc=commoninc - ) - ui.status(_(b'looking for unused includes to remove\n')) - localfiles = set() - for n in itertools.chain(outgoing.missing, outgoing.excluded): - localfiles.update(repo[n].files()) - suggestedremovals = [] - for include in sorted(oldincludes): - match = narrowspec.match(repo.root, [include], oldexcludes) - if not any(match(f) for f in localfiles): - suggestedremovals.append(include) - if suggestedremovals: - for s in suggestedremovals: - ui.status(b'%s\n' % s) - if ( - ui.promptchoice( - _( - b'remove these unused includes (yn)?' - b'$$ &Yes $$ &No' + try: + # check narrow support before doing anything if widening needs to be + # performed. In future we should also abort if client is ellipses and + # server does not support ellipses + if ( + widening + and wireprototypes.NARROWCAP not in remote.capabilities() + ): + raise error.Abort(_(b"server does not support narrow clones")) + + commoninc = discovery.findcommonincoming(repo, remote) + + if autoremoveincludes: + outgoing = discovery.findcommonoutgoing( + repo, remote, commoninc=commoninc + ) + ui.status(_(b'looking for unused includes to remove\n')) + localfiles = set() + for n in itertools.chain(outgoing.missing, outgoing.excluded): + localfiles.update(repo[n].files()) + suggestedremovals = [] + for include in sorted(oldincludes): + match = narrowspec.match(repo.root, [include], oldexcludes) + if not any(match(f) for f in localfiles): + suggestedremovals.append(include) + if suggestedremovals: + for s in suggestedremovals: + ui.status(b'%s\n' % s) + if ( + ui.promptchoice( + _( + b'remove these unused includes (yn)?' + b'$$ &Yes $$ &No' + ) ) - ) - == 0 - ): - removedincludes.update(suggestedremovals) - narrowing = True - else: - ui.status(_(b'found no unused includes\n')) - - if narrowing: - newincludes = oldincludes - removedincludes - newexcludes = oldexcludes | addedexcludes - _narrow( - ui, - repo, - remote, - commoninc, - oldincludes, - oldexcludes, - newincludes, - newexcludes, - opts[b'force_delete_local_changes'], - opts[b'backup'], - ) - # _narrow() updated the narrowspec and _widen() below needs to - # use the updated values as its base (otherwise removed includes - # and addedexcludes will be lost in the resulting narrowspec) - oldincludes = newincludes - oldexcludes = newexcludes - - if widening: - newincludes = oldincludes | addedincludes - newexcludes = oldexcludes - removedexcludes - _widen( - ui, - repo, - remote, - commoninc, - oldincludes, - oldexcludes, - newincludes, - newexcludes, - ) + == 0 + ): + removedincludes.update(suggestedremovals) + narrowing = True + else: + ui.status(_(b'found no unused includes\n')) + + if narrowing: + newincludes = oldincludes - removedincludes + newexcludes = oldexcludes | addedexcludes + _narrow( + ui, + repo, + remote, + commoninc, + oldincludes, + oldexcludes, + newincludes, + newexcludes, + opts[b'force_delete_local_changes'], + opts[b'backup'], + ) + # _narrow() updated the narrowspec and _widen() below needs to + # use the updated values as its base (otherwise removed includes + # and addedexcludes will be lost in the resulting narrowspec) + oldincludes = newincludes + oldexcludes = newexcludes + + if widening: + newincludes = oldincludes | addedincludes + newexcludes = oldexcludes - removedexcludes + _widen( + ui, + repo, + remote, + commoninc, + oldincludes, + oldexcludes, + newincludes, + newexcludes, + ) + finally: + remote.close() return 0 diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -3820,132 +3820,138 @@ output = [] revs = [] - if source: - source, branches = hg.parseurl(ui.expandpath(source)) - peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo - repo = peer.local() - revs, checkout = hg.addbranchrevs(repo, peer, branches, None) - - fm = ui.formatter(b'identify', opts) - fm.startitem() - - if not repo: - if num or branch or tags: - raise error.InputError( - _(b"can't query remote revision number, branch, or tags") - ) - if not rev and revs: - rev = revs[0] - if not rev: - rev = b"tip" - - remoterev = peer.lookup(rev) - hexrev = fm.hexfunc(remoterev) - if default or id: - output = [hexrev] - fm.data(id=hexrev) - - @util.cachefunc - def getbms(): - bms = [] - - if b'bookmarks' in peer.listkeys(b'namespaces'): - hexremoterev = hex(remoterev) - bms = [ - bm - for bm, bmr in pycompat.iteritems( - peer.listkeys(b'bookmarks') + peer = None + try: + if source: + source, branches = hg.parseurl(ui.expandpath(source)) + # only pass ui when no repo + peer = hg.peer(repo or ui, opts, source) + repo = peer.local() + revs, checkout = hg.addbranchrevs(repo, peer, branches, None) + + fm = ui.formatter(b'identify', opts) + fm.startitem() + + if not repo: + if num or branch or tags: + raise error.InputError( + _(b"can't query remote revision number, branch, or tags") + ) + if not rev and revs: + rev = revs[0] + if not rev: + rev = b"tip" + + remoterev = peer.lookup(rev) + hexrev = fm.hexfunc(remoterev) + if default or id: + output = [hexrev] + fm.data(id=hexrev) + + @util.cachefunc + def getbms(): + bms = [] + + if b'bookmarks' in peer.listkeys(b'namespaces'): + hexremoterev = hex(remoterev) + bms = [ + bm + for bm, bmr in pycompat.iteritems( + peer.listkeys(b'bookmarks') + ) + if bmr == hexremoterev + ] + + return sorted(bms) + + if fm.isplain(): + if bookmarks: + output.extend(getbms()) + elif default and not ui.quiet: + # multiple bookmarks for a single parent separated by '/' + bm = b'/'.join(getbms()) + if bm: + output.append(bm) + else: + fm.data(node=hex(remoterev)) + if bookmarks or b'bookmarks' in fm.datahint(): + fm.data(bookmarks=fm.formatlist(getbms(), name=b'bookmark')) + else: + if rev: + repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn') + ctx = scmutil.revsingle(repo, rev, None) + + if ctx.rev() is None: + ctx = repo[None] + parents = ctx.parents() + taglist = [] + for p in parents: + taglist.extend(p.tags()) + + dirty = b"" + if ctx.dirty(missing=True, merge=False, branch=False): + dirty = b'+' + fm.data(dirty=dirty) + + hexoutput = [fm.hexfunc(p.node()) for p in parents] + if default or id: + output = [b"%s%s" % (b'+'.join(hexoutput), dirty)] + fm.data(id=b"%s%s" % (b'+'.join(hexoutput), dirty)) + + if num: + numoutput = [b"%d" % p.rev() for p in parents] + output.append(b"%s%s" % (b'+'.join(numoutput), dirty)) + + fm.data( + parents=fm.formatlist( + [fm.hexfunc(p.node()) for p in parents], name=b'node' ) - if bmr == hexremoterev - ] - - return sorted(bms) - - if fm.isplain(): - if bookmarks: - output.extend(getbms()) - elif default and not ui.quiet: + ) + else: + hexoutput = fm.hexfunc(ctx.node()) + if default or id: + output = [hexoutput] + fm.data(id=hexoutput) + + if num: + output.append(pycompat.bytestr(ctx.rev())) + taglist = ctx.tags() + + if default and not ui.quiet: + b = ctx.branch() + if b != b'default': + output.append(b"(%s)" % b) + + # multiple tags for a single parent separated by '/' + t = b'/'.join(taglist) + if t: + output.append(t) + # multiple bookmarks for a single parent separated by '/' - bm = b'/'.join(getbms()) + bm = b'/'.join(ctx.bookmarks()) if bm: output.append(bm) - else: - fm.data(node=hex(remoterev)) - if bookmarks or b'bookmarks' in fm.datahint(): - fm.data(bookmarks=fm.formatlist(getbms(), name=b'bookmark')) - else: - if rev: - repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn') - ctx = scmutil.revsingle(repo, rev, None) - - if ctx.rev() is None: - ctx = repo[None] - parents = ctx.parents() - taglist = [] - for p in parents: - taglist.extend(p.tags()) - - dirty = b"" - if ctx.dirty(missing=True, merge=False, branch=False): - dirty = b'+' - fm.data(dirty=dirty) - - hexoutput = [fm.hexfunc(p.node()) for p in parents] - if default or id: - output = [b"%s%s" % (b'+'.join(hexoutput), dirty)] - fm.data(id=b"%s%s" % (b'+'.join(hexoutput), dirty)) - - if num: - numoutput = [b"%d" % p.rev() for p in parents] - output.append(b"%s%s" % (b'+'.join(numoutput), dirty)) - - fm.data( - parents=fm.formatlist( - [fm.hexfunc(p.node()) for p in parents], name=b'node' - ) - ) - else: - hexoutput = fm.hexfunc(ctx.node()) - if default or id: - output = [hexoutput] - fm.data(id=hexoutput) - - if num: - output.append(pycompat.bytestr(ctx.rev())) - taglist = ctx.tags() - - if default and not ui.quiet: - b = ctx.branch() - if b != b'default': - output.append(b"(%s)" % b) - - # multiple tags for a single parent separated by '/' - t = b'/'.join(taglist) - if t: - output.append(t) - - # multiple bookmarks for a single parent separated by '/' - bm = b'/'.join(ctx.bookmarks()) - if bm: - output.append(bm) - else: - if branch: - output.append(ctx.branch()) - - if tags: - output.extend(taglist) - - if bookmarks: - output.extend(ctx.bookmarks()) - - fm.data(node=ctx.hex()) - fm.data(branch=ctx.branch()) - fm.data(tags=fm.formatlist(taglist, name=b'tag', sep=b':')) - fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark')) - fm.context(ctx=ctx) - - fm.plain(b"%s\n" % b' '.join(output)) - fm.end() + else: + if branch: + output.append(ctx.branch()) + + if tags: + output.extend(taglist) + + if bookmarks: + output.extend(ctx.bookmarks()) + + fm.data(node=ctx.hex()) + fm.data(branch=ctx.branch()) + fm.data(tags=fm.formatlist(taglist, name=b'tag', sep=b':')) + fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark')) + fm.context(ctx=ctx) + + fm.plain(b"%s\n" % b' '.join(output)) + fm.end() + finally: + if peer: + peer.close() @command( @@ -4291,12 +4297,15 @@ ui.expandpath(source), opts.get(b'branch') ) other = hg.peer(repo, opts, source) - if b'bookmarks' not in other.listkeys(b'namespaces'): - ui.warn(_(b"remote doesn't support bookmarks\n")) - return 0 - ui.pager(b'incoming') - ui.status(_(b'comparing with %s\n') % util.hidepassword(source)) - return bookmarks.incoming(ui, repo, other) + try: + if b'bookmarks' not in other.listkeys(b'namespaces'): + ui.warn(_(b"remote doesn't support bookmarks\n")) + return 0 + ui.pager(b'incoming') + ui.status(_(b'comparing with %s\n') % util.hidepassword(source)) + return bookmarks.incoming(ui, repo, other) + finally: + other.close() repo._subtoppath = ui.expandpath(source) try: @@ -4327,7 +4336,8 @@ Returns 0 on success. """ opts = pycompat.byteskwargs(opts) - hg.peer(ui, opts, ui.expandpath(dest), create=True) + peer = hg.peer(ui, opts, ui.expandpath(dest), create=True) + peer.close() @command( @@ -4963,12 +4973,15 @@ if opts.get(b'bookmarks'): dest = path.pushloc or path.loc other = hg.peer(repo, opts, dest) - if b'bookmarks' not in other.listkeys(b'namespaces'): - ui.warn(_(b"remote doesn't support bookmarks\n")) - return 0 - ui.status(_(b'comparing with %s\n') % util.hidepassword(dest)) - ui.pager(b'outgoing') - return bookmarks.outgoing(ui, repo, other) + try: + if b'bookmarks' not in other.listkeys(b'namespaces'): + ui.warn(_(b"remote doesn't support bookmarks\n")) + return 0 + ui.status(_(b'comparing with %s\n') % util.hidepassword(dest)) + ui.pager(b'outgoing') + return bookmarks.outgoing(ui, repo, other) + finally: + other.close() repo._subtoppath = path.pushloc or path.loc try: @@ -5679,63 +5692,67 @@ revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get(b'rev')) other = hg.peer(repo, opts, dest) - if revs: - revs = [repo[r].node() for r in scmutil.revrange(repo, revs)] - if not revs: + try: + if revs: + revs = [repo[r].node() for r in scmutil.revrange(repo, revs)] + if not revs: + raise error.InputError( + _(b"specified revisions evaluate to an empty set"), + hint=_(b"use different revision arguments"), + ) + elif path.pushrev: + # It doesn't make any sense to specify ancestor revisions. So limit + # to DAG heads to make discovery simpler. + expr = revsetlang.formatspec(b'heads(%r)', path.pushrev) + revs = scmutil.revrange(repo, [expr]) + revs = [repo[rev].node() for rev in revs] + if not revs: + raise error.InputError( + _(b'default push revset for path evaluates to an empty set') + ) + elif ui.configbool(b'commands', b'push.require-revs'): raise error.InputError( - _(b"specified revisions evaluate to an empty set"), - hint=_(b"use different revision arguments"), + _(b'no revisions specified to push'), + hint=_(b'did you mean "hg push -r ."?'), ) - elif path.pushrev: - # It doesn't make any sense to specify ancestor revisions. So limit - # to DAG heads to make discovery simpler. - expr = revsetlang.formatspec(b'heads(%r)', path.pushrev) - revs = scmutil.revrange(repo, [expr]) - revs = [repo[rev].node() for rev in revs] - if not revs: - raise error.InputError( - _(b'default push revset for path evaluates to an empty set') - ) - elif ui.configbool(b'commands', b'push.require-revs'): - raise error.InputError( - _(b'no revisions specified to push'), - hint=_(b'did you mean "hg push -r ."?'), + + repo._subtoppath = dest + try: + # push subrepos depth-first for coherent ordering + c = repo[b'.'] + subs = c.substate # only repos that are committed + for s in sorted(subs): + result = c.sub(s).push(opts) + if result == 0: + return not result + finally: + del repo._subtoppath + + opargs = dict( + opts.get(b'opargs', {}) + ) # copy opargs since we may mutate it + opargs.setdefault(b'pushvars', []).extend(opts.get(b'pushvars', [])) + + pushop = exchange.push( + repo, + other, + opts.get(b'force'), + revs=revs, + newbranch=opts.get(b'new_branch'), + bookmarks=opts.get(b'bookmark', ()), + publish=opts.get(b'publish'), + opargs=opargs, ) - repo._subtoppath = dest - try: - # push subrepos depth-first for coherent ordering - c = repo[b'.'] - subs = c.substate # only repos that are committed - for s in sorted(subs): - result = c.sub(s).push(opts) - if result == 0: - return not result + result = not pushop.cgresult + + if pushop.bkresult is not None: + if pushop.bkresult == 2: + result = 2 + elif not result and pushop.bkresult: + result = 2 finally: - del repo._subtoppath - - opargs = dict(opts.get(b'opargs', {})) # copy opargs since we may mutate it - opargs.setdefault(b'pushvars', []).extend(opts.get(b'pushvars', [])) - - pushop = exchange.push( - repo, - other, - opts.get(b'force'), - revs=revs, - newbranch=opts.get(b'new_branch'), - bookmarks=opts.get(b'bookmark', ()), - publish=opts.get(b'publish'), - opargs=opargs, - ) - - result = not pushop.cgresult - - if pushop.bkresult is not None: - if pushop.bkresult == 2: - result = 2 - elif not result and pushop.bkresult: - result = 2 - + other.close() return result diff --git a/mercurial/debugcommands.py b/mercurial/debugcommands.py --- a/mercurial/debugcommands.py +++ b/mercurial/debugcommands.py @@ -471,17 +471,20 @@ """lists the capabilities of a remote peer""" opts = pycompat.byteskwargs(opts) peer = hg.peer(ui, opts, path) - caps = peer.capabilities() - ui.writenoi18n(b'Main capabilities:\n') - for c in sorted(caps): - ui.write(b' %s\n' % c) - b2caps = bundle2.bundle2caps(peer) - if b2caps: - ui.writenoi18n(b'Bundle2 capabilities:\n') - for key, values in sorted(pycompat.iteritems(b2caps)): - ui.write(b' %s\n' % key) - for v in values: - ui.write(b' %s\n' % v) + try: + caps = peer.capabilities() + ui.writenoi18n(b'Main capabilities:\n') + for c in sorted(caps): + ui.write(b' %s\n' % c) + b2caps = bundle2.bundle2caps(peer) + if b2caps: + ui.writenoi18n(b'Bundle2 capabilities:\n') + for key, values in sorted(pycompat.iteritems(b2caps)): + ui.write(b' %s\n' % key) + for v in values: + ui.write(b' %s\n' % v) + finally: + peer.close() @command( @@ -2615,12 +2618,17 @@ with ui.configoverride(overrides): peer = hg.peer(ui, {}, path) - local = peer.local() is not None - canpush = peer.canpush() - - ui.write(_(b'url: %s\n') % peer.url()) - ui.write(_(b'local: %s\n') % (_(b'yes') if local else _(b'no'))) - ui.write(_(b'pushable: %s\n') % (_(b'yes') if canpush else _(b'no'))) + try: + local = peer.local() is not None + canpush = peer.canpush() + + ui.write(_(b'url: %s\n') % peer.url()) + ui.write(_(b'local: %s\n') % (_(b'yes') if local else _(b'no'))) + ui.write( + _(b'pushable: %s\n') % (_(b'yes') if canpush else _(b'no')) + ) + finally: + peer.close() @command( @@ -2723,26 +2731,30 @@ """ target = hg.peer(ui, {}, repopath) - if keyinfo: - key, old, new = keyinfo - with target.commandexecutor() as e: - r = e.callcommand( - b'pushkey', - { - b'namespace': namespace, - b'key': key, - b'old': old, - b'new': new, - }, - ).result() - - ui.status(pycompat.bytestr(r) + b'\n') - return not r - else: - for k, v in sorted(pycompat.iteritems(target.listkeys(namespace))): - ui.write( - b"%s\t%s\n" % (stringutil.escapestr(k), stringutil.escapestr(v)) - ) + try: + if keyinfo: + key, old, new = keyinfo + with target.commandexecutor() as e: + r = e.callcommand( + b'pushkey', + { + b'namespace': namespace, + b'key': key, + b'old': old, + b'new': new, + }, + ).result() + + ui.status(pycompat.bytestr(r) + b'\n') + return not r + else: + for k, v in sorted(pycompat.iteritems(target.listkeys(namespace))): + ui.write( + b"%s\t%s\n" + % (stringutil.escapestr(k), stringutil.escapestr(v)) + ) + finally: + target.close() @command(b'debugpvec', [], _(b'A B')) @@ -4092,19 +4104,22 @@ def debugwireargs(ui, repopath, *vals, **opts): opts = pycompat.byteskwargs(opts) repo = hg.peer(ui, opts, repopath) - for opt in cmdutil.remoteopts: - del opts[opt[1]] - args = {} - for k, v in pycompat.iteritems(opts): - if v: - args[k] = v - args = pycompat.strkwargs(args) - # run twice to check that we don't mess up the stream for the next command - res1 = repo.debugwireargs(*vals, **args) - res2 = repo.debugwireargs(*vals, **args) - ui.write(b"%s\n" % res1) - if res1 != res2: - ui.warn(b"%s\n" % res2) + try: + for opt in cmdutil.remoteopts: + del opts[opt[1]] + args = {} + for k, v in pycompat.iteritems(opts): + if v: + args[k] = v + args = pycompat.strkwargs(args) + # run twice to check that we don't mess up the stream for the next command + res1 = repo.debugwireargs(*vals, **args) + res2 = repo.debugwireargs(*vals, **args) + ui.write(b"%s\n" % res1) + if res1 != res2: + ui.warn(b"%s\n" % res2) + finally: + repo.close() def _parsewirelangblocks(fh): diff --git a/mercurial/hg.py b/mercurial/hg.py --- a/mercurial/hg.py +++ b/mercurial/hg.py @@ -678,140 +678,148 @@ srcpeer = source.peer() # in case we were called with a localrepo branches = (None, branch or []) origsource = source = srcpeer.url() - revs, checkout = addbranchrevs(srcpeer, srcpeer, branches, revs) - - if dest is None: - dest = defaultdest(source) - if dest: - ui.status(_(b"destination directory: %s\n") % dest) - else: - dest = ui.expandpath(dest) - - dest = util.urllocalpath(dest) - source = util.urllocalpath(source) - - if not dest: - raise error.InputError(_(b"empty destination path is not valid")) - - destvfs = vfsmod.vfs(dest, expandpath=True) - if destvfs.lexists(): - if not destvfs.isdir(): - raise error.InputError(_(b"destination '%s' already exists") % dest) - elif destvfs.listdir(): - raise error.InputError(_(b"destination '%s' is not empty") % dest) - - createopts = {} - narrow = False - - if storeincludepats is not None: - narrowspec.validatepatterns(storeincludepats) - narrow = True - - if storeexcludepats is not None: - narrowspec.validatepatterns(storeexcludepats) - narrow = True - - if narrow: - # Include everything by default if only exclusion patterns defined. - if storeexcludepats and not storeincludepats: - storeincludepats = {b'path:.'} - - createopts[b'narrowfiles'] = True - - if depth: - createopts[b'shallowfilestore'] = True - - if srcpeer.capable(b'lfs-serve'): - # Repository creation honors the config if it disabled the extension, so - # we can't just announce that lfs will be enabled. This check avoids - # saying that lfs will be enabled, and then saying it's an unknown - # feature. The lfs creation option is set in either case so that a - # requirement is added. If the extension is explicitly disabled but the - # requirement is set, the clone aborts early, before transferring any - # data. - createopts[b'lfs'] = True - - if extensions.disabled_help(b'lfs'): - ui.status( - _( - b'(remote is using large file support (lfs), but it is ' - b'explicitly disabled in the local configuration)\n' + srclock = destlock = cleandir = None + destpeer = None + try: + revs, checkout = addbranchrevs(srcpeer, srcpeer, branches, revs) + + if dest is None: + dest = defaultdest(source) + if dest: + ui.status(_(b"destination directory: %s\n") % dest) + else: + dest = ui.expandpath(dest) + + dest = util.urllocalpath(dest) + source = util.urllocalpath(source) + + if not dest: + raise error.InputError(_(b"empty destination path is not valid")) + + destvfs = vfsmod.vfs(dest, expandpath=True) + if destvfs.lexists(): + if not destvfs.isdir(): + raise error.InputError( + _(b"destination '%s' already exists") % dest ) - ) - else: - ui.status( - _( - b'(remote is using large file support (lfs); lfs will ' - b'be enabled for this repository)\n' + elif destvfs.listdir(): + raise error.InputError( + _(b"destination '%s' is not empty") % dest ) - ) - - shareopts = shareopts or {} - sharepool = shareopts.get(b'pool') - sharenamemode = shareopts.get(b'mode') - if sharepool and islocal(dest): - sharepath = None - if sharenamemode == b'identity': - # Resolve the name from the initial changeset in the remote - # repository. This returns nullid when the remote is empty. It - # raises RepoLookupError if revision 0 is filtered or otherwise - # not available. If we fail to resolve, sharing is not enabled. - try: - with srcpeer.commandexecutor() as e: - rootnode = e.callcommand( - b'lookup', - { - b'key': b'0', - }, - ).result() - - if rootnode != nullid: - sharepath = os.path.join(sharepool, hex(rootnode)) - else: + + createopts = {} + narrow = False + + if storeincludepats is not None: + narrowspec.validatepatterns(storeincludepats) + narrow = True + + if storeexcludepats is not None: + narrowspec.validatepatterns(storeexcludepats) + narrow = True + + if narrow: + # Include everything by default if only exclusion patterns defined. + if storeexcludepats and not storeincludepats: + storeincludepats = {b'path:.'} + + createopts[b'narrowfiles'] = True + + if depth: + createopts[b'shallowfilestore'] = True + + if srcpeer.capable(b'lfs-serve'): + # Repository creation honors the config if it disabled the extension, so + # we can't just announce that lfs will be enabled. This check avoids + # saying that lfs will be enabled, and then saying it's an unknown + # feature. The lfs creation option is set in either case so that a + # requirement is added. If the extension is explicitly disabled but the + # requirement is set, the clone aborts early, before transferring any + # data. + createopts[b'lfs'] = True + + if extensions.disabled_help(b'lfs'): + ui.status( + _( + b'(remote is using large file support (lfs), but it is ' + b'explicitly disabled in the local configuration)\n' + ) + ) + else: + ui.status( + _( + b'(remote is using large file support (lfs); lfs will ' + b'be enabled for this repository)\n' + ) + ) + + shareopts = shareopts or {} + sharepool = shareopts.get(b'pool') + sharenamemode = shareopts.get(b'mode') + if sharepool and islocal(dest): + sharepath = None + if sharenamemode == b'identity': + # Resolve the name from the initial changeset in the remote + # repository. This returns nullid when the remote is empty. It + # raises RepoLookupError if revision 0 is filtered or otherwise + # not available. If we fail to resolve, sharing is not enabled. + try: + with srcpeer.commandexecutor() as e: + rootnode = e.callcommand( + b'lookup', + { + b'key': b'0', + }, + ).result() + + if rootnode != nullid: + sharepath = os.path.join(sharepool, hex(rootnode)) + else: + ui.status( + _( + b'(not using pooled storage: ' + b'remote appears to be empty)\n' + ) + ) + except error.RepoLookupError: ui.status( _( b'(not using pooled storage: ' - b'remote appears to be empty)\n' + b'unable to resolve identity of remote)\n' ) ) - except error.RepoLookupError: + elif sharenamemode == b'remote': + sharepath = os.path.join( + sharepool, hex(hashutil.sha1(source).digest()) + ) + else: + raise error.Abort( + _(b'unknown share naming mode: %s') % sharenamemode + ) + + # TODO this is a somewhat arbitrary restriction. + if narrow: ui.status( - _( - b'(not using pooled storage: ' - b'unable to resolve identity of remote)\n' - ) + _(b'(pooled storage not supported for narrow clones)\n') ) - elif sharenamemode == b'remote': - sharepath = os.path.join( - sharepool, hex(hashutil.sha1(source).digest()) - ) - else: - raise error.Abort( - _(b'unknown share naming mode: %s') % sharenamemode - ) - - # TODO this is a somewhat arbitrary restriction. - if narrow: - ui.status(_(b'(pooled storage not supported for narrow clones)\n')) - sharepath = None - - if sharepath: - return clonewithshare( - ui, - peeropts, - sharepath, - source, - srcpeer, - dest, - pull=pull, - rev=revs, - update=update, - stream=stream, - ) - - srclock = destlock = cleandir = None - srcrepo = srcpeer.local() - try: + sharepath = None + + if sharepath: + return clonewithshare( + ui, + peeropts, + sharepath, + source, + srcpeer, + dest, + pull=pull, + rev=revs, + update=update, + stream=stream, + ) + + srcrepo = srcpeer.local() + abspath = origsource if islocal(origsource): abspath = os.path.abspath(util.urllocalpath(origsource)) @@ -1052,6 +1060,8 @@ shutil.rmtree(cleandir, True) if srcpeer is not None: srcpeer.close() + if destpeer and destpeer.local() is None: + destpeer.close() return srcpeer, destpeer @@ -1253,15 +1263,17 @@ """ source, branches = parseurl(ui.expandpath(source), opts.get(b'branch')) other = peer(repo, opts, source) - ui.status(_(b'comparing with %s\n') % util.hidepassword(source)) - revs, checkout = addbranchrevs(repo, other, branches, opts.get(b'rev')) - - if revs: - revs = [other.lookup(rev) for rev in revs] - other, chlist, cleanupfn = bundlerepo.getremotechanges( - ui, repo, other, revs, opts[b"bundle"], opts[b"force"] - ) + cleanupfn = other.close try: + ui.status(_(b'comparing with %s\n') % util.hidepassword(source)) + revs, checkout = addbranchrevs(repo, other, branches, opts.get(b'rev')) + + if revs: + revs = [other.lookup(rev) for rev in revs] + other, chlist, cleanupfn = bundlerepo.getremotechanges( + ui, repo, other, revs, opts[b"bundle"], opts[b"force"] + ) + if not chlist: ui.status(_(b"no changes found\n")) return subreporecurse() @@ -1320,13 +1332,17 @@ revs = [repo[rev].node() for rev in scmutil.revrange(repo, revs)] other = peer(repo, opts, dest) - outgoing = discovery.findcommonoutgoing( - repo, other, revs, force=opts.get(b'force') - ) - o = outgoing.missing - if not o: - scmutil.nochangesfound(repo.ui, repo, outgoing.excluded) - return o, other + try: + outgoing = discovery.findcommonoutgoing( + repo, other, revs, force=opts.get(b'force') + ) + o = outgoing.missing + if not o: + scmutil.nochangesfound(repo.ui, repo, outgoing.excluded) + return o, other + except: # re-raises + other.close() + raise def outgoing(ui, repo, dest, opts): @@ -1341,27 +1357,30 @@ limit = logcmdutil.getlimit(opts) o, other = _outgoing(ui, repo, dest, opts) - if not o: + try: + if not o: + cmdutil.outgoinghooks(ui, repo, other, opts, o) + return recurse() + + if opts.get(b'newest_first'): + o.reverse() + ui.pager(b'outgoing') + displayer = logcmdutil.changesetdisplayer(ui, repo, opts) + count = 0 + for n in o: + if limit is not None and count >= limit: + break + parents = [p for p in repo.changelog.parents(n) if p != nullid] + if opts.get(b'no_merges') and len(parents) == 2: + continue + count += 1 + displayer.show(repo[n]) + displayer.close() cmdutil.outgoinghooks(ui, repo, other, opts, o) - return recurse() - - if opts.get(b'newest_first'): - o.reverse() - ui.pager(b'outgoing') - displayer = logcmdutil.changesetdisplayer(ui, repo, opts) - count = 0 - for n in o: - if limit is not None and count >= limit: - break - parents = [p for p in repo.changelog.parents(n) if p != nullid] - if opts.get(b'no_merges') and len(parents) == 2: - continue - count += 1 - displayer.show(repo[n]) - displayer.close() - cmdutil.outgoinghooks(ui, repo, other, opts, o) - recurse() - return 0 # exit code is zero since we found outgoing changes + recurse() + return 0 # exit code is zero since we found outgoing changes + finally: + other.close() def verify(repo, level=None): diff --git a/mercurial/revset.py b/mercurial/revset.py --- a/mercurial/revset.py +++ b/mercurial/revset.py @@ -1841,9 +1841,12 @@ if revs: revs = [repo.lookup(rev) for rev in revs] other = hg.peer(repo, {}, dest) - repo.ui.pushbuffer() - outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs) - repo.ui.popbuffer() + try: + repo.ui.pushbuffer() + outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs) + repo.ui.popbuffer() + finally: + other.close() cl = repo.changelog o = {cl.rev(r) for r in outgoing.missing} return subset & o diff --git a/mercurial/sshpeer.py b/mercurial/sshpeer.py --- a/mercurial/sshpeer.py +++ b/mercurial/sshpeer.py @@ -175,10 +175,7 @@ # to deadlocks due to a peer get gc'ed in a fork # We add our own stack trace, because the stacktrace when called # from __del__ is useless. - if False: # enabled in next commit - ui.develwarn( - b'missing close on SSH connection created at:\n%s' % warn - ) + ui.develwarn(b'missing close on SSH connection created at:\n%s' % warn) def _makeconnection(ui, sshcmd, args, remotecmd, path, sshenv=None): diff --git a/mercurial/subrepo.py b/mercurial/subrepo.py --- a/mercurial/subrepo.py +++ b/mercurial/subrepo.py @@ -716,13 +716,17 @@ _(b'sharing subrepo %s from %s\n') % (subrelpath(self), srcurl) ) - shared = hg.share( - self._repo._subparent.baseui, - getpeer(), - self._repo.root, - update=False, - bookmarks=False, - ) + peer = getpeer() + try: + shared = hg.share( + self._repo._subparent.baseui, + peer, + self._repo.root, + update=False, + bookmarks=False, + ) + finally: + peer.close() self._repo = shared.local() else: # TODO: find a common place for this and this code in the @@ -743,14 +747,18 @@ _(b'cloning subrepo %s from %s\n') % (subrelpath(self), util.hidepassword(srcurl)) ) - other, cloned = hg.clone( - self._repo._subparent.baseui, - {}, - getpeer(), - self._repo.root, - update=False, - shareopts=shareopts, - ) + peer = getpeer() + try: + other, cloned = hg.clone( + self._repo._subparent.baseui, + {}, + peer, + self._repo.root, + update=False, + shareopts=shareopts, + ) + finally: + peer.close() self._repo = cloned.local() self._initrepo(parentrepo, source, create=True) self._cachestorehash(srcurl) @@ -760,7 +768,11 @@ % (subrelpath(self), util.hidepassword(srcurl)) ) cleansub = self.storeclean(srcurl) - exchange.pull(self._repo, getpeer()) + peer = getpeer() + try: + exchange.pull(self._repo, peer) + finally: + peer.close() if cleansub: # keep the repo clean after pull self._cachestorehash(srcurl) @@ -845,7 +857,10 @@ % (subrelpath(self), util.hidepassword(dsturl)) ) other = hg.peer(self._repo, {b'ssh': ssh}, dsturl) - res = exchange.push(self._repo, other, force, newbranch=newbranch) + try: + res = exchange.push(self._repo, other, force, newbranch=newbranch) + finally: + other.close() # the repo is now clean self._cachestorehash(dsturl) diff --git a/tests/remotefilelog-getflogheads.py b/tests/remotefilelog-getflogheads.py --- a/tests/remotefilelog-getflogheads.py +++ b/tests/remotefilelog-getflogheads.py @@ -21,7 +21,10 @@ dest = repo.ui.expandpath(b'default') peer = hg.peer(repo, {}, dest) - flogheads = peer.x_rfl_getflogheads(path) + try: + flogheads = peer.x_rfl_getflogheads(path) + finally: + peer.close() if flogheads: for head in flogheads: diff --git a/tests/test-acl.t b/tests/test-acl.t --- a/tests/test-acl.t +++ b/tests/test-acl.t @@ -361,6 +361,7 @@ bundle2-input-bundle: 5 parts total transaction abort! rollback completed + truncating cache/rbc-revs-v1 to 8 abort: acl: user "fred" not allowed on "foo/file.txt" (changeset "ef1ea85a6374") no rollback information available 0:6675d58eff77 @@ -808,7 +809,6 @@ acl: acl.deny.bookmarks not enabled acl: bookmark access granted: "ef1ea85a6374b77d6da9dcda9541f498f2d17df7" on bookmark "moving-bookmark" bundle2-input-bundle: 7 parts total - truncating cache/rbc-revs-v1 to 8 updating the branch cache invalid branch cache (served.hidden): tip differs added 1 changesets with 1 changes to 1 files @@ -900,6 +900,7 @@ bundle2-input-bundle: 7 parts total transaction abort! rollback completed + truncating cache/rbc-revs-v1 to 8 abort: acl: user "fred" denied on bookmark "moving-bookmark" (changeset "ef1ea85a6374b77d6da9dcda9541f498f2d17df7") no rollback information available 0:6675d58eff77 @@ -985,7 +986,6 @@ bundle2-input-part: "phase-heads" supported bundle2-input-part: total payload size 24 bundle2-input-bundle: 5 parts total - truncating cache/rbc-revs-v1 to 8 updating the branch cache added 3 changesets with 3 changes to 3 files bundle2-output-bundle: "HG20", 1 parts total @@ -1073,6 +1073,7 @@ bundle2-input-bundle: 5 parts total transaction abort! rollback completed + truncating cache/rbc-revs-v1 to 8 abort: acl: user "wilma" not allowed on "quux/file.py" (changeset "911600dab2ae") no rollback information available 0:6675d58eff77 @@ -1322,7 +1323,6 @@ bundle2-input-part: "phase-heads" supported bundle2-input-part: total payload size 24 bundle2-input-bundle: 5 parts total - truncating cache/rbc-revs-v1 to 8 updating the branch cache added 3 changesets with 3 changes to 3 files bundle2-output-bundle: "HG20", 1 parts total @@ -1499,6 +1499,7 @@ bundle2-input-bundle: 5 parts total transaction abort! rollback completed + truncating cache/rbc-revs-v1 to 8 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8") no rollback information available 0:6675d58eff77 @@ -1583,7 +1584,6 @@ bundle2-input-part: "phase-heads" supported bundle2-input-part: total payload size 24 bundle2-input-bundle: 5 parts total - truncating cache/rbc-revs-v1 to 8 updating the branch cache added 3 changesets with 3 changes to 3 files bundle2-output-bundle: "HG20", 1 parts total @@ -1671,6 +1671,7 @@ bundle2-input-bundle: 5 parts total transaction abort! rollback completed + truncating cache/rbc-revs-v1 to 8 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8") no rollback information available 0:6675d58eff77 diff --git a/tests/test-http.t b/tests/test-http.t --- a/tests/test-http.t +++ b/tests/test-http.t @@ -382,6 +382,7 @@ devel-peer-request: 16 bytes of commands arguments in headers devel-peer-request: finished in *.???? seconds (200) (glob) received listkey for "phases": 15 bytes + (sent 9 HTTP requests and 3898 bytes; received 920 bytes in responses) $ hg rollback -q $ sed 's/.*] "/"/' < ../access.log diff --git a/tests/test-lfs-serve.t b/tests/test-lfs-serve.t --- a/tests/test-lfs-serve.t +++ b/tests/test-lfs-serve.t @@ -462,6 +462,7 @@ remote: adding manifests remote: adding file changes remote: added 1 changesets with 1 changes to 1 files + (sent 8 HTTP requests and 3526 bytes; received 961 bytes in responses) (?) $ grep 'lfs' .hg/requires $SERVER_REQUIRES .hg/requires:lfs $TESTTMP/server/.hg/requires:lfs