Mercurial > hg > python
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() |