summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew McDermott <andrew.mcdermott@linaro.org>2014-03-19 10:56:29 +0000
committerAndrew McDermott <andrew.mcdermott@linaro.org>2014-03-19 12:04:43 +0000
commit290b311f403d55e881cdeb6f6613e3f4a0161bbd (patch)
tree5efe856e3582b83ae2a7d257a6bc7553efd45686
parentff9b689348e4a770132fe34ea93739d996bd44dd (diff)
Add script to clone aarch64-port tree using Mercurial
Signed-off-by: Andrew McDermott <andrew.mcdermott@linaro.org>
-rw-r--r--.gitignore1
-rwxr-xr-xopenjdk-clone-tree63
-rw-r--r--trees.py994
3 files changed, 1058 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index 212f94f..e8393b9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,4 @@ net/
jdk8/
openjdk8-aarch64-port-snapshot/
openjdk8-jdk8-snapshot/
+out/
diff --git a/openjdk-clone-tree b/openjdk-clone-tree
new file mode 100755
index 0000000..7966902
--- /dev/null
+++ b/openjdk-clone-tree
@@ -0,0 +1,63 @@
+#!/bin/bash
+
+# Copyright (C) 2014, Linaro Limited.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# Author: Andrew McDermott <andrew.mcdermott@linaro.org>
+
+set -eu
+
+progname=$(basename $0)
+SCRIPTPATH=$(cd $(dirname $0); pwd -P)
+
+: ${DOWNLOAD_DIR:=/tmp/${progname}-download.$$}
+
+tdir=$(mktemp -d /tmp/${progname}-hgrc-XXXXXX.$$)
+echo "[extensions]" > $tdir/.hgrc
+echo "trees=${SCRIPTPATH}/trees.py" >> $tdir/.hgrc
+
+trap "rm -rf $tdir" EXIT
+
+export HGRCPATH=${tdir}/.hgrc
+
+: ${URL:=http://hg.openjdk.java.net/aarch64-port/jdk8}
+
+if [ ! -d jdk8 ]; then
+ hg tclone ${URL}
+else
+ hg --cwd jdk8 tpull
+ hg --cwd jdk8 tupdate
+fi
+
+pushd jdk8 >/dev/null
+commit_id=$(hg identify | awk '{print $1}')
+echo "jdk8-${commit_id}" > BOM
+for i in corba jaxp jaxws langtools jdk hotspot nashorn; do
+ pushd $i >/dev/null
+ commit_id=$(hg identify | awk '{print $1}')
+ dir=$(basename $PWD)
+ echo "${dir}-${commit_id}" >> ../BOM
+ popd >/dev/null
+done
+popd >/dev/null
+
+rm -rf out
+mkdir -p out
+
+[ -e jdk8/BOM ] && cat jdk8/BOM
+tar -acf out/openjdk8-aarch64-port-aarch64-port.tar.bz2 --exclude=.hg --transform='s/^jdk8/openjdk8-aarch64-port/' jdk8
+ls -l out/*.bz2
+md5sum out/*.bz2
diff --git a/trees.py b/trees.py
new file mode 100644
index 0000000..9cbc5c0
--- /dev/null
+++ b/trees.py
@@ -0,0 +1,994 @@
+#
+# Copyright (c) 2010, 2014, 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
+
+A 'tree' is simply a repository that may contain other repositories (or other
+trees) nested within its working directory. This extension provides commands
+that operate on an entire tree, or on selected trees within it.
+
+Each tree stores a list of the repositories contained within it in a
+non-versioned file, .hg/trees. The file lists the path to each contained tree,
+relative to the root, one per line. The file is created when a tree is cloned,
+and can be modified using the tconfig command. It is not updated when pushing
+or pulling changesets (tpush, tpull).
+
+The extension is usable on the client even if the hg server does not have the
+trees extension enabled; it simply requires a bit more typing when cloning
+repos. Each repo in the tree maintains its own path information in .hg/hgrc, so
+that repositories from different locations can be combined into a single tree.
+
+The following example creates a tree called 'myproj' that includes four nested
+repositories ('src', 'docs', 'images' and 'styles') with the last two coming
+from a different server. If the hg servers have the trees extension enabled::
+
+ $ hg tclone http://abc/proj myproj
+ $ hg tclone --skiproot http://xyz/pub myproj
+
+If the hg servers do not have the trees extension enabled, then simply append
+the desired contained repos (subtrees) to the command line::
+
+ $ hg tclone http://abc/proj myproj src docs
+ $ hg tclone --skiproot http://xyz/pub myproj images styles
+
+The above is shorthand for five clone operations (three from the first command,
+and two more from the second)::
+
+ $ hg clone http://abc/proj myproj
+ $ hg clone http://abc/proj/src myproj/src
+ $ hg clone http://abc/proj/docs myproj/docs
+ $ hg clone http://xyz/pub/images myproj/images
+ $ hg clone http://xyz/pub/styles myproj/styles
+
+It also writes the tree configuration to myproj/.hg/trees. Note that the same
+tree can also be created with a single tclone::
+
+ $ hg tclone http://abc/proj myproj src docs http://xyz/pub images styles
+
+More examples::
+
+Show the working directory status for each repo in the tree::
+
+ $ hg tstatus
+
+Pull upstream changes into each repo in the tree and update the working dir::
+
+ $ hg tpull -u
+
+Push local changes from each repo in the tree::
+
+ $ hg tpush
+
+List the tree configuration recursively::
+
+ $ hg tlist
+ /path/to/myproj
+ /path/to/myproj/src
+ /path/to/myproj/docs
+ /path/to/myproj/images
+ /path/to/myproj/styles
+
+You can create abbreviations for tree lists by adding a [trees] section to your
+hgrc file, e.g.::
+
+ [trees]
+ jdk-lt = jdk langtools
+ ojdk-all = corba hotspot jaxp jaxws jdk-lt
+
+which could be used like this:
+
+ $ hg tclone http://hg.openjdk.java.net/jdk7/jdk7 myjdk7 ojdk-all
+
+to create the myjdk7 tree which contains corba, hotspot, jaxp, jaxws, jdk and
+langtools repos.
+
+Show the working directory status, but only for the root repo plus the jdk and
+langtools repos::
+
+ $ hg tstatus --subtrees jdk-lt
+"""
+
+import __builtin__
+import exceptions
+import inspect
+import os
+import re
+import subprocess
+
+from mercurial import cmdutil
+from mercurial import commands
+from mercurial import extensions
+from mercurial import hg
+from mercurial import localrepo
+from mercurial import pushkey
+from mercurial import ui
+from mercurial import util
+from mercurial.i18n import _
+
+testedwith = '''
+1.1 1.1.2 1.2 1.2.1 1.3 1.3.1 1.4 1.4.3 1.5 1.5.4
+1.6 1.6.4 1.7 1.7.5 1.8 1.8.4 1.9 1.9.3
+2.0-rc 2.0 2.0.2 2.1-rc 2.1 2.1.2 2.2-rc 2.2 2.2.3
+2.3-rc 2.3 2.3.2 2.4-rc 2.4 2.4.2 2.5-rc 2.5 2.5.4
+2.6-rc 2.6 2.6.3 2.7-rc 2.7 2.7.2 2.8-rc 2.8 2.8.2
+2.9-rc 2.9
+'''
+
+def _checklocal(repo):
+ if not isinstance(repo, localrepo.localrepository):
+ 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 _nsnormalize(s):
+ if s == 'trees' or s.startswith('trees.'):
+ return s
+ return 'trees.' + s
+
+def _ns(ui, opts):
+ return _nsnormalize(opts.get('tns') or
+ ui.config('trees', 'namespace', 'trees'))
+
+_splitui = None
+
+def _splitsubtrees(l):
+ global _splitui
+ res = []
+ for s in l:
+ if "'" in s or '"' in s:
+ # Use ui.configlist() for quoted strings; requires hg 1.6 or later.
+ if not _splitui:
+ _splitui = ui.ui()
+ _splitui.setconfig('x', 'x', s)
+ res += _splitui.configlist('x', 'x')
+ else:
+ res += s.split()
+ return res
+
+def _subtreelist(ui, repo, opts):
+ l = opts.get('subtrees')
+ if l:
+ del opts['subtrees']
+ cansplit = ui.configbool('trees', 'splitargs', True)
+ return _expandsubtrees(ui, cansplit and _splitsubtrees(l) or l)
+ l = []
+ try:
+ keys = repo.listkeys(_ns(ui, opts))
+ for i in xrange(0, len(keys)):
+ l.append(keys[str(i)])
+ except:
+ pass
+ return l
+
+def hg_repo(ui, url, opts):
+ parts = url.split(':', 2)
+ if len(parts) == 1 or parts[0] == 'file':
+ return hg.repository(ui, url)
+ return hg.peer(ui, opts, url)
+
+def _subtreegen_listkeys(ui, repo, opts, namespace):
+ keys = repo.listkeys(namespace)
+ for i in range(len(keys)):
+ subtree = keys[("%d" % i)]
+ yield repo, subtree
+
+def _subtreegen(ui, repo, opts):
+ """yields (repo, subtree) tuples"""
+ l = _subtreelist(ui, repo, opts)
+ namespace = _ns(ui, opts)
+ yielded = False
+ if l:
+ for subtree in l:
+ # Look for file://..., http://..., ssh://..., etc.
+ if subtree.split(':', 2)[0] in hg.schemes:
+ if not yielded:
+ for r, st in _subtreegen_listkeys(ui, repo, opts, namespace):
+ yield r, st
+ repo = hg_repo(ui, subtree, opts)
+ yielded = False
+ else:
+ yielded = True
+ yield repo, subtree
+ if not yielded:
+ for r, st in _subtreegen_listkeys(ui, repo, opts, namespace):
+ yield r, st
+
+def _docmd1(cmd, ui, repo, *args, **opts):
+ """Call cmd for repo and each configured/specified subtree.
+
+ This is for commands which operate on a single tree (e.g., tstatus,
+ tupdate)."""
+
+ ui.status('[%s]:\n' % repo.root)
+ # XXX - should be done just once.
+ cmdopts = dict(opts)
+ for o in subtreesopts:
+ if o[1] in cmdopts:
+ del cmdopts[o[1]]
+ trc = cmd(ui, repo, *args, **cmdopts)
+ rc = trc != None and trc or 0
+ for subtree in _subtreelist(ui, repo, opts):
+ ui.status('\n')
+ lr = hg.repository(ui, repo.wjoin(subtree))
+ trc = _docmd1(cmd, lr.ui, lr, *args, **opts)
+ rc += trc != None and trc or 0
+ return rc
+
+def _docmd2(cmd, ui, repo, remote, adjust, **opts):
+ """Call cmd for repo and each configured/specified subtree.
+
+ This is for commands which operate on two trees (e.g., tpull, tpush)."""
+
+ ui.status('[%s]:\n' % repo.root)
+ trc = cmd(ui, repo, remote, **opts)
+ rc = trc != None and trc or 0
+ 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
+ trc = _docmd2(cmd, lr.ui, lr, remote2, adjust, **opts)
+ rc += trc != None and trc or 0
+ return rc
+
+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 _origcmd(name):
+ """Return the callable mercurial will invoke for the given command name."""
+ return cmdutil.findcmd(name, commands.table)[1][0]
+
+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 _stripfilescheme(url):
+ if url.startswith('file:'):
+ i = 5
+ while url[i:i+1] == '//':
+ i += 1
+ url = url[i:]
+ return url
+
+def _subtreejoin(repo, subtree):
+ """Return a string (url or path) referring to subtree within repo."""
+ u = _stripfilescheme(repo.url()).rstrip('/')
+ return u + '/' + 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 _readfile(path):
+ f = None
+ try:
+ f = open(path, 'r')
+ s = f.read()
+ f.close()
+ return s
+ except:
+ if f:
+ f.close()
+ return None
+
+def _writeconfig(repo, namespace, subtrees, append = False):
+ confpath = repo.join(namespace or 'trees')
+ if subtrees:
+ newconfig = '\n'.join(subtrees) + '\n'
+ if append or newconfig != _readfile(confpath):
+ f = open(confpath, append and 'a' or 'w')
+ try:
+ f.write(newconfig)
+ finally:
+ f.close()
+ elif os.path.exists(confpath):
+ os.remove(confpath)
+ return 0
+
+# ---------------- commands and associated recursion helpers -------------------
+
+def _clonerepo(ui, source, dest, opts):
+ _makeparentdir(dest)
+ # Copied from mercurial/hg.py; need the returned dest repo.
+ s, d = hg_clone(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'))
+ if isinstance(s, localrepo.localrepository) or isinstance(d.local(), bool):
+ return (s, d)
+ # peers; return the destination localrepo
+ return (s, d.local())
+
+def _skiprepo(ui, source, dest):
+ src = None
+ try:
+ src = hg_repo(ui, source, {})
+ except:
+ class fakerepo(object):
+ def __init__(self, ui, path):
+ self.ui = ui
+ self._path = path
+ def peer(self):
+ return self
+ def local(self):
+ return self._path
+ def url(self):
+ return self._path
+ def wjoin(self, path):
+ return os.path.join(self._path, path)
+ src = fakerepo(ui, source)
+ return (src, hg.repository(ui, dest))
+
+def _clonesubtrees(ui, src, dst, opts):
+ 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)
+ return subtrees
+
+def _clone(ui, source, dest, opts, skiproot = False):
+ if not skiproot and not os.path.exists(os.path.join(dest, '.hg')):
+ ui.status('cloning %s\n' % source)
+ src, dst = _clonerepo(ui, source, dest, opts)
+ ui.status(_('created %s\n') % dst.root)
+ else:
+ msg = 'skipping %s (destination exists)\n'
+ if skiproot:
+ msg = 'skipping root %s\n'
+ ui.status(msg % source)
+ src, dst = _skiprepo(ui, source, dest)
+ subtrees = _clonesubtrees(ui, src, dst, opts)
+ addconfig(ui, dst, subtrees, opts, True)
+
+# Need to indirect through hg_clone for compatibility w/various hg versions.
+hg_clone = None
+
+def clone(ui, source, dest=None, *subtreeargs, **opts):
+ '''copy one or more existing repositories to create a tree'''
+ global hg_clone
+ if not hg_clone:
+ hg_clone = compatible_clone()
+ if subtreeargs:
+ s = __builtin__.list(subtreeargs)
+ s.extend(opts.get('subtrees')) # Note: extend does not return a value
+ opts['subtrees'] = s
+ if dest is None:
+ dest = hg.defaultdest(source)
+ _clone(ui, source, dest, opts, opts.get('skiproot'))
+ 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.
+
+ The command is executed directly (i.e., not using a shell), so if i/o
+ redirection or other shell features are desired, include the shell
+ invocation in the command, e.g.: hg tcommand -- sh -c 'ls -l > ls.out'
+
+ Mercurial 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 commit(ui, repo, *pats, **opts):
+ """commit all files"""
+ _checklocal(repo)
+ if pats:
+ util.Abort('must commit all files')
+
+ hgcommit = _origcmd('commit')
+ def condcommit(ui, repo, *pats, **opts):
+ '''commit conditionally - only if there is something to commit'''
+ for l in repo.status()[:3]: # modified, added, removed
+ if l:
+ return hgcommit(ui, repo, *pats, **opts)
+ ui.status('nothing to commit\n')
+ return 0
+
+ return _docmd1(condcommit, ui, repo, *pats, **opts)
+
+def addconfig(ui, repo, subtrees, opts, ignoredups = False):
+ modified = False
+ l = _subtreelist(ui, repo, opts)
+ for subtree in subtrees:
+ if subtree in l:
+ if not ignoredups:
+ raise util.Abort(_('subtree %s already configured' % subtree))
+ else:
+ l += [subtree]
+ modified = True
+ if modified:
+ return _writeconfig(repo, _ns(ui, opts), l)
+ return 0
+
+def delconfig(ui, repo, subtrees, opts):
+ all = opts.get('all')
+ if all and subtrees:
+ raise util.Abort(_('use either --all or subtrees (but not both)'))
+ if all:
+ return _writeconfig(repo, _ns(ui, opts), [])
+ 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, _ns(ui, opts), l)
+
+def expandconfig(ui, repo, args, opts):
+ """show recursively-expanded trees config items
+
+ Config items in the [trees] section can be defined in terms of other items;
+ this command shows the expanded value.
+
+ returns 0 if at least one config item was found; otherwise returns 1.
+ """
+
+ rc = 1
+ for item in args:
+ rhs = ui.configlist('trees', item)
+ if rhs:
+ rc = 0
+ l = _expandsubtrees(ui, rhs)
+ ui.write(' '.join(l))
+ ui.write('\n')
+ return rc
+
+def _depthmostsplit(subtreemap, subtree):
+ repo, sub = os.path.split(subtree)
+ while repo:
+ if repo in subtreemap:
+ return repo, sub
+ repo, sub2 = os.path.split(repo)
+ sub = subtree[len(repo) + 1:]
+ return '.', subtree
+
+def nestconfig(ui, repo, subtrees, opts):
+ newtrees = { }
+ subtreemap = dict.fromkeys(subtrees)
+ for sub in subtrees:
+ nestedrepo, nestedsub = _depthmostsplit(subtreemap, sub)
+ if nestedrepo in newtrees:
+ nl = newtrees[nestedrepo]
+ if nestedsub not in nl:
+ nl.append(nestedsub)
+ else:
+ newtrees[nestedrepo] = [nestedsub]
+ for sub in subtrees:
+ nr = hg_repo(ui, _subtreejoin(repo, sub), {})
+ _writeconfig(nr, _ns(ui, opts), newtrees.get(sub))
+ return _writeconfig(repo, _ns(ui, opts), newtrees.get('.'))
+
+# tconfig --set --depth example::
+#
+# before after
+# ------------ --------------------
+# $ hg tconfig $ hg tconfig
+# sub1 sub1
+# sub1/sub1.1 sub2
+# sub2 $ hg -R sub1 tconfig
+# sub2/sub2.1 sub1.1
+# sub2/sub2.2 $ hg -R sub2 tconfig
+# sub2.1
+# sub2.2
+
+def setconfig(ui, repo, subtrees, opts):
+ walk = opts.get('walk')
+ depth = opts.get('depth')
+ if walk and subtrees:
+ msg = _('subtrees cannot be specified when --walk is used')
+ raise util.Abort(msg)
+ elif not (subtrees or walk or depth):
+ msg = _('specify subtrees, or use --walk and/or --depth')
+ raise util.Abort(msg)
+
+ if walk:
+ subtrees = _shortpaths(repo.root, _walk(ui, repo, {}))[1:]
+ elif not subtrees:
+ subtrees = _shortpaths(repo.root, _list(ui, repo, opts))[1:]
+ if depth:
+ return nestconfig(ui, repo, subtrees, opts)
+ return _writeconfig(repo, _ns(ui, opts), subtrees)
+
+def config(ui, repo, *subtrees, **opts):
+ """list or change the subtrees configuration
+
+ One of five operations can be selected:
+
+ --list: list the configured subtrees; this is the default if no other
+ operation is selected.
+
+ --add: add the specified subtrees to the configuration.
+
+ --del: delete the specified subtrees from the configuration.
+ Use --del --all to delete all subtrees.
+
+ --set: set the subtree configuration to the specified subtrees.
+ Use --set --walk to walk the filesystem rooted at REPO and set the
+ subtree configuration to the discovered repos. Use --depth
+ to write the subtree configuration depth-most, so that each
+ subtree is defined within the nearest enclosing repository. Note
+ that --walk and --depth may be used together.
+
+ --expand: list the value of config items from the [trees] section.
+ Items in the [trees] section can be defined in terms of other
+ items in the [trees] section; tconfig --expand shows the
+ recursively expanded value. It returns 0 if at least one config
+ item was found; otherwise it returns 1.
+
+ Note that with the slight exception of --set --depth, this command
+ does not recurse into subtrees; it operates only on the current
+ repository. (To recursively list subtrees, use the tlist command.)
+
+ """
+
+ opadd = opts.get('add')
+ opdel = opts.get('del')
+ opexp = opts.get('expand')
+ oplst = opts.get('list')
+ opset = opts.get('set')
+ cnt = opadd + opdel + opexp + oplst + opset
+ if cnt > 1:
+ raise util.Abort(_('at most one of --add, --del, --list, ' +
+ '--set or --expand is allowed'))
+ if not opexp and not repo:
+ raise util.Abort(_('no repository found'))
+ if repo:
+ _checklocal(repo)
+
+ if opadd:
+ return addconfig(ui, repo, subtrees, opts)
+ if opdel:
+ return delconfig(ui, repo, subtrees, opts)
+ if opexp:
+ return expandconfig(ui, repo, subtrees, opts)
+ if opset:
+ return setconfig(ui, repo, subtrees, opts)
+
+ for subtree in _subtreelist(ui, repo, opts):
+ ui.write(subtree + '\n')
+ return 0
+
+def diff(ui, repo, *args, **opts):
+ """diff repository (or selected files)"""
+ _checklocal(repo)
+ return _docmd1(_origcmd('diff'), ui, repo, *args, **opts)
+
+def heads(ui, repo, *branchrevs, **opts):
+ """show current repository heads or show branch heads"""
+ _checklocal(repo)
+ st = opts.get('subtrees')
+ repocount = len(_list(ui, repo, st and {'subtrees': st} or {}))
+ rc = _docmd1(_origcmd('heads'), ui, repo, *branchrevs, **opts)
+ # return 0 if any of the repos have matching heads; 1 otherwise.
+ return int(rc == repocount)
+
+def incoming(ui, repo, remote="default", **opts):
+ """show new changesets found in source"""
+ _checklocal(repo)
+ adjust = remote and not ui.config('paths', remote)
+ st = opts.get('subtrees')
+ repocount = len(_list(ui, repo, st and {'subtrees': st} or {}))
+ rc = _docmd2(_origcmd('incoming'), ui, repo, remote, adjust, **opts)
+ # return 0 if any of the repos have incoming changes; 1 otherwise.
+ return int(rc == repocount)
+
+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 configuration.
+
+ If the --walk option is specified, search the filesystem instead of using
+ the command line or repo configuration.
+
+ If the --short option is specified, the listed paths are 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 log(ui, repo, *args, **opts):
+ '''show revision history of entire repository or files'''
+ _checklocal(repo)
+ return _docmd1(_origcmd('log'), ui, repo, *args, **opts)
+
+def merge(ui, repo, node=None, **opts):
+ '''merge working directory with another revision'''
+ _checklocal(repo)
+
+ hgmerge = _origcmd('merge')
+ def condmerge(ui, repo, node=None, **opts):
+ if len(repo.heads()) > 1:
+ return hgmerge(ui, repo, node, **opts)
+ ui.status('nothing to merge\n')
+ return 0
+
+ return _docmd1(condmerge, ui, repo, node, **opts)
+
+def outgoing(ui, repo, remote=None, **opts):
+ '''show changesets not found in the destination'''
+ _checklocal(repo)
+ adjust = remote and not ui.config('paths', remote)
+ st = opts.get('subtrees')
+ repocount = len(_list(ui, repo, st and {'subtrees': st} or {}))
+ rc = _docmd2(_origcmd('outgoing'), ui, repo, remote, adjust, **opts)
+ # return 0 if any of the repos have outgoing changes; 1 otherwise.
+ return int(rc == repocount)
+
+def parents(ui, repo, filename=None, **opts):
+ _checklocal(repo)
+ return _docmd1(_origcmd('parents'), ui, repo, filename, **opts)
+
+def _paths(cmd, ui, repo, search=None, **opts):
+ ui.status('[%s]:\n' % repo.root)
+ cmd(ui, repo, search)
+ for subtree in _subtreelist(ui, repo, opts):
+ ui.status('\n')
+ lr = hg.repository(ui, repo.wjoin(subtree))
+ _paths(cmd, lr.ui, lr, search, **opts)
+ return 0
+
+def paths(ui, repo, search=None, **opts):
+ '''show aliases for remote repositories'''
+ _checklocal(repo)
+ return _paths(_origcmd('paths'), ui, repo, search, **opts)
+
+def pull(ui, repo, remote="default", **opts):
+ '''pull changes from the specified source'''
+ _checklocal(repo)
+ adjust = remote and not ui.config('paths', remote)
+ st = opts.get('subtrees')
+ repocount = len(_list(ui, repo, st and {'subtrees': st} or {}))
+ rc = _docmd2(_origcmd('pull'), ui, repo, remote, adjust, **opts)
+ # Sadly, pull returns 1 if there was nothing to pull *or* if there are
+ # unresolved files on update. No way to distinguish between them.
+ # return 0 if any subtree pulled successfully.
+ return int(rc == repocount)
+
+def push(ui, repo, remote=None, **opts):
+ '''push changes to the specified destination'''
+ _checklocal(repo)
+ adjust = remote and not ui.config('paths', remote)
+ st = opts.get('subtrees')
+ repocount = len(_list(ui, repo, st and {'subtrees': st} or {}))
+ rc = _docmd2(_origcmd('push'), ui, repo, remote, adjust, **opts)
+ # return 0 if all pushes were successful; 1 if none of the repos had
+ # anything to push.
+ return int(rc == repocount)
+
+def status(ui, repo, *args, **opts):
+ '''show changed files in the working directory'''
+ _checklocal(repo)
+ return _docmd1(_origcmd('status'), ui, repo, *args, **opts)
+
+def summary(ui, repo, **opts):
+ """summarize working directory state"""
+ _checklocal(repo)
+ return _docmd1(_origcmd('summary'), ui, repo, **opts)
+
+def tag(ui, repo, name1, *names, **opts):
+ '''add one or more tags for the current or given revision'''
+ _checklocal(repo)
+ return _docmd1(_origcmd('tag'), ui, repo, name1, *names, **opts)
+
+def tip(ui, repo, **opts):
+ '''show the tip revision'''
+ _checklocal(repo)
+ return _docmd1(_origcmd('tip'), ui, repo, **opts)
+
+def _update(cmd, ui, repo, node=None, rev=None, clean=False, date=None,
+ check=False, **opts):
+ ui.status('[%s]:\n' % repo.root)
+ if _newupdate:
+ trc = cmd(ui, repo, node, rev, clean, date, check)
+ else:
+ trc = cmd(ui, repo, node, rev, clean, date)
+ rc = trc != None and trc or 0
+ for subtree in _subtreelist(ui, repo, opts):
+ ui.status('\n')
+ lr = hg.repository(ui, repo.wjoin(subtree))
+ trc = _update(cmd, lr.ui, lr, node, rev, clean, date, check, **opts)
+ rc += trc != None and trc or 0
+ return rc
+
+def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
+ **opts):
+ '''update working directory (or switch revisions)'''
+ _checklocal(repo)
+ rc = _update(_origcmd('update'), ui, repo, node, rev, clean, date, check,
+ **opts)
+ return rc and 1 or 0
+
+def version(ui, **opts):
+ '''show version information'''
+ ui.status('trees extension (version 0.7)\n')
+
+def defpath(ui, repo, peer=None, peer_push=None, **opts):
+ '''examine and manipulate default path settings for a tree.'''
+ def walker(r):
+ return _list(ui, r, opts)
+ return defpath_mod.defpath(ui, repo, peer, peer_push, walker, opts)
+
+def debugkeys(ui, src, **opts):
+ '''list the tree configuration using mercurial's pushkey mechanism.
+
+ This works for remote repositories as long as the remote hg server has the
+ trees extension enabled.'''
+ d = hg_repo(ui, src, opts).listkeys(_ns(ui, opts))
+ i = 0
+ n = len(d)
+ while i < n:
+ istr = str(i)
+ ui.write("%s: %s\n" % (istr, d[istr]))
+ i += 1
+ return 0
+
+# ----------------------------- mercurial linkage ------------------------------
+
+if not hasattr(hg, 'remoteui'):
+ if hasattr(cmdutil, 'remoteui'):
+ # hg < 1.5.4: remoteui is in cmdutil instead of hg
+ hg.remoteui = cmdutil.remoteui
+ else:
+ # hg < 1.3: no remoteui
+ def _remoteui(ui, opts):
+ cmdutil.setremoteconfig(ui, opts)
+ return ui
+ hg.remoteui = _remoteui
+
+# Tolerate changes to the signature of hg.clone().
+def compatible_clone():
+ clone_args = inspect.getargspec(hg.clone)[0]
+ if not 'branch' in clone_args:
+ # hg < 1.5: no 'branch' parameter (a78bfaf988e1)
+ def hg_clone(ui, peeropts, source, dest=None, pull=False, rev=None,
+ update=True, stream=False, branch=None):
+ rui = hg.remoteui(ui, peeropts)
+ return hg.clone(rui, source, dest, pull, rev, update, stream)
+ return hg_clone
+ if len(clone_args) < 9:
+ # hg < 1.9: no 'peeropts' parameter (d976542986d2, bd1acea552ff).
+ def hg_clone(ui, peeropts, source, dest=None, pull=False, rev=None,
+ update=True, stream=False, branch=None):
+ rui = hg.remoteui(ui, peeropts)
+ return hg.clone(rui, source, dest, pull, rev, update, stream,
+ branch)
+ return hg_clone
+ return hg.clone
+
+# hg < 2.3: no peer() method
+if getattr(hg, 'peer', None) is None:
+ hg.peer = lambda ui, opts, url: hg.repository(ui, url)
+
+_newupdate = len(inspect.getargspec(commands.update)[0]) >= 7
+namespaceopt = [('', 'tns', '',
+ _('trees namespace to use'),
+ _('NAMESPACE'))]
+subtreesopts = [('', 'subtrees', [],
+ _('path to subtree'),
+ _('SUBTREE'))] + namespaceopt
+
+if len(commands.globalopts[0]) < 5:
+ # hg < 1.5.4: arg description (5th tuple element) is not supported
+ def trimoptions(l):
+ i = 0
+ for opt in l:
+ l[i] = opt[:4]
+ i += 1
+ trimoptions(namespaceopt)
+ trimoptions(subtreesopts)
+
+walkopt = [('w', 'walk', False,
+ _('walk the filesystem to discover subtrees'))]
+
+cloneopts = [('', 'skiproot', False,
+ _('do not clone the root repo in the tree'))
+ ] + subtreesopts
+commandopts = [('', 'stop', False,
+ _('stop if command returns non-zero'))
+ ] + subtreesopts
+listopts = [('s', 'short', False,
+ _('list short paths (relative to repo root)'))
+ ] + walkopt + subtreesopts
+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')),
+ ('e', 'expand', False,
+ _('recursively expand config items in the [trees] section')),
+ ('l', 'list', False,
+ _('list the configured subtrees')),
+ ('', 'depth', False,
+ _('store subtree configuration depth-most')),
+ ('s', 'set', False, _('set the subtree config to SUBTREEs'))
+ ] + namespaceopt + walkopt
+
+def _newcte(origcmd, newfunc, extraopts = [], synopsis = None):
+ '''generate a cmdtable entry based on that for origcmd'''
+ cte = cmdutil.findcmd(origcmd, commands.table)[1]
+ # Filter out --exclude and --include, since those do not work across
+ # repositories (mercurial converts them to abs paths).
+ opts = [o for o in cte[1] if o[1] not in ('exclude', 'include')]
+ if len(cte) > 2:
+ return (newfunc, opts + extraopts, synopsis or cte[2])
+ return (newfunc, opts + extraopts, synopsis)
+
+def extsetup(ui = None):
+ # The cmdtable is initialized here to pick up options added by other
+ # extensions (e.g., rebase, bookmarks).
+ #
+ # Commands tagged with '^' are listed by 'hg help'.
+ global defpath_mod
+ defpath_mod = None
+ defpath_opts = []
+ try:
+ defpath_mod = extensions.find('defpath')
+ defpath_opts = __builtin__.list(defpath_mod.opts) + subtreesopts
+ defpath_doc = getattr(defpath_mod, 'common_docstring', '')
+ if defpath_doc:
+ defpath.__doc__ += defpath_doc
+ except:
+ pass
+
+ global cmdtable
+ cmdtable = {
+ '^tclone': _newcte('clone', clone, cloneopts,
+ _('[OPTION]... SOURCE [DEST [SUBTREE]...]')),
+ 'tcommand|tcmd': (command, commandopts, _('command [arg] ...')),
+ 'tcommit|tci': _newcte('commit', commit, subtreesopts),
+ 'tconfig': (config, configopts, _('[OPTION]... [SUBTREE]...')),
+ 'tdiff': _newcte('diff', diff, subtreesopts),
+ 'theads': _newcte('heads', heads, subtreesopts),
+ 'tincoming': _newcte('incoming', incoming, subtreesopts),
+ 'toutgoing': _newcte('outgoing', outgoing, subtreesopts),
+ 'tlist': (list, listopts, _('[OPTION]...')),
+ '^tlog|thistory': _newcte('log', log, subtreesopts),
+ 'tmerge': _newcte('merge', merge, subtreesopts),
+ 'tparents': _newcte('parents', parents, subtreesopts),
+ 'tpaths': _newcte('paths', paths, subtreesopts),
+ '^tpull': _newcte('pull', pull, subtreesopts),
+ '^tpush': _newcte('push', push, subtreesopts),
+ '^tstatus': _newcte('status', status, subtreesopts),
+ '^tupdate': _newcte('update', update, subtreesopts),
+ 'ttag': _newcte('tag', tag, subtreesopts),
+ 'ttip': _newcte('tip', tip, subtreesopts),
+ 'tversion': (version, [], ''),
+ 'tdebugkeys': (debugkeys, namespaceopt, '')
+ }
+ if defpath_mod:
+ cmdtable['tdefpath'] = (defpath, defpath_opts, _(''))
+ if getattr(commands, 'summary', None):
+ cmdtable['tsummary'] = _newcte('summary', summary, subtreesopts)
+
+commands.norepo += ' tclone tversion tdebugkeys'
+commands.optionalrepo += ' tconfig'
+
+def genlistkeys(namespace):
+ def _listkeys(repo):
+ # trees are ordered, so the keys are the non-negative integers.
+ d = {}
+ i = 0
+ try:
+ for line in repo.opener(namespace):
+ d[("%d" % i)] = line.rstrip('\n\r')
+ i += 1
+ return d
+ except:
+ return {}
+ return _listkeys
+
+def reposetup(ui, repo):
+ # Pushing keys is disabled; unclear whether/how it should work.
+ pushfunc = lambda *x: False
+ x = [_nsnormalize(s) for s in ui.configlist('trees', 'namespaces', [])]
+ try:
+ for ns in [_ns(ui, {})] + x:
+ pushkey.register(ns, pushfunc, genlistkeys(ns))
+ except exceptions.ImportError:
+ # hg < 1.6 - no pushkey.
+ def _listkeys(self, namespace):
+ # trees are ordered, so the keys are the non-negative integers.
+ d = {}
+ i = 0
+ try:
+ for line in self.opener(namespace):
+ d[("%d" % i)] = line.rstrip('\n\r')
+ i += 1
+ return d
+ except:
+ return {}
+ setattr(type(repo), 'listkeys', _listkeys)