Commits

Martin Geisler  committed e49f3bb

Initial version.

  • Participants

Comments (0)

Files changed (3)

+syntax: glob
+*~
+*.pyc
+
+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'))
+}

File test-onsub.t

+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