Show More
@@ -1181,6 +1181,9 b" coreconfigitem('ui', 'mergemarkertemplat" | |||
|
1181 | 1181 | '{ifeq(branch, "default", "", "{branch} ")}' |
|
1182 | 1182 | '- {author|user}: {desc|firstline}') |
|
1183 | 1183 | ) |
|
1184 | coreconfigitem('ui', 'message-output', | |
|
1185 | default='stdio', | |
|
1186 | ) | |
|
1184 | 1187 | coreconfigitem('ui', 'nontty', |
|
1185 | 1188 | default=False, |
|
1186 | 1189 | ) |
@@ -2246,6 +2246,14 b' User interface controls.' | |||
|
2246 | 2246 | |
|
2247 | 2247 | Can be overridden per-merge-tool, see the ``[merge-tools]`` section. |
|
2248 | 2248 | |
|
2249 | ``message-output`` | |
|
2250 | Where to write status and error messages. (default: ``stdio``) | |
|
2251 | ||
|
2252 | ``stderr`` | |
|
2253 | Everything to stderr. | |
|
2254 | ``stdio`` | |
|
2255 | Status to stdout, and error to stderr. | |
|
2256 | ||
|
2249 | 2257 | ``origbackuppath`` |
|
2250 | 2258 | The path to a directory used to store generated .orig files. If the path is |
|
2251 | 2259 | not a directory, one will be created. If set, files stored in this |
@@ -234,6 +234,8 b' class ui(object):' | |||
|
234 | 234 | self._fout = src._fout |
|
235 | 235 | self._ferr = src._ferr |
|
236 | 236 | self._fin = src._fin |
|
237 | self._fmsgout = src._fmsgout | |
|
238 | self._fmsgerr = src._fmsgerr | |
|
237 | 239 | self._finoutredirected = src._finoutredirected |
|
238 | 240 | self.pageractive = src.pageractive |
|
239 | 241 | self._disablepager = src._disablepager |
@@ -259,6 +261,8 b' class ui(object):' | |||
|
259 | 261 | self._fout = procutil.stdout |
|
260 | 262 | self._ferr = procutil.stderr |
|
261 | 263 | self._fin = procutil.stdin |
|
264 | self._fmsgout = self.fout # configurable | |
|
265 | self._fmsgerr = self.ferr # configurable | |
|
262 | 266 | self._finoutredirected = False |
|
263 | 267 | self.pageractive = False |
|
264 | 268 | self._disablepager = False |
@@ -416,7 +420,7 b' class ui(object):' | |||
|
416 | 420 | |
|
417 | 421 | if self.plain(): |
|
418 | 422 | for k in ('debug', 'fallbackencoding', 'quiet', 'slash', |
|
419 | 'logtemplate', 'statuscopies', 'style', | |
|
423 | 'logtemplate', 'message-output', 'statuscopies', 'style', | |
|
420 | 424 | 'traceback', 'verbose'): |
|
421 | 425 | if k in cfg['ui']: |
|
422 | 426 | del cfg['ui'][k] |
@@ -469,6 +473,7 b' class ui(object):' | |||
|
469 | 473 | |
|
470 | 474 | if section in (None, 'ui'): |
|
471 | 475 | # update ui options |
|
476 | self._fmsgout, self._fmsgerr = _selectmsgdests(self) | |
|
472 | 477 | self.debugflag = self.configbool('ui', 'debug') |
|
473 | 478 | self.verbose = self.debugflag or self.configbool('ui', 'verbose') |
|
474 | 479 | self.quiet = not self.debugflag and self.configbool('ui', 'quiet') |
@@ -891,6 +896,7 b' class ui(object):' | |||
|
891 | 896 | @fout.setter |
|
892 | 897 | def fout(self, f): |
|
893 | 898 | self._fout = f |
|
899 | self._fmsgout, self._fmsgerr = _selectmsgdests(self) | |
|
894 | 900 | |
|
895 | 901 | @property |
|
896 | 902 | def ferr(self): |
@@ -899,6 +905,7 b' class ui(object):' | |||
|
899 | 905 | @ferr.setter |
|
900 | 906 | def ferr(self, f): |
|
901 | 907 | self._ferr = f |
|
908 | self._fmsgout, self._fmsgerr = _selectmsgdests(self) | |
|
902 | 909 | |
|
903 | 910 | @property |
|
904 | 911 | def fin(self): |
@@ -1364,17 +1371,18 b' class ui(object):' | |||
|
1364 | 1371 | If ui is not interactive, the default is returned. |
|
1365 | 1372 | """ |
|
1366 | 1373 | if not self.interactive(): |
|
1367 | self.write(msg, ' ', label='ui.prompt') | |
|
1368 |
self.write(default or '', "\n", |
|
|
1374 | self._write(self._fmsgout, msg, ' ', label='ui.prompt') | |
|
1375 | self._write(self._fmsgout, default or '', "\n", | |
|
1376 | label='ui.promptecho') | |
|
1369 | 1377 | return default |
|
1370 | self._writenobuf(self._fout, msg, label='ui.prompt') | |
|
1378 | self._writenobuf(self._fmsgout, msg, label='ui.prompt') | |
|
1371 | 1379 | self.flush() |
|
1372 | 1380 | try: |
|
1373 | 1381 | r = self._readline() |
|
1374 | 1382 | if not r: |
|
1375 | 1383 | r = default |
|
1376 | 1384 | if self.configbool('ui', 'promptecho'): |
|
1377 | self.write(r, "\n", label='ui.promptecho') | |
|
1385 | self._write(self._fmsgout, r, "\n", label='ui.promptecho') | |
|
1378 | 1386 | return r |
|
1379 | 1387 | except EOFError: |
|
1380 | 1388 | raise error.ResponseExpected() |
@@ -1424,13 +1432,15 b' class ui(object):' | |||
|
1424 | 1432 | r = self.prompt(msg, resps[default]) |
|
1425 | 1433 | if r.lower() in resps: |
|
1426 | 1434 | return resps.index(r.lower()) |
|
1427 | self.write(_("unrecognized response\n")) | |
|
1435 | # TODO: shouldn't it be a warning? | |
|
1436 | self._write(self._fmsgout, _("unrecognized response\n")) | |
|
1428 | 1437 | |
|
1429 | 1438 | def getpass(self, prompt=None, default=None): |
|
1430 | 1439 | if not self.interactive(): |
|
1431 | 1440 | return default |
|
1432 | 1441 | try: |
|
1433 |
self.write |
|
|
1442 | self._write(self._fmsgerr, prompt or _('password: '), | |
|
1443 | label='ui.prompt') | |
|
1434 | 1444 | # disable getpass() only if explicitly specified. it's still valid |
|
1435 | 1445 | # to interact with tty even if fin is not a tty. |
|
1436 | 1446 | with self.timeblockedsection('stdio'): |
@@ -1451,7 +1461,7 b' class ui(object):' | |||
|
1451 | 1461 | ''' |
|
1452 | 1462 | if not self.quiet: |
|
1453 | 1463 | opts[r'label'] = opts.get(r'label', '') + ' ui.status' |
|
1454 | self.write(*msg, **opts) | |
|
1464 | self._write(self._fmsgout, *msg, **opts) | |
|
1455 | 1465 | |
|
1456 | 1466 | def warn(self, *msg, **opts): |
|
1457 | 1467 | '''write warning message to output (stderr) |
@@ -1459,7 +1469,7 b' class ui(object):' | |||
|
1459 | 1469 | This adds an output label of "ui.warning". |
|
1460 | 1470 | ''' |
|
1461 | 1471 | opts[r'label'] = opts.get(r'label', '') + ' ui.warning' |
|
1462 |
self.write |
|
|
1472 | self._write(self._fmsgerr, *msg, **opts) | |
|
1463 | 1473 | |
|
1464 | 1474 | def error(self, *msg, **opts): |
|
1465 | 1475 | '''write error message to output (stderr) |
@@ -1467,7 +1477,7 b' class ui(object):' | |||
|
1467 | 1477 | This adds an output label of "ui.error". |
|
1468 | 1478 | ''' |
|
1469 | 1479 | opts[r'label'] = opts.get(r'label', '') + ' ui.error' |
|
1470 |
self.write |
|
|
1480 | self._write(self._fmsgerr, *msg, **opts) | |
|
1471 | 1481 | |
|
1472 | 1482 | def note(self, *msg, **opts): |
|
1473 | 1483 | '''write note to output (if ui.verbose is True) |
@@ -1476,7 +1486,7 b' class ui(object):' | |||
|
1476 | 1486 | ''' |
|
1477 | 1487 | if self.verbose: |
|
1478 | 1488 | opts[r'label'] = opts.get(r'label', '') + ' ui.note' |
|
1479 | self.write(*msg, **opts) | |
|
1489 | self._write(self._fmsgout, *msg, **opts) | |
|
1480 | 1490 | |
|
1481 | 1491 | def debug(self, *msg, **opts): |
|
1482 | 1492 | '''write debug message to output (if ui.debugflag is True) |
@@ -1485,7 +1495,7 b' class ui(object):' | |||
|
1485 | 1495 | ''' |
|
1486 | 1496 | if self.debugflag: |
|
1487 | 1497 | opts[r'label'] = opts.get(r'label', '') + ' ui.debug' |
|
1488 | self.write(*msg, **opts) | |
|
1498 | self._write(self._fmsgout, *msg, **opts) | |
|
1489 | 1499 | |
|
1490 | 1500 | def edit(self, text, user, extra=None, editform=None, pending=None, |
|
1491 | 1501 | repopath=None, action=None): |
@@ -1939,3 +1949,11 b' def getprogbar(ui):' | |||
|
1939 | 1949 | |
|
1940 | 1950 | def haveprogbar(): |
|
1941 | 1951 | return _progresssingleton is not None |
|
1952 | ||
|
1953 | def _selectmsgdests(ui): | |
|
1954 | name = ui.config(b'ui', b'message-output') | |
|
1955 | if name == b'stdio': | |
|
1956 | return ui.fout, ui.ferr | |
|
1957 | if name == b'stderr': | |
|
1958 | return ui.ferr, ui.ferr | |
|
1959 | raise error.Abort(b'invalid ui.message-output destination: %s' % name) |
@@ -102,3 +102,118 b' Repository root:' | |||
|
102 | 102 | At the end... |
|
103 | 103 | |
|
104 | 104 | $ cd .. |
|
105 | ||
|
106 | Status message redirection: | |
|
107 | ||
|
108 | $ hg init empty | |
|
109 | ||
|
110 | status messages are sent to stdout by default: | |
|
111 | ||
|
112 | $ hg outgoing -R t empty -Tjson 2>/dev/null | |
|
113 | comparing with empty | |
|
114 | searching for changes | |
|
115 | [ | |
|
116 | { | |
|
117 | "bookmarks": [], | |
|
118 | "branch": "default", | |
|
119 | "date": [0, 0], | |
|
120 | "desc": "test", | |
|
121 | "node": "acb14030fe0a21b60322c440ad2d20cf7685a376", | |
|
122 | "parents": ["0000000000000000000000000000000000000000"], | |
|
123 | "phase": "draft", | |
|
124 | "rev": 0, | |
|
125 | "tags": ["tip"], | |
|
126 | "user": "test" | |
|
127 | } | |
|
128 | ] | |
|
129 | ||
|
130 | which can be configured to send to stderr, so the output wouldn't be | |
|
131 | interleaved: | |
|
132 | ||
|
133 | $ cat <<'EOF' >> "$HGRCPATH" | |
|
134 | > [ui] | |
|
135 | > message-output = stderr | |
|
136 | > EOF | |
|
137 | $ hg outgoing -R t empty -Tjson 2>/dev/null | |
|
138 | [ | |
|
139 | { | |
|
140 | "bookmarks": [], | |
|
141 | "branch": "default", | |
|
142 | "date": [0, 0], | |
|
143 | "desc": "test", | |
|
144 | "node": "acb14030fe0a21b60322c440ad2d20cf7685a376", | |
|
145 | "parents": ["0000000000000000000000000000000000000000"], | |
|
146 | "phase": "draft", | |
|
147 | "rev": 0, | |
|
148 | "tags": ["tip"], | |
|
149 | "user": "test" | |
|
150 | } | |
|
151 | ] | |
|
152 | $ hg outgoing -R t empty -Tjson >/dev/null | |
|
153 | comparing with empty | |
|
154 | searching for changes | |
|
155 | ||
|
156 | this option should be turned off by HGPLAIN= since it may break scripting use: | |
|
157 | ||
|
158 | $ HGPLAIN= hg outgoing -R t empty -Tjson 2>/dev/null | |
|
159 | comparing with empty | |
|
160 | searching for changes | |
|
161 | [ | |
|
162 | { | |
|
163 | "bookmarks": [], | |
|
164 | "branch": "default", | |
|
165 | "date": [0, 0], | |
|
166 | "desc": "test", | |
|
167 | "node": "acb14030fe0a21b60322c440ad2d20cf7685a376", | |
|
168 | "parents": ["0000000000000000000000000000000000000000"], | |
|
169 | "phase": "draft", | |
|
170 | "rev": 0, | |
|
171 | "tags": ["tip"], | |
|
172 | "user": "test" | |
|
173 | } | |
|
174 | ] | |
|
175 | ||
|
176 | but still overridden by --config: | |
|
177 | ||
|
178 | $ HGPLAIN= hg outgoing -R t empty -Tjson --config ui.message-output=stderr \ | |
|
179 | > 2>/dev/null | |
|
180 | [ | |
|
181 | { | |
|
182 | "bookmarks": [], | |
|
183 | "branch": "default", | |
|
184 | "date": [0, 0], | |
|
185 | "desc": "test", | |
|
186 | "node": "acb14030fe0a21b60322c440ad2d20cf7685a376", | |
|
187 | "parents": ["0000000000000000000000000000000000000000"], | |
|
188 | "phase": "draft", | |
|
189 | "rev": 0, | |
|
190 | "tags": ["tip"], | |
|
191 | "user": "test" | |
|
192 | } | |
|
193 | ] | |
|
194 | ||
|
195 | Invalid ui.message-output option: | |
|
196 | ||
|
197 | $ hg log -R t --config ui.message-output=bad | |
|
198 | abort: invalid ui.message-output destination: bad | |
|
199 | [255] | |
|
200 | ||
|
201 | Underlying message streams should be updated when ui.fout/ferr are set: | |
|
202 | ||
|
203 | $ cat <<'EOF' > capui.py | |
|
204 | > from mercurial import pycompat, registrar | |
|
205 | > cmdtable = {} | |
|
206 | > command = registrar.command(cmdtable) | |
|
207 | > @command(b'capui', norepo=True) | |
|
208 | > def capui(ui): | |
|
209 | > out = ui.fout | |
|
210 | > ui.fout = pycompat.bytesio() | |
|
211 | > ui.status(b'status\n') | |
|
212 | > ui.ferr = pycompat.bytesio() | |
|
213 | > ui.warn(b'warn\n') | |
|
214 | > out.write(b'stdout: %s' % ui.fout.getvalue()) | |
|
215 | > out.write(b'stderr: %s' % ui.ferr.getvalue()) | |
|
216 | > EOF | |
|
217 | $ hg --config extensions.capui=capui.py --config ui.message-output=stdio capui | |
|
218 | stdout: status | |
|
219 | stderr: warn |
General Comments 0
You need to be logged in to leave comments.
Login now