Mercurial > hg > openjdk > trees
changeset 0:94469fb5f177
initial revision
Subtree information is stored in the file .hg/trees, with a format of one
subtree per line.
author | jcoomes |
---|---|
date | Wed, 13 Oct 2010 23:41:12 -0700 |
parents | |
children | ecf1aa057cc2 |
files | makefile tests/hgrc trees.py |
diffstat | 3 files changed, 727 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/makefile Wed Oct 13 23:41:12 2010 -0700 @@ -0,0 +1,242 @@ +# +# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +# Use $hg from the environment, if set. +HG := $(word 1,${hg} hg) + +# Installation. +PREFIX := ${HG_INSTALL_BASE}/hg +SRC_D := . +TREES_PY := ${SRC_D}/trees.py +SRC_F := trees.py +GET_PY_VER := import sys; print ".".join(sys.version.split(".")[0:2]) +PY_VER := $(shell python -c '${GET_PY_VER}') +DST_D = ${PREFIX}/lib/python${PY_VER}/site-packages/hgext +DST_F = $(addprefix ${DST_D}/,${SRC_F}) + +# Testing. +TEST_RESULTS := test-results +TR := ${TEST_RESULTS} +TEST_CMDS1 = list paths st up +TEST_CMDS2 = in out pull push +TESTS_1 := $(foreach r,r1 r2 r3 r4,$(addprefix ${r}.,${TEST_CMDS1})) +TESTS_2 := $(foreach r,r1 r2,$(addprefix ${r}.,${TEST_CMDS2})) +TESTS = $(TESTS_1) $(TESTS_2) r4.config hs.paths + +HGRC := ${TR}/hgrc +RUN_HG := HGRCPATH=${HGRC} ${HG} + +QUIET := @ + +.PHONY: all install test version ${TESTS} r1.cset r2.cset FORCE + +all: + ${QUIET} echo '${MAKE} install [PREFIX=...]' + ${QUIET} echo '${MAKE} test [ARGS=...]' + ${QUIET} echo '${MAKE} ${TESTS_1} [ARGS=...]' + ${QUIET} echo '${MAKE} ${TESTS_2} [ARGS=...]' + ${QUIET} echo '${MAKE} r4.config hs.paths [ARGS=...]' + +clean: + rm -fr "${TR}" "${SRC_D}"/*.pyc + +install: ${DST_F} + +# File still seen as out of date, even with cp -p. Grrr. +${DST_D}/%: ${SRC_D}/% + cp -p '$^' '$@' && touch '$@' + +test: ${TESTS} + +version: + ${RUN_HG} version + +${TR}: + mkdir -p ${TR} + +${HGRC}: ${SRC_D}/tests/hgrc makefile + ${QUIET} [ -d '${TR}' ] || mkdir -p '${TR}' + ${QUIET} cp '$<' '$@' + ${QUIET} \ + { \ + echo; \ + echo '[extensions]'; \ + echo 'trees = ${TREES_PY}'; \ + echo; \ + echo '[trees]'; \ + echo 's1s2 = s1 s2'; \ + echo 'hs-closed = src/closed test/closed'; \ + } >> '$@' + +${TR}/r1: ${HGRC} + rm -fr '$@' + ${QUIET} echo creating '$@' ... + ${QUIET} \ + all=''; \ + for r in '$@' \ + '$@/s1' \ + '$@/s1/s1.1 with spaces' \ + '$@/s2' \ + '$@/s2/s2.1' \ + '$@/s2/s2.2' \ + '$@/s2/s2.2/s2.2.1'; \ + do \ + ${RUN_HG} init "$$r" && \ + echo $$r > "$$r/x" && \ + ${RUN_HG} -R "$$r" ci -qAm "$$r"; \ + if [ -n "$$all" ]; \ + then \ + all="$$all\n$${r:-.}"; \ + else \ + all="$${r:-.}"; \ + fi; \ + done; \ + root=`${RUN_HG} -R $@ root`; \ + echo "$$all" | sed 's!$@!'"$$root"'!' > '$@.list.ref' + ${QUIET} { \ + echo s1; echo s2; \ + } >> '$@'/.hg/trees + ${QUIET} { \ + echo 's1.1 with spaces'; \ + } >> '$@'/s1/.hg/trees + ${QUIET} { \ + echo s2.1; echo s2.2; echo s2.2/s2.2.1; \ + } >> '$@'/s2/.hg/trees + +${TR}/r2: ${TR}/r1 ${TREES_PY} + rm -fr '$@' + ${RUN_HG} tclone ${ARGS} $< '$@' + ${QUIET} sed 's!$<!$@!' ${<}.list.ref > ${@}.list.ref + ${QUIET} cp -p ${<}.list.ref ${@}.paths.ref + @ echo + +${TR}/r3: ${TR}/r1 ${TR}/r2 ${TREES_PY} + rm -fr '$@' + ${RUN_HG} tclone $< '$@' s1 file:${TR}/r2 s2 + ${QUIET} sed 's!$<!$@!' ${<}.list.ref > ${@}.list.ref + ${QUIET} sed 's!$</s2!${TR}/r2/s2!' ${<}.list.ref > ${@}.paths.ref + @ echo + +${TR}/r4: ${TR}/r1 ${TR}/r2 ${TREES_PY} + rm -fr '$@' + ${RUN_HG} tclone $< '$@' s1 file:${TR}/r2 s2 + ${QUIET} sed 's!$<!$@!' ${<}.list.ref > ${@}.list.ref + @ echo + +%.up: rev = tip +r1.%: rlocal = '${TR}/r1' +r1.%: rother = '${TR}/r2' +r2.%: rlocal = '${TR}/r2' +r2.%: rother = +r3.%: rlocal = '${TR}/r3' +r4.%: rlocal = '${TR}/r4' + +rx_treecmd = t$(subst $(basename $@).,,$@) +rx_redirect = 2>&1 | tee ${TR}/${@}.raw +rx_pathsfilt = sed -n 's/default[ ]*=[ ]*//p' ${TR}/${@}.raw > ${TR}/${@}.out +rx_diff = if [ -f ${TR}/${@}.ref ]; then diff -u ${TR}/${@}.ref ${TR}/${@}.out; fi + +r1.cset: ${TR}/r1 FORCE + ${QUIET} for r in s2 s2/s2.2; \ + do \ + echo $$r >> ${rlocal}/$$r/w; \ + ${RUN_HG} -R ${rlocal}/$$r ci -qAm "$@ $$r w"; \ + echo $$r >> ${rlocal}/$$r/x; \ + ${RUN_HG} -R ${rlocal}/$$r ci -qAm "$@ $$r x"; \ + done + +r2.cset: ${TR}/r2 FORCE + ${QUIET} for r in s1 s2/s2.1 s2/s2.2/s2.2.1; \ + do \ + echo $$r >> ${rlocal}/$$r/y; \ + ${RUN_HG} -R ${rlocal}/$$r ci -qAm "$@ $$r y"; \ + echo $$r >> ${rlocal}/$$r/z; \ + ${RUN_HG} -R ${rlocal}/$$r ci -qAm "$@ $$r z"; \ + done + +${TESTS_1}: ${TR}/r3 ${TR}/r4 + ${RUN_HG} -R ${rlocal} ${rx_treecmd} ${ARGS} ${rev} ${rx_redirect} + ${QUIET} \ + case "$@" in \ + *.paths) ${rx_pathsfilt};; \ + *) mv ${TR}/${@}.raw ${TR}/${@}.out;; \ + esac + ${QUIET} ${rx_diff} + @ echo + +r4.config: ${TR}/r4 + rm -fr ${rlocal}/s3 + ${RUN_HG} init ${rlocal}/s3 + + cat ${rlocal}/.hg/trees + ${RUN_HG} tconfig -R ${rlocal} + + ${RUN_HG} tconfig -R ${rlocal} -a s3 + if ${RUN_HG} tconfig -R ${rlocal} -a s3; then exit 1; fi + cat ${rlocal}/.hg/trees + ${RUN_HG} tconfig -R ${rlocal} + + ${RUN_HG} tconfig -R ${rlocal} -s s1 s2 + cat ${rlocal}/.hg/trees + ${RUN_HG} tconfig -R ${rlocal} + + ${RUN_HG} tconfig -R ${rlocal} -s + cat ${rlocal}/.hg/trees + ${RUN_HG} tconfig -R ${rlocal} + + ${RUN_HG} tconfig -R ${rlocal} -s s1 s2 s3 + cat ${rlocal}/.hg/trees + ${RUN_HG} tconfig -R ${rlocal} + + ${RUN_HG} tconfig -R ${rlocal} -d s1 + cat ${rlocal}/.hg/trees + ${RUN_HG} tconfig -R ${rlocal} + + ${RUN_HG} tconfig -R ${rlocal} -sw + cat ${rlocal}/.hg/trees + ${RUN_HG} tconfig -R ${rlocal} + +r1.in r1.pull r2.out r2.push: r2.cset + ${RUN_HG} -R ${rlocal} ${rx_treecmd} ${ARGS} ${rother} +r1.out r1.push r2.in r2.pull: r1.cset + ${RUN_HG} -R ${rlocal} ${rx_treecmd} ${ARGS} ${rother} + +# combine repos from different trees (http) +hs_url_o = http://hg.openjdk.java.net/jdk7/hotspot +hs_url_c = http://closedjdk.sfbay.sun.com/jdk7/hotspot +${TR}/hs: ${HGRC} ${TREES_PY} + rm -fr '$@' + ${RUN_HG} tclone '${hs_url_o}' '$@' pubs '${hs_url_c}-gc' hs-closed + ${QUIET} \ + for p in '${hs_url_o}' '${hs_url_o}'/pubs \ + '${hs_url_c}-gc/src/closed' '${hs_url_c}-gc/test/closed'; \ + do \ + echo "$$p"; \ + done > $@.paths.ref + + @ echo + +hs.paths: ${TR}/hs + ${RUN_HG} -R '$<' tpaths ${ARGS} ${rx_redirect} + ${QUIET} ${rx_pathsfilt} + ${QUIET} ${rx_diff}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/hgrc Wed Oct 13 23:41:12 2010 -0700 @@ -0,0 +1,9 @@ +[defaults] +backout = -d "0 0" +commit = -d "0 0" +tag = -d "0 0" +ttag = -d "0 0" + +[ui] +slash = True +username = anontester
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trees.py Wed Oct 13 23:41:12 2010 -0700 @@ -0,0 +1,476 @@ +# +# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +'''manage loosely-coupled nested repositories +''' + +import __builtin__ +import inspect +import os +import re +import subprocess + +from mercurial import cmdutil +from mercurial import commands +from mercurial import hg +from mercurial import pushkey +from mercurial import ui +from mercurial import util +from mercurial.i18n import _ + +def _checklocal(repo): + if not repo.local(): + raise util.Abort(_('repository is not local')) + +def _expandsubtrees(ui, subtrees): + '''Expand subtree 'aliases.' + + Each string in subtrees that has a like-named config entry in the [trees] + section is replaced by the right hand side of the config entry.''' + l = [] + for subtree in subtrees: + if '/' in subtree: + l += [subtree] + else: + cfglist = ui.configlist('trees', subtree) + if cfglist: + l += _expandsubtrees(ui, cfglist) + else: + l += [subtree] + return l + +def _subtreelist(ui, repo, opts): + l = opts.get('subtrees') + if l: + del opts['subtrees'] + return _expandsubtrees(ui, l) + l = [] + try: + for line in repo.opener('trees'): + l += [line.rstrip('\r\n')] + except: + pass + return l + +def _subtreegen(ui, repo, opts): + '''yields (repo, subtree) tuples''' + l = _subtreelist(ui, repo, opts) + if l: + for subtree in l: + # Look for file://..., http://..., ssh://..., etc. + if subtree.split(':', 2)[0] in hg.schemes: + repo = hg.repository(ui, subtree) + else: + yield repo, subtree + return + if repo.capable('pushkey'): + s = repo.listkeys('trees') + i = 0 + n = len(s) + while i < n: + subtree = s[("%d" % i)] + # Look for file://..., http://..., ssh://..., etc. + if subtree.split(':', 2)[0] in hg.schemes: + repo = hg.repository(ui, subtree) + else: + yield repo, subtree + i += 1 + +def _makeparentdir(path): + if path: + pdir = os.path.split(path.rstrip(os.sep + '/'))[0] + if pdir and not os.path.exists(pdir): + os.makedirs(pdir) + +def _shortpaths(root, subtrees): + l = [] + if subtrees: + n = len(root) + 1 + if subtrees[0] == root: + l = ['.'] + else: + l = [subtrees[0][n:]] + for subtree in subtrees[1:]: + l += [subtree[n:]] + return l + +def _subtreejoin(repo, subtree): + '''return a string (url or path) referring to subtree within repo''' + if repo.local(): + return repo.wjoin(subtree) + return repo.url().rstrip('/') + '/' + subtree + +def _walk(ui, repo, opts): + l = [] + for dirpath, subdirs, files in os.walk(repo.root, True): + if '.hg' in subdirs: + subdirs.remove('.hg') + l += [dirpath] + return sorted(l) + +def _writeconfig(repo, subtrees): + f = open(repo.join('trees'), 'w') + try: + if subtrees: + f.write('\n'.join(subtrees) + '\n') + finally: + f.close() + return 0 + +# ---------------- commands and associated recursion helpers ------------------- + +def clone(ui, source, dest=None, *subtreeargs, **opts): + '''copy one or more existing repositories to create a tree''' + if subtreeargs: + s = __builtin__.list(subtreeargs) + s.extend(opts.get('subtrees')) # Note: extend does not return a value + opts['subtrees'] = s + + ui.status('cloning %s\n' % source) + _makeparentdir(dest) + # Copied from mercurial/hg.py; need the returned dest repo. + r = hg.clone(hg.remoteui(ui, opts), source, dest, + pull=opts.get('pull'), + stream=opts.get('uncompressed'), + rev=opts.get('rev'), + update=opts.get('updaterev') or not opts.get('noupdate'), + branch=opts.get('branch')) + src = r[0] + dst = r[1] + ui.status(_('created %s\n') % dst.root) + + subtrees = [] + for src, subtree in _subtreegen(src.ui, src, opts): + ui.status('\n') + clone(ui, _subtreejoin(src, subtree), dst.wjoin(subtree), **opts) + subtrees.append(subtree) + _writeconfig(dst, subtrees) + return 0 + +def _command(ui, repo, argv, stop, opts): + ui.status('[%s]:\n' % repo.root) + ui.flush() + # Mercurial bug? util.system() drops elements of argv after the first. + # rc = util.system(argv, cwd=repo.root) + rc = subprocess.call(argv, cwd=repo.root) + if rc and stop: + return rc + for subtree in _subtreelist(ui, repo, opts): + ui.status('\n') + ui.flush() + lr = hg.repository(ui, repo.wjoin(subtree)) + rc += _command(lr.ui, lr, argv, stop, opts) + if rc and stop: + return rc + return rc + +def command(ui, repo, cmd, *args, **opts): + '''run a command in each repo in the tree. + + Change directory to the root of each repo and run the command. + + Note that mercurial normally parses all arguments that start with a dash, + including those that follow the command name, which usually results in an + error. Prevent this by using '--' before the command or arguments, e.g.: + + hg tcommand -- ls -l + ''' + + _checklocal(repo) + l = __builtin__.list((cmd,) + args) + return _command(ui, repo, l, opts.get('stop'), opts) + +def config(ui, repo, *subtrees, **opts): + '''list or change the subtrees configuration + + One of four operations can be selected: --list, --add, --del, or --set. + If no operation is specified, --list is assumed. + + If the --walk option is used with --set, the filesystem rooted at + REPO is scanned and the subtree configuration set to the discovered repos. + ''' + + _checklocal(repo) + + opadd = opts.get('add') + opdel = opts.get('del') + oplst = opts.get('list') + opset = opts.get('set') + cnt = opadd + opdel + oplst + opset + if cnt > 1: + raise util.Abort(_('at most one of --add, --del, --list or --set is ' + + 'allowed')) + if cnt == 0 or oplst: + l = _subtreelist(ui, repo, opts) + for subtree in l: + ui.write(subtree + '\n') + return 0 + if opadd and subtrees: + l = _subtreelist(ui, repo, opts) + for subtree in subtrees: + if subtree in l: + raise util.Abort(_('subtree %s already configured' % subtree)) + l += [subtree] + return _writeconfig(repo, l) + if opdel: + all = opts.get('all') + if all + bool(subtrees) != 1: + raise util.Abort(_('use either --all or subtrees (but not both)')) + if all: + return _writeconfig(repo, []) + l = _subtreelist(ui, repo, opts) + for subtree in subtrees: + if not subtree in l: + raise util.Abort(_('no subtree %s' % subtree)) + l.remove(subtree) + return _writeconfig(repo, l) + if opset: + walk = opts.get('walk') + if walk + bool(subtrees) != 1: + raise util.Abort(_('use either --walk or subtrees (but not both)')) + l = subtrees + if walk: + l = _shortpaths(repo.root, _walk(ui, repo, {}))[1:] + return _writeconfig(repo, l) + +def _incoming(ui, repo, remote, adjust, opts): + ui.status('[%s]:\n' % repo.root) + commands.incoming(ui, repo, remote, **opts) + for subtree in _subtreelist(ui, repo, opts): + ui.status('\n') + lr = hg.repository(ui, repo.wjoin(subtree)) + remote2 = adjust and os.path.join(remote, subtree) or remote + _incoming(lr.ui, lr, remote2, adjust, opts) + return 0 + +def incoming(ui, repo, remote="default", **opts): + '''show new changesets found in source ''' + _checklocal(repo) + adjust = remote and not ui.config('paths', remote) + return _incoming(ui, repo, remote, adjust, opts) + +def _list(ui, repo, opts): + l = [repo.root] + for subtree in _subtreelist(ui, repo, opts): + dir = repo.wjoin(subtree) + if os.path.exists(dir): + lr = hg.repository(ui, dir) + l += _list(lr.ui, lr, opts) + else: + ui.warn('repo %s is missing subtree %s\n' % (repo.root, subtree)) + return l + +def list(ui, repo, **opts): + '''list the repo and configured subtrees, recursively + + The initial list of subtrees is obtained from the command line (if present) + or from the repo's subtrees configuration. + + If the --walk option is specified, search the filesystem for subtrees + instead of using the command line or configuration item. + + If the --short option is specified, "short" paths are listed (relative to + the top-level repo).''' + + _checklocal(repo) + if opts.get('walk'): + l = _walk(ui, repo, opts) + else: + l = _list(ui, repo, opts) + if opts.get('short'): + l = _shortpaths(repo.root, l) + for subtree in l: + ui.write(subtree + '\n') + return 0 + +def _outgoing(ui, repo, remote, adjust, opts): + ui.status('[%s]:\n' % repo.root) + commands.outgoing(ui, repo, remote, **opts) + for subtree in _subtreelist(ui, repo, opts): + ui.status('\n') + lr = hg.repository(ui, repo.wjoin(subtree)) + remote2 = adjust and os.path.join(remote, subtree) or remote + _outgoing(lr.ui, lr, remote2, adjust, opts) + return 0 + +def outgoing(ui, repo, remote=None, **opts): + '''show changesets not found in the destination''' + _checklocal(repo) + adjust = remote and not ui.config('paths', remote) + return _outgoing(ui, repo, remote, adjust, opts) + +def paths(ui, repo, search=None, **opts): + '''show aliases for remote repositories''' + _checklocal(repo) + ui.status('[%s]:\n' % repo.root) + commands.paths(ui, repo, search) + for subtree in _subtreelist(ui, repo, opts): + ui.status('\n') + lr = hg.repository(ui, repo.wjoin(subtree)) + paths(lr.ui, lr, search, **opts) + return 0 + +def _pull(ui, repo, remote, adjust, opts): + ui.status('[%s]:\n' % repo.root) + commands.pull(ui, repo, remote, **opts) + for subtree in _subtreelist(ui, repo, opts): + ui.status('\n') + lr = hg.repository(ui, repo.wjoin(subtree)) + remote2 = adjust and os.path.join(remote, subtree) or remote + _pull(lr.ui, lr, remote2, adjust, opts) + return 0 + +def pull(ui, repo, remote="default", **opts): + '''pull changes from the specified source''' + _checklocal(repo) + adjust = remote and not ui.config('paths', remote) + return _pull(ui, repo, remote, adjust, opts) + +def _push(ui, repo, remote, adjust, opts): + ui.status('[%s]:\n' % repo.root) + commands.push(ui, repo, remote, **opts) + for subtree in _subtreelist(ui, repo, opts): + ui.status('\n') + lr = hg.repository(ui, repo.wjoin(subtree)) + remote2 = adjust and os.path.join(remote, subtree) or remote + _push(lr.ui, lr, remote2, adjust, opts) + return 0 + +def push(ui, repo, remote=None, **opts): + '''push changes to the specified destination''' + _checklocal(repo) + adjust = remote and not ui.config('paths', remote) + return _push(ui, repo, remote, adjust, opts) + +def status(ui, repo, *pats, **opts): + '''show changed files in the working directory''' + _checklocal(repo) + ui.status('[%s]:\n' % repo.root) + commands.status(ui, repo, *pats, **opts) + for subtree in _subtreelist(ui, repo, opts): + ui.status('\n') + lr = hg.repository(ui, repo.wjoin(subtree)) + status(lr.ui, lr, *pats, **opts) + return 0 + +def tag(ui, repo, name1, *names, **opts): + '''add one or more tags for the current or given revision''' + _checklocal(repo) + ui.status('[%s]:\n' % repo.root) + commands.tag(ui, repo, name1, *names, **opts) + for subtree in _subtreelist(ui, repo, opts): + ui.status('\n') + lr = hg.repository(ui, repo.wjoin(subtree)) + tag(lr.ui, lr, name1, *names, **opts) + return 0 + +def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False, + **opts): + '''update working directory (or switch revisions)''' + _checklocal(repo) + ui.status('[%s]:\n' % repo.root) + commands.update(ui, repo, node, rev, clean, date, check) + for subtree in _subtreelist(ui, repo, opts): + ui.status('\n') + lr = hg.repository(ui, repo.wjoin(subtree)) + update(lr.ui, lr, node, rev, clean, date, check, **opts) + return 0 + +def version(ui, **opts): + '''show version information''' + ui.status('trees extension (version 0.5)\n') + +def debugkeys(ui, src, **opts): + repo = hg.repository(ui, src) + print(repo.listkeys('trees')) + +# ----------------------------- mercurial linkage ------------------------------ + +subtreesopt = [('', 'subtrees', [], + _('path to subtree'), + _('SUBTREE'))] + +walkopt = [('w', 'walk', False, + _('walk the filesystem to discover subtrees'))] + +commandopts = [('', 'stop', False, + _('stop if command returns non-zero')) + ] + subtreesopt +listopts = [('s', 'short', False, + _('list short paths (relative to repo root)')) + ] + walkopt + subtreesopt +configopts = [('a', 'add', False, + _('add the specified SUBTREEs to config')), + ('', 'all', False, + _('with --del, delete all subtrees from config')), + ('d', 'del', False, + _('delete the specified SUBTREEs from config')), + ('l', 'list', False, + _('list the configured subtrees')), + ('s', 'set', False, _('set the subtree config to SUBTREEs')) + ] + walkopt + +def _newcte(origcmd, newfunc, extraopts = [], synopsis = None): + '''generate a cmdtable entry based on that for origcmd''' + cte = cmdutil.findcmd(origcmd, commands.table)[1] + if (len(cte) > 2): + return (newfunc, cte[1] + extraopts, synopsis or cte[2]) + return (newfunc, cte[1] + extraopts, synopsis) + +# Commands tagged with '^' are listed by 'hg help'. +cmdtable = { + '^tclone': _newcte('clone', clone, subtreesopt, + _('[OPTION]... SOURCE [DEST [SUBTREE]...]')), + 'tcommand': (command, commandopts, _('command [arg] ...')), + 'tconfig': (config, configopts, _('[OPTION]... [SUBTREE]...')), + 'tincoming': _newcte('incoming', incoming, subtreesopt), + 'toutgoing': _newcte('outgoing', outgoing, subtreesopt), + 'tlist': (list, listopts, _('[OPTION]...')), + 'tpaths': _newcte('paths', paths, subtreesopt), + '^tpull': _newcte('pull', pull, subtreesopt), + '^tpush': _newcte('push', push, subtreesopt), + '^tstatus': _newcte('status', status, subtreesopt), + '^tupdate': _newcte('update', update, subtreesopt), + 'ttag': _newcte('tag', tag, subtreesopt), + 'tversion': (version, [], ''), + 'tdebugkeys': (debugkeys, [], '') +} + +commands.norepo += ' tclone tversion tdebugkeys' + +def _treeslistkeys(repo): + # trees are ordered, so the keys are the non-negative integers. + s = {} + i = 0 + try: + for line in repo.opener('trees'): + s[("%d" % i)] = line.rstrip('\n\r') + i += 1 + return s + except: + return {} + +def reposetup(ui, repo): + if r.capable('pushkey'): + # Pushing keys is disabled; unclear whether/how it should work. + pushkey.register('trees', lambda *x: False, _treeslistkeys)