Mercurial > hg-onsub
changeset 0:e49f3bbfec4d
Initial version.
author | Martin Geisler <mg@aragost.com> |
---|---|
date | Mon, 06 Sep 2010 12:09:47 +0200 |
parents | |
children | 0a9d6bf5874e |
files | .hgignore onsub.py test-onsub.t |
diffstat | 3 files changed, 218 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgignore Mon Sep 06 12:09:47 2010 +0200 @@ -0,0 +1,3 @@ +syntax: glob +*~ +*.pyc
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/onsub.py Mon Sep 06 12:09:47 2010 +0200 @@ -0,0 +1,83 @@ + +import os +from mercurial.i18n import _ +from mercurial import extensions, subrepo, util + +"""execute a command in each subrepository""" + +def onsub(ui, repo, *args, **opts): + """execute a command in each subrepository + + The command is executed with the current working directory set to + the root of each subrepository. By default, execution stops if the + command returns a non-zero exit code. Use --ignore-errors to + override this. + + Use --verbose/-v to print the name of each subrepo before the + command is executed, use --print0/-0 to terminate this line with a + NUL character instead of a newline. This can for instance be + useful in combination with :hg:`status --print0`. + + The command has access to the following environment variables: + + ``HG_REPO``: + Absolute path to the top-level repository in which the onsub + command was executed. + + ``HG_SUBPATH``: + Relative path to the current subrepository from the top-level + repository. + + ``HG_SUBURL``: + URL for the current subrepository as specified in the + containing repository's ``.hgsub`` file. + + ``HG_SUBSTATE``: + State of the current subrepository as specified in the + containing repository's ``.hgsubstate`` file. + """ + cmd = ' '.join(args) + foreach(ui, repo, cmd, not opts.get('breadth_first'), opts.get('print0')) + +def foreach(ui, repo, cmd, depthfirst, print0): + """execute cmd in repo.root and in each subrepository""" + ctx = repo['.'] + work = [ctx.sub(subpath) for subpath in sorted(ctx.substate)] + if depthfirst: + work.reverse() + + while work: + if depthfirst: + sub = work.pop() + else: + sub = work.pop(0) + + relpath = subrepo.relpath(sub) + if print0: + ui.write(relpath, "\0") + else: + ui.note(_("executing '%s' in %s\n") % (cmd, relpath)) + util.system(cmd, environ=dict(HG_SUBPATH=relpath, + HG_SUBURL=sub._path, + HG_SUBSTATE=sub._state[1], + HG_REPO=repo.root), + cwd=os.path.join(repo.root, relpath), onerr=util.Abort, + errprefix=_('terminated onsub in %s') % relpath) + + if isinstance(sub, subrepo.hgsubrepo): + rev = sub._state[1] + ctx = sub._repo[rev] + w = [ctx.sub(subpath) for subpath in sorted(ctx.substate)] + if depthfirst: + w.reverse() + work.extend(w) + +cmdtable = { + "onsub": + (onsub, + [('b', 'breadth-first', None, + _('use breadth-first traversal')), + ('0', 'print0', None, + _('end subrepository names with NUL, for use with xargs'))], + _('[-b] [-0] CMD')) +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-onsub.t Mon Sep 06 12:09:47 2010 +0200 @@ -0,0 +1,132 @@ +Load extension: + + $ echo "[extensions]" >> $HGRCPATH + $ echo "onsub = $TESTDIR/onsub.py" >> $HGRCPATH + +Check help formatting: + + $ hg help onsub + hg onsub [-b] [-0] CMD + + execute a command in each subrepository + + The command is executed with the current working directory set to the root + of each subrepository. By default, execution stops if the command returns + a non-zero exit code. Use --ignore-errors to override this. + + Use --verbose/-v to print the name of each subrepo before the command is + executed, use --print0/-0 to terminate this line with a NUL character + instead of a newline. This can for instance be useful in combination with + "hg status --print0". + + The command has access to the following environment variables: + + "HG_REPO": + Absolute path to the top-level repository in which the onsub command + was executed. + + "HG_SUBPATH": + Relative path to the current subrepository from the top-level + repository. + + "HG_SUBURL": + URL for the current subrepository as specified in the containing + repository's ".hgsub" file. + + "HG_SUBSTATE": + State of the current subrepository as specified in the containing + repository's ".hgsubstate" file. + + options: + + -b --breadth-first use breadth-first traversal + -0 --print0 end subrepository names with NUL, for use with xargs + + use "hg -v help onsub" to show global options + +Create some nicely nested subrepositories: + + $ hg init + $ for d in a b; do hg init $d; echo "$d = $d" >> .hgsub; done + $ hg add .hgsub + + $ cd a + + $ for d in x y; do hg init $d; echo "$d = $d" >> .hgsub; done + $ hg add .hgsub + + $ cd y + $ for d in r s t; do hg init $d; echo "$d = $d" >> .hgsub; done + $ hg add .hgsub + $ cd .. + + $ cd .. + + $ cd b + $ for d in u v; do hg init $d; echo "$d = $d" >> .hgsub; done + $ hg add .hgsub + $ cd .. + + $ hg commit -m init + committing subrepository a + committing subrepository a/x + committing subrepository a/y + committing subrepository a/y/r + committing subrepository a/y/s + committing subrepository a/y/t + committing subrepository b + committing subrepository b/u + committing subrepository b/v + +The default depth-first traversal: + + $ hg onsub 'echo $HG_SUBPATH' + a + a/x + a/y + a/y/r + a/y/s + a/y/t + b + b/u + b/v + +Breadth-first traversal: + + $ hg onsub 'echo $HG_SUBPATH' --breadth-first + a + b + a/x + a/y + b/u + b/v + a/y/r + a/y/s + a/y/t + +Test aborting: + + $ hg onsub -v 'test $HG_SUBPATH != "a/y/r"' + executing 'test $HG_SUBPATH != "a/y/r"' in a + executing 'test $HG_SUBPATH != "a/y/r"' in a/x + executing 'test $HG_SUBPATH != "a/y/r"' in a/y + executing 'test $HG_SUBPATH != "a/y/r"' in a/y/r + abort: terminated onsub in a/y/r: test exited with status 1 + +Test --print0: + + $ mv a 'with spaces' + $ echo 'with spaces = with spaces' > .hgsub + $ echo 'b = b' >> .hgsub + $ hg commit -m rename + committing subrepository with spaces + $ hg onsub -0 | xargs -n 1 -0 + b + b/u + b/v + with spaces + with spaces/x + with spaces/y + with spaces/y/r + with spaces/y/s + with spaces/y/t