mirror of
https://github.com/facebook/sapling.git
synced 2024-10-11 17:27:53 +03:00
344 lines
11 KiB
Python
344 lines
11 KiB
Python
import os
|
|
|
|
from mercurial import error
|
|
from mercurial import exchange
|
|
from mercurial import extensions
|
|
from mercurial import hg
|
|
from mercurial import namespaces
|
|
from mercurial import repoview
|
|
from mercurial import revset
|
|
from mercurial import templatekw
|
|
from mercurial import ui
|
|
from mercurial import url
|
|
from mercurial import util
|
|
from mercurial.node import hex
|
|
from hgext import schemes
|
|
|
|
_remotenames = {}
|
|
_remotetypes = {}
|
|
|
|
def expush(orig, repo, remote, *args, **kwargs):
|
|
# hack for pushing that turns off the dynamic blockerhook
|
|
repo.__setattr__('_hackremotenamepush', True)
|
|
|
|
res = orig(repo, remote, *args, **kwargs)
|
|
lock = repo.lock()
|
|
try:
|
|
try:
|
|
path = activepath(repo.ui, remote)
|
|
if path:
|
|
# on a push, we don't want to keep obsolete heads since
|
|
# they won't show up as heads on the next pull, so we
|
|
# remove them here otherwise we would require the user
|
|
# to issue a pull to refresh .hg/remotenames
|
|
bmap = {}
|
|
repo = repo.unfiltered()
|
|
for branch, nodes in remote.branchmap().iteritems():
|
|
bmap[branch] = [n for n in nodes if not repo[n].obsolete()]
|
|
saveremotenames(repo, path, bmap, remote.listkeys('bookmarks'))
|
|
except Exception, e:
|
|
ui.debug('remote branches for path %s not saved: %s\n'
|
|
% (path, e))
|
|
finally:
|
|
repo.__setattr__('_hackremotenamepush', False)
|
|
lock.release()
|
|
return res
|
|
|
|
def expull(orig, repo, remote, *args, **kwargs):
|
|
res = orig(repo, remote, *args, **kwargs)
|
|
lock = repo.lock()
|
|
try:
|
|
try:
|
|
path = activepath(repo.ui, remote)
|
|
if path:
|
|
saveremotenames(repo, path, remote.branchmap(),
|
|
remote.listkeys('bookmarks'))
|
|
except Exception, e:
|
|
ui.debug('remote branches for path %s not saved: %s\n'
|
|
% (path, e))
|
|
finally:
|
|
lock.release()
|
|
return res
|
|
|
|
def blockerhook(orig, repo, *args, **kwargs):
|
|
blockers = orig(repo)
|
|
|
|
# protect un-hiding changesets behind a config knob
|
|
unhide = repo.ui.configbool('remotenames', 'unhide')
|
|
hackpush = util.safehasattr(repo, '_hackremotenamepush')
|
|
if not unhide or (hackpush and repo._hackremotenamepush):
|
|
return blockers
|
|
|
|
# add remotenames to blockers
|
|
cl = repo.changelog
|
|
ns = repo.names["remotenames"]
|
|
for name in ns.listnames(repo):
|
|
blockers.update(cl.rev(node) for node in
|
|
ns.nodes(repo, name))
|
|
|
|
return blockers
|
|
|
|
extensions.wrapfunction(exchange, 'push', expush)
|
|
extensions.wrapfunction(exchange, 'pull', expull)
|
|
extensions.wrapfunction(repoview, '_getdynamicblockers', blockerhook)
|
|
|
|
def reposetup(ui, repo):
|
|
if not repo.local():
|
|
return
|
|
|
|
loadremotenames(repo)
|
|
|
|
ns = namespaces.namespace
|
|
n = ns("remotenames", "remotename",
|
|
lambda rp: _remotenames.keys(),
|
|
lambda rp, name: namespaces.tolist(_remotenames.get(name)),
|
|
lambda rp, node: [name for name, n in _remotenames.iteritems()
|
|
if n == node])
|
|
repo.names.addnamespace(n)
|
|
|
|
def activepath(ui, remote):
|
|
realpath = ''
|
|
local = None
|
|
try:
|
|
local = remote.local()
|
|
except AttributeError:
|
|
pass
|
|
|
|
# determine the remote path from the repo, if possible; else just
|
|
# use the string given to us
|
|
rpath = remote
|
|
if local:
|
|
rpath = getattr(remote, 'root', None)
|
|
if rpath is None:
|
|
# Maybe a localpeer? (hg@1ac628cd7113, 2.3)
|
|
rpath = getattr(getattr(remote, '_repo', None),
|
|
'root', None)
|
|
elif not isinstance(remote, str):
|
|
try:
|
|
rpath = remote._url
|
|
except:
|
|
rpath = remote.url
|
|
|
|
for path, uri in ui.configitems('paths'):
|
|
uri = ui.expandpath(expandscheme(ui, uri))
|
|
if local:
|
|
uri = os.path.realpath(uri)
|
|
else:
|
|
if uri.startswith('http'):
|
|
try:
|
|
uri = url.url(uri).authinfo()[0]
|
|
except AttributeError:
|
|
try:
|
|
uri = util.url(uri).authinfo()[0]
|
|
except AttributeError:
|
|
uri = url.getauthinfo(uri)[0]
|
|
uri = uri.rstrip('/')
|
|
rpath = rpath.rstrip('/')
|
|
if uri == rpath:
|
|
realpath = path
|
|
# prefer a non-default name to default
|
|
if path != 'default' and path != 'default-push':
|
|
break
|
|
return realpath
|
|
|
|
def expandscheme(ui, uri):
|
|
'''For a given uri, expand the scheme for it'''
|
|
urischemes = [s for s in schemes.schemes.iterkeys()
|
|
if uri.startswith('%s://' % s)]
|
|
for s in urischemes:
|
|
# TODO: refactor schemes so we don't
|
|
# duplicate this logic
|
|
ui.note('performing schemes expansion with '
|
|
'scheme %s\n' % s)
|
|
scheme = hg.schemes[s]
|
|
parts = uri.split('://', 1)[1].split('/', scheme.parts)
|
|
if len(parts) > scheme.parts:
|
|
tail = parts[-1]
|
|
parts = parts[:-1]
|
|
else:
|
|
tail = ''
|
|
ctx = dict((str(i + 1), v) for i, v in enumerate(parts))
|
|
uri = ''.join(scheme.templater.process(scheme.url, ctx)) + tail
|
|
return uri
|
|
|
|
def splitremotename(remote):
|
|
name = ''
|
|
if '/' in remote:
|
|
remote, name = remote.split('/', 1)
|
|
return remote, name
|
|
|
|
def joinremotename(remote, ref):
|
|
if ref:
|
|
remote += '/' + ref
|
|
return remote
|
|
|
|
def loadremotenames(repo):
|
|
rfile = repo.join('remotenames')
|
|
# exit early if there is nothing to do
|
|
if not os.path.exists(rfile):
|
|
return
|
|
|
|
branches = repo.names['branches'].listnames(repo)
|
|
bookmarks = repo.names['bookmarks'].listnames(repo)
|
|
|
|
alias_default = repo.ui.configbool('remotenames', 'alias.default')
|
|
|
|
f = open(rfile)
|
|
for line in f:
|
|
line = line.strip()
|
|
if not line:
|
|
continue
|
|
node, name = line.split(' ', 1)
|
|
remote, rname = splitremotename(name)
|
|
|
|
# skip old data that didn't write the name (only wrote the alias)
|
|
if not rname:
|
|
continue
|
|
|
|
# handle alias_default here
|
|
if remote != "default" and rname == "default" and alias_default:
|
|
name = remote
|
|
|
|
try:
|
|
ctx = repo[node]
|
|
except error.RepoLookupError:
|
|
continue
|
|
|
|
if not ctx.extra().get('close'):
|
|
_remotenames[name] = ctx.node()
|
|
|
|
# cache the type of the remote name
|
|
if rname in branches:
|
|
_remotetypes[name] = 'branches'
|
|
elif rname in bookmarks:
|
|
_remotetypes[name] = 'bookmarks'
|
|
f.close()
|
|
|
|
def saveremotenames(repo, remote, branches, bookmarks):
|
|
bfile = repo.join('remotenames')
|
|
olddata = []
|
|
existed = os.path.exists(bfile)
|
|
if existed:
|
|
f = open(bfile)
|
|
olddata = [l for l in f
|
|
if not l.split(' ', 1)[1].startswith(remote)]
|
|
f = open(bfile, 'w')
|
|
if existed:
|
|
f.write(''.join(olddata))
|
|
for branch, nodes in branches.iteritems():
|
|
for n in nodes:
|
|
f.write('%s %s/%s\n' % (hex(n), remote, branch))
|
|
for bookmark, n in bookmarks.iteritems():
|
|
f.write('%s %s/%s\n' % (n, remote, bookmark))
|
|
f.close()
|
|
|
|
#########
|
|
# revsets
|
|
#########
|
|
|
|
def upstream_revs(filt, repo, subset, x):
|
|
upstream_tips = set()
|
|
ns = repo.names["remotenames"]
|
|
for name in ns.listnames(repo):
|
|
if filt(name):
|
|
upstream_tips.update(ns.nodes(repo, name))
|
|
|
|
if not upstream_tips:
|
|
return revset.baseset([])
|
|
|
|
tipancestors = repo.revs('::%ln', upstream_tips)
|
|
return revset.filteredset(subset, lambda n: n in tipancestors)
|
|
|
|
def upstream(repo, subset, x):
|
|
'''``upstream()``
|
|
Select changesets in an upstream repository according to remotenames.
|
|
'''
|
|
revset.getargs(x, 0, 0, "upstream takes no arguments")
|
|
upstream_names = [s + '/' for s in
|
|
repo.ui.configlist('remotenames', 'upstream')]
|
|
if not upstream_names:
|
|
filt = lambda x: True
|
|
else:
|
|
filt = lambda name: any(map(name.startswith, upstream_names))
|
|
return upstream_revs(filt, repo, subset, x)
|
|
|
|
def pushed(repo, subset, x):
|
|
'''``pushed()``
|
|
Select changesets in any remote repository according to remotenames.
|
|
'''
|
|
revset.getargs(x, 0, 0, "pushed takes no arguments")
|
|
return upstream_revs(lambda x: True, repo, subset, x)
|
|
|
|
def remotenamesrevset(repo, subset, x):
|
|
"""``remotenames()``
|
|
All remote branches heads.
|
|
"""
|
|
revset.getargs(x, 0, 0, "remotenames takes no arguments")
|
|
remoterevs = set()
|
|
cl = repo.changelog
|
|
ns = repo.names["remotenames"]
|
|
for name in ns.listnames(repo):
|
|
remoterevs.update(ns.nodes(repo, name))
|
|
return revset.baseset(sorted(cl.rev(n) for n in remoterevs))
|
|
|
|
revset.symbols.update({'upstream': upstream,
|
|
'pushed': pushed,
|
|
'remotenames': remotenamesrevset})
|
|
|
|
###########
|
|
# templates
|
|
###########
|
|
|
|
def remotebookmarkskw(**args):
|
|
""":remotebookmarks: List of strings. List of remote bookmarks associated with
|
|
the changeset.
|
|
|
|
"""
|
|
repo, ctx = args['repo'], args['ctx']
|
|
|
|
remotebooks = [name for name in
|
|
repo.names['remotenames'].names(repo, ctx.node())
|
|
if _remotetypes[name] == 'bookmarks']
|
|
|
|
return templatekw.showlist('remotebookmark', remotebooks,
|
|
plural='remotebookmarks', **args)
|
|
|
|
def remotebrancheskw(**args):
|
|
""":remotebranches: List of strings. List of remote branches associated with
|
|
the changeset.
|
|
|
|
"""
|
|
repo, ctx = args['repo'], args['ctx']
|
|
|
|
remotebranches = [name for name in
|
|
repo.names['remotenames'].names(repo, ctx.node())
|
|
if _remotetypes[name] == 'branches']
|
|
|
|
return templatekw.showlist('remotebranch', remotebranches,
|
|
plural='remotebranches', **args)
|
|
|
|
def remotenameskw(**args):
|
|
""":remotenames: List of strings. List of remote names associated with the
|
|
changeset. If remotenames.suppressbranches is True then branch names will
|
|
be hidden if there is a bookmark at the same changeset.
|
|
|
|
"""
|
|
repo, ctx = args['repo'], args['ctx']
|
|
|
|
remotenames = [name for name in
|
|
repo.names['remotenames'].names(repo, ctx.node())
|
|
if _remotetypes[name] == 'bookmarks']
|
|
|
|
suppress = repo.ui.configbool('remotenames', 'suppressbranches', False)
|
|
if not remotenames or not suppress:
|
|
remotenames += [name for name in
|
|
repo.names['remotenames'].names(repo, ctx.node())
|
|
if _remotetypes[name] == 'branches']
|
|
|
|
return templatekw.showlist('remotename', remotenames,
|
|
plural='remotenames', **args)
|
|
|
|
templatekw.keywords['remotebookmarks'] = remotebookmarkskw
|
|
templatekw.keywords['remotebranches'] = remotebrancheskw
|
|
templatekw.keywords['remotenames'] = remotenameskw
|