comparison onsub.py @ 46:c8bf62a6eb21

from ???
author Henry S. Thompson <ht@inf.ed.ac.uk>
date Tue, 05 Jul 2022 10:23:32 +0100
parents
children
comparison
equal deleted inserted replaced
45:7d4da4e72d37 46:c8bf62a6eb21
1 # onsub.py - execute commands recursively on subrepositories
2 #
3 # Copyright 2010, 2011 aragost Trifork
4 #
5 # This software may be used and distributed according to the terms of
6 # the GNU General Public License version 2 or any later version.
7
8 import os
9 from mercurial.i18n import _
10 from mercurial import extensions, subrepo, util, registrar
11
12 """execute a command in each subrepository"""
13
14 cmdtable = {}
15 command = registrar.command(cmdtable)
16
17 @command(b'onsub',
18 [(b'b', b'breadth-first', None,
19 _(b'use breadth-first traversal')),
20 (b'p', b'post-order', None,
21 _(b'use post-order depth-first traversal')),
22 (b'', b'root-repo', None,
23 _(b'include root repository in traversal')),
24 (b'', b'max-depth', -1,
25 _(b'limit recursion to N levels (negative for no limit)'), b'N'),
26 (b'', b'ignore-errors', None,
27 _(b'continue execution despite errors')),
28 (b't', b'type', b'',
29 _(b'the type of repo to filter'), b'TYPE'),
30 (b'0', b'print0', None,
31 _(b'end subrepository names with NUL, for use with xargs'))],
32 _(b'[-b] [-0] [-t TYPE] [--ignore-errors] CMD [POST-CMD]'),
33 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
34 helpbasic=True
35 )
36 def onsub(ui, repo, *args, **opts):
37 """execute a command in each subrepository
38
39 Executes CMD with the current working directory set to the root of
40 each subrepository. By default, execution stops if CMD returns a
41 non-zero exit code. Use --ignore-errors to override this.
42
43 If a POST-CMD is specified, this will be executed after all
44 subrepositories below the current subrepository has been visited.
45 This corresponds to a post-order traversal of the tree.
46
47 It is an error to specify a POST-CMD together with the
48 --breadth-first flag.
49
50 Use --verbose/-v to print the command being run and the subrepo
51 name for each run of CMD in a subrepo. Alternately, use
52 --print0/-0 to print just the subrepo name followed by a NUL
53 character instead of a newline. This can be useful in combination
54 with :hg:`status --print0`.
55
56 The command has access to the following environment variables:
57
58 ``HG_REPO``:
59 Absolute path to the top-level repository in which the onsub
60 command was executed.
61
62 ``HG_SUBPATH``:
63 Relative path to the current subrepository from the top-level
64 repository.
65
66 ``HG_SUBURL``:
67 URL for the current subrepository as specified in the
68 containing repository's ``.hgsub`` file.
69
70 ``HG_SUBSTATE``:
71 State of the current subrepository as specified in the
72 containing repository's ``.hgsubstate`` file.
73
74 ``HG_SUBTYPE``:
75 The type of the current subrepository (hg, git or svn).
76 """
77
78 # function level "constants" - these won't be modified by the nested functions
79 print0 = opts.get('print0')
80 if opts.get('ignore_errors'):
81 onerr = None
82 else:
83 onerr = util.Abort
84 maxdepth = opts.get('max_depth')
85 precmd = None
86 postcmd = None
87 includeroot = opts.get('root_repo')
88 repotypefilter = opts.get('type')
89
90 def execCmd(sub, cmd, kind):
91 """if sub == None, cmd is executed inside repo; else, inside sub.
92 If cmd == None, do nothing. If cmd == '', do only the print0 (if needed).
93 Else, do either print0 or the debugging message, then execute the command.
94 kind is the type of the (sub)repo.
95 """
96 if sub == None:
97 envargdict = dict(HG_SUBPATH='.',
98 HG_SUBURL='.',
99 HG_SUBSTATE=repo['.'].hex(),
100 HG_REPO=repo.root,
101 HG_SUBTYPE=kind)
102 relpath = '.'
103 cmdwd = repo.root
104 else:
105 # subrepo.relpath was renamed to subrepo.subrelpath in
106 # 18b5b6392fcf.
107 if hasattr(subrepo, 'relpath'):
108 relpath = subrepo.relpath(sub)
109 else:
110 relpath = subrepo.subrelpath(sub)
111 envargdict = dict(HG_SUBPATH=relpath,
112 HG_SUBURL=sub._path,
113 HG_SUBSTATE=sub._state[1],
114 HG_REPO=repo.root,
115 HG_SUBTYPE=kind)
116 cmdwd = os.path.join(repo.root, relpath)
117 if cmd != None and (repotypefilter == '' or repotypefilter == kind):
118 if print0:
119 ui.write(relpath, "\0")
120 if cmd != '':
121 if not print0: ui.write(_("executing '%s' in %s\n") % (cmd, relpath))
122 util.system(cmd, environ=envargdict, cwd=cmdwd, onerr=onerr,
123 errprefix=_('terminated onsub in %s') % relpath)
124
125 def bfs():
126 """execute precmd in repo.root and in each subrepository, breadth-first"""
127 if includeroot:
128 execCmd(None, precmd, 'hg')
129 ctx = repo['.']
130 work = [(1, ctx.sub(subpath), ctx.substate[subpath][2]) for subpath in sorted(ctx.substate)]
131 while work:
132 (depth, sub, kind) = work.pop(0)
133 if depth > maxdepth >= 0:
134 continue
135 execCmd(sub, precmd, kind)
136 if kind == 'hg':
137 rev = sub._state[1]
138 ctx = sub._repo[rev]
139 w = [(depth + 1, ctx.sub(subpath), ctx.substate[subpath][2])
140 for subpath in sorted(ctx.substate)]
141 work.extend(w)
142
143 def dfs():
144 """execute pre-/postcmd in repo.root and in each subrepository, depth-first"""
145
146 def dfs_rek(depth, sub, kind):
147 if depth > maxdepth >= 0:
148 return
149 execCmd(sub, precmd, kind)
150 if kind == 'hg':
151 rev = sub._state[1]
152 ctx = sub._repo[rev]
153 for subpath in sorted(ctx.substate):
154 dfs_rek(depth+1, ctx.sub(subpath), ctx.substate[subpath][2])
155 execCmd(sub, postcmd, kind)
156
157 ctx = repo['.']
158 work = [(ctx.sub(subpath), ctx.substate[subpath][2]) for subpath in sorted(ctx.substate)]
159 if includeroot:
160 execCmd(None, precmd, 'hg')
161 for (sub, kind) in work:
162 dfs_rek(1, sub, kind)
163 if includeroot:
164 execCmd(None, postcmd, 'hg')
165
166 ### start of main function part ###
167 if len(args) == 2:
168 precmd = args[0]
169 postcmd = args[1]
170 if opts.get('breadth_first') or opts.get('post_order'):
171 raise util.Abort(_("onsub: '-b' and '-p' imply the use of only one command"))
172 elif len(args) == 1:
173 if opts.get('post_order'):
174 precmd = None
175 postcmd = args[0]
176 else:
177 precmd = args[0]
178 postcmd = None
179 elif len(args) == 0:
180 # cmd == '' means only do print0
181 if opts.get('post_order'):
182 precmd = None
183 postcmd = ''
184 else:
185 precmd = ''
186 postcmd = None
187 else:
188 raise util.Abort(_("onsub: at most 2 command arguments required"))
189 if opts.get('post_order') and opts.get('breadth_first'):
190 raise util.Abort(_("onsub: '-b' and '-p' are mutually exclusive"))
191
192 if opts.get('breadth_first'):
193 bfs()
194 else:
195 dfs()