JakobKrainz avatar JakobKrainz committed 90b54c4

onsub extension: pre and post order

Comments (0)

Files changed (2)

         State of the current subrepository as specified in the
         containing repository's ``.hgsubstate`` file.
     """
-    cmd = ' '.join(args)
-    foreach(ui, repo, cmd, 
+    if len(args) == 2:
+        precmd = args[0]
+        postcmd = args[1]
+        if opts.get('breadth_first') or opts.get('post_order'):
+            raise util.Abort(_("onsub: '-b' and '-p' imply the use of only one command"))
+    elif len(args) == 1:
+        if opts.get('post_order'):
+            precmd = None
+            postcmd = args[0]
+        else:
+            precmd = args[0]
+            postcmd = None
+    elif len(args) == 0:
+        # cmd == '' means only do print0
+        if opts.get('post_order'):
+            precmd = None
+            postcmd = ''
+        else:
+            precmd = ''
+            postcmd = None
+    else:
+        raise util.Abort(_("onsub: at most 2 command arguments required"))
+    if opts.get('post_order') and opts.get('breadth_first'):
+        raise util.Abort(_("onsub: '-b' and '-p' are mutually exclusive"))
+    foreach(ui, repo, precmd, postcmd,
             not opts.get('breadth_first'),
             opts.get('max_depth'),
             opts.get('print0'),
-            opts.get('ignore_errors'))
+            opts.get('ignore_errors'),
+            opts.get('root_repo'))
 
-def foreach(ui, repo, cmd, depthfirst, maxdepth, print0, ignoreerrors):
-    """execute cmd in repo.root and in each subrepository"""
-    ctx = repo['.']
-    work = [(1, ctx.sub(subpath)) for subpath in sorted(ctx.substate)]
-    if depthfirst:
-        work.reverse()
-
-    while work:
-        if depthfirst:
-            (depth, sub) = work.pop()
-        else:
-            (depth, sub) = work.pop(0)
-        if depth > maxdepth >= 0:
-            continue
-
+def execCmd(ui, rootrepo, sub, doaction, print0, cmd, inroot, ignoreerrors):
+    """ if doaction, execute cmd; else, do nothing.  if inroot,
+    then command is executed inside rootrepo; else, inside sub
+    
+    If cmd == None, do nothing. If cmd == '', do only the print0 (if needed). 
+    Else, do either print0 or the debugging message, then execute the command.
+    """
+    if not doaction:
+        return
+    if inroot:
+        envargdict = dict(HG_SUBPATH='.',
+                          HG_SUBURL='.',
+                          HG_SUBSTATE=rootrepo['.'].hex(),
+                          HG_REPO=rootrepo.root)
+        relpath = '.'
+        cmdwd = rootrepo.root
+    else:
         # subrepo.relpath was renamed to subrepo.subrelpath in
         # 18b5b6392fcf.
         if hasattr(subrepo, 'relpath'):
             relpath = subrepo.relpath(sub)
         else:
             relpath = subrepo.subrelpath(sub)
-
+        envargdict = dict(HG_SUBPATH=relpath,
+                          HG_SUBURL=sub._path,
+                          HG_SUBSTATE=sub._state[1],
+                          HG_REPO=rootrepo.root)
+        cmdwd = os.path.join(rootrepo.root, relpath)
+    if ignoreerrors:
+        onerr = None
+    else:
+        onerr = util.Abort
+    if cmd != None:
         if print0:
             ui.write(relpath, "\0")
+        if cmd != '':
+            if not print0: ui.note(_("executing '%s' in %s\n") % (cmd, relpath))
+            util.system(cmd, environ=envargdict, cwd=cmdwd, onerr=onerr,
+                        errprefix=_('terminated onsub in %s') % relpath)
+    
+
+
+def foreach(ui, repo, precmd, postcmd, depthfirst, maxdepth, print0, ignoreerrors, includeroot):
+    """execute (pre/post)cmd in repo.root and in each subrepository"""
+    seen = set()
+    execCmd(ui=ui, rootrepo=repo, sub=None, doaction=includeroot, 
+            print0=print0, cmd=precmd, inroot=True, ignoreerrors=ignoreerrors) 
+    ctx = repo['.']
+    work = [(1, ctx.sub(subpath)) for subpath in sorted(ctx.substate)]
+    if depthfirst:
+        work.reverse()
+    while work:
+        if depthfirst:
+            (depth, sub) = work[-1]
+            if sub in seen:
+                dopreaction = False
+                dopostaction = True
+                doaddchildren = False
+                work.pop()
+            else:
+                dopreaction = True
+                dopostaction = False
+                doaddchildren = True
+                seen.add(sub)
         else:
-            ui.note(_("executing '%s' in %s\n") % (cmd, relpath))
-        if ignoreerrors:
-            onerr = None
-        else:
-            onerr = util.Abort
-        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=onerr,
-                    errprefix=_('terminated onsub in %s') % relpath)
-
-        if isinstance(sub, subrepo.hgsubrepo):
-            rev = sub._state[1]
-            ctx = sub._repo[rev]
-            w = [(depth + 1, ctx.sub(subpath)) 
-                 for subpath in sorted(ctx.substate)]
-            if depthfirst:
-                w.reverse()
-            work.extend(w)
-
+            (depth, sub) = work.pop(0)
+            dopreaction = True
+            dopostaction = False
+            doaddchildren = True
+        if depth > maxdepth >= 0:
+            continue
+        execCmd(ui=ui, rootrepo=repo, sub=sub, doaction=dopreaction, 
+                print0=print0, cmd=precmd, inroot=False, ignoreerrors=ignoreerrors) 
+        if doaddchildren:
+            if isinstance(sub, subrepo.hgsubrepo):
+                rev = sub._state[1]
+                ctx = sub._repo[rev]
+                w = [(depth + 1, ctx.sub(subpath)) 
+                     for subpath in sorted(ctx.substate)]
+                if depthfirst:
+                    w.reverse()
+                work.extend(w)
+        execCmd(ui=ui, rootrepo=repo, sub=sub, doaction=dopostaction, 
+                print0=print0, cmd=postcmd, inroot=False, ignoreerrors=ignoreerrors) 
+    execCmd(ui=ui, rootrepo=repo, sub=None, doaction=includeroot, 
+            print0=print0, cmd=postcmd, inroot=True, ignoreerrors=ignoreerrors) 
+            
 cmdtable = {
     "onsub":
         (onsub,
          [('b', 'breadth-first', None,
            _('use breadth-first traversal')),
+          ('p', 'post-order', None,
+           _('use post-order depth-first traversal')),
+          ('', 'root-repo', None,
+           _('include root repository in traversal')),
           ('', 'max-depth', -1,
            _('limit recursion to N levels (negative for no limit)'), 'N'),
           ('', 'ignore-errors', None,
   
   options:
   
-   -b --breadth-first  use breadth-first traversal
-      --max-depth N    limit recursion to N levels (negative for no limit)
-                       (default: -1)
-      --ignore-errors  continue execution despite errors
-   -0 --print0         end subrepository names with NUL, for use with xargs
+   -b --breadth-first use breadth-first traversal
+   -p --post-order    use post-order depth-first traversal
+      --root-repo     include root repository in traversal
+      --max-depth N   limit recursion to N levels (negative for no limit)
+                      (default: -1)
+      --ignore-errors continue execution despite errors
+   -0 --print0        end subrepository names with NUL, for use with xargs
   
   use "hg -v help onsub" to show global options
 
   committing subrepository b/u
   committing subrepository b/v
 
-The default depth-first traversal:
+The default depth-first pre-order traversal:
 
   $ hg onsub 'echo $HG_SUBPATH'
   a
   b/u
   b/v
 
+Traversal including the root repository:
+
+  $ hg onsub 'echo $HG_SUBPATH' --root-repo
+  .
+  a
+  a/x
+  a/y
+  a/y/r
+  a/y/s
+  a/y/t
+  b
+  b/u
+  b/v
+
+Depth-first post-order traversal: 
+
+  $ hg onsub 'echo $HG_SUBPATH' --post-order
+  a/x
+  a/y/r
+  a/y/s
+  a/y/t
+  a/y
+  a
+  b/u
+  b/v
+  b
+
+Depth-first pre- and post-order traversal:
+
+  $ hg onsub 'echo pre $HG_SUBPATH'  'echo post $HG_SUBPATH'
+  pre a
+  pre a/x
+  post a/x
+  pre a/y
+  pre a/y/r
+  post a/y/r
+  pre a/y/s
+  post a/y/s
+  pre a/y/t
+  post a/y/t
+  post a/y
+  post a
+  pre b
+  pre b/u
+  post b/u
+  pre b/v
+  post b/v
+  post b
+
 Breadth-first traversal:
 
   $ hg onsub 'echo $HG_SUBPATH' --breadth-first
   a/y
   b/u
   b/v
+  $ hg onsub --max-depth 1 -b --root-repo 'echo $HG_SUBPATH'
+  .
+  a
+  b
 
 Test aborting:
 
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.