[PATCH] foreach: new command for traversing subrepositories

Mads Kiilerich mads at kiilerich.com
Tue Aug 31 21:41:03 UTC 2010


  Martin Geisler roter, On 08/31/2010 04:52 PM:
> # HG changeset patch
> # User Martin Geisler<mg at lazybytes.net>
> # Date 1283266348 -7200
> # Node ID 3875e9ddeb830ab19b85efb6b823cb85c0e80e7a
> # Parent  36a65283c3afd6f957da54817b3b8c577aa78ec4
> foreach: new command for traversing subrepositories

The name is nice, but very generic and gives no hint that it deals with 
subrepos. Isn't that a problem?

> This should be a useful building block for people who make heavy use
> of subrepositories. Inspired by a Git command of the same name.

Can you give some relevant use cases?

hg foreach 'hg pull -u'
?

> diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
> --- a/mercurial/cmdutil.py
> +++ b/mercurial/cmdutil.py
> @@ -10,7 +10,7 @@
>   import os, sys, errno, re, glob, tempfile
>   import util, templater, patch, error, encoding, templatekw
>   import match as matchmod
> -import similar, revset
> +import similar, revset, subrepo
>
>   revrangesep = ':'
>
> @@ -1224,6 +1224,34 @@
>                   yield change(rev)
>       return iterate()
>
> +def foreach(ui, repo, cmd, depthfirst):
> +    """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)
> +
> +        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 foreach in %s') % relpath)

Termination on the first non-successful command isn't documented? Is 
that something we always want? I can imagine that it will confuse users 
more often than it will do what they want.

> +        w = sub.subrepos()
> +        if depthfirst:
> +            w.reverse()
> +        work.extend(w)
> +
>   def commit(ui, repo, commitfunc, pats, opts):
>       '''commit the specified files or all outstanding changes'''
>       date = opts.get('date')
> diff --git a/mercurial/commands.py b/mercurial/commands.py
> --- a/mercurial/commands.py
> +++ b/mercurial/commands.py
> @@ -1504,6 +1504,32 @@
>                    switch_parent=opts.get('switch_parent'),
>                    opts=patch.diffopts(ui, opts))
>
> +def foreach(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.

I think this is our first command that takes a shell command as unnamed 
parameter(s). Perhaps it should be made extra clear what the quoting 
rules are and how several command parameters are handled.

It seems like this works like the way ssh executes a command, where 
parameters to the command can be specified as separate parameters - but 
where parameters that requires quoting must be double quoted, and where 
it in non-trivial cases often is simplest to put everything in one big 
quotation. (sudo behaves differently and exec's the parameters directly.)

Perhaps it also would be helpful to clarify that it is a "shell command" 
(and thus not just an "executable") that can be specified. (That 
clarification might be useful in other help texts too.)

>   It has access to the following
> +    environment variables:
> +
> +    ``HG_REPO``:
> +        Absolute path to the top-level repository in which the foreach
> +        command was executed.
> +
> +    ``HG_SUBPATH``:
> +        Relative path to the current subrepository from the top-level
> +        repository.

Both variables points to a repo. But one is a REPO the other is a PATH?

HG_REPO is also a bit ambiguous as the subrepo also is a repo.

How about HG_TOPREPO/HG_SUBREPO? Do we have an "official" name for the 
"top, outermost repo"?

> +    ``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)
> +    cmdutil.foreach(ui, repo, cmd, not opts.get('breadth_first'))
> +

/Mads



More information about the Mercurial-devel mailing list