##// END OF EJS Templates
color: move triggering of the initialisation logic in core...
Pierre-Yves David -
r31105:45be7590 default
parent child Browse files
Show More
@@ -1,252 +1,240
1 # color.py color output for Mercurial commands
1 # color.py color output for Mercurial commands
2 #
2 #
3 # Copyright (C) 2007 Kevin Christen <kevin.christen@gmail.com>
3 # Copyright (C) 2007 Kevin Christen <kevin.christen@gmail.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 '''colorize output from some commands
8 '''colorize output from some commands
9
9
10 The color extension colorizes output from several Mercurial commands.
10 The color extension colorizes output from several Mercurial commands.
11 For example, the diff command shows additions in green and deletions
11 For example, the diff command shows additions in green and deletions
12 in red, while the status command shows modified files in magenta. Many
12 in red, while the status command shows modified files in magenta. Many
13 other commands have analogous colors. It is possible to customize
13 other commands have analogous colors. It is possible to customize
14 these colors.
14 these colors.
15
15
16 Effects
16 Effects
17 -------
17 -------
18
18
19 Other effects in addition to color, like bold and underlined text, are
19 Other effects in addition to color, like bold and underlined text, are
20 also available. By default, the terminfo database is used to find the
20 also available. By default, the terminfo database is used to find the
21 terminal codes used to change color and effect. If terminfo is not
21 terminal codes used to change color and effect. If terminfo is not
22 available, then effects are rendered with the ECMA-48 SGR control
22 available, then effects are rendered with the ECMA-48 SGR control
23 function (aka ANSI escape codes).
23 function (aka ANSI escape codes).
24
24
25 The available effects in terminfo mode are 'blink', 'bold', 'dim',
25 The available effects in terminfo mode are 'blink', 'bold', 'dim',
26 'inverse', 'invisible', 'italic', 'standout', and 'underline'; in
26 'inverse', 'invisible', 'italic', 'standout', and 'underline'; in
27 ECMA-48 mode, the options are 'bold', 'inverse', 'italic', and
27 ECMA-48 mode, the options are 'bold', 'inverse', 'italic', and
28 'underline'. How each is rendered depends on the terminal emulator.
28 'underline'. How each is rendered depends on the terminal emulator.
29 Some may not be available for a given terminal type, and will be
29 Some may not be available for a given terminal type, and will be
30 silently ignored.
30 silently ignored.
31
31
32 If the terminfo entry for your terminal is missing codes for an effect
32 If the terminfo entry for your terminal is missing codes for an effect
33 or has the wrong codes, you can add or override those codes in your
33 or has the wrong codes, you can add or override those codes in your
34 configuration::
34 configuration::
35
35
36 [color]
36 [color]
37 terminfo.dim = \E[2m
37 terminfo.dim = \E[2m
38
38
39 where '\E' is substituted with an escape character.
39 where '\E' is substituted with an escape character.
40
40
41 Labels
41 Labels
42 ------
42 ------
43
43
44 Text receives color effects depending on the labels that it has. Many
44 Text receives color effects depending on the labels that it has. Many
45 default Mercurial commands emit labelled text. You can also define
45 default Mercurial commands emit labelled text. You can also define
46 your own labels in templates using the label function, see :hg:`help
46 your own labels in templates using the label function, see :hg:`help
47 templates`. A single portion of text may have more than one label. In
47 templates`. A single portion of text may have more than one label. In
48 that case, effects given to the last label will override any other
48 that case, effects given to the last label will override any other
49 effects. This includes the special "none" effect, which nullifies
49 effects. This includes the special "none" effect, which nullifies
50 other effects.
50 other effects.
51
51
52 Labels are normally invisible. In order to see these labels and their
52 Labels are normally invisible. In order to see these labels and their
53 position in the text, use the global --color=debug option. The same
53 position in the text, use the global --color=debug option. The same
54 anchor text may be associated to multiple labels, e.g.
54 anchor text may be associated to multiple labels, e.g.
55
55
56 [log.changeset changeset.secret|changeset: 22611:6f0a53c8f587]
56 [log.changeset changeset.secret|changeset: 22611:6f0a53c8f587]
57
57
58 The following are the default effects for some default labels. Default
58 The following are the default effects for some default labels. Default
59 effects may be overridden from your configuration file::
59 effects may be overridden from your configuration file::
60
60
61 [color]
61 [color]
62 status.modified = blue bold underline red_background
62 status.modified = blue bold underline red_background
63 status.added = green bold
63 status.added = green bold
64 status.removed = red bold blue_background
64 status.removed = red bold blue_background
65 status.deleted = cyan bold underline
65 status.deleted = cyan bold underline
66 status.unknown = magenta bold underline
66 status.unknown = magenta bold underline
67 status.ignored = black bold
67 status.ignored = black bold
68
68
69 # 'none' turns off all effects
69 # 'none' turns off all effects
70 status.clean = none
70 status.clean = none
71 status.copied = none
71 status.copied = none
72
72
73 qseries.applied = blue bold underline
73 qseries.applied = blue bold underline
74 qseries.unapplied = black bold
74 qseries.unapplied = black bold
75 qseries.missing = red bold
75 qseries.missing = red bold
76
76
77 diff.diffline = bold
77 diff.diffline = bold
78 diff.extended = cyan bold
78 diff.extended = cyan bold
79 diff.file_a = red bold
79 diff.file_a = red bold
80 diff.file_b = green bold
80 diff.file_b = green bold
81 diff.hunk = magenta
81 diff.hunk = magenta
82 diff.deleted = red
82 diff.deleted = red
83 diff.inserted = green
83 diff.inserted = green
84 diff.changed = white
84 diff.changed = white
85 diff.tab =
85 diff.tab =
86 diff.trailingwhitespace = bold red_background
86 diff.trailingwhitespace = bold red_background
87
87
88 # Blank so it inherits the style of the surrounding label
88 # Blank so it inherits the style of the surrounding label
89 changeset.public =
89 changeset.public =
90 changeset.draft =
90 changeset.draft =
91 changeset.secret =
91 changeset.secret =
92
92
93 resolve.unresolved = red bold
93 resolve.unresolved = red bold
94 resolve.resolved = green bold
94 resolve.resolved = green bold
95
95
96 bookmarks.active = green
96 bookmarks.active = green
97
97
98 branches.active = none
98 branches.active = none
99 branches.closed = black bold
99 branches.closed = black bold
100 branches.current = green
100 branches.current = green
101 branches.inactive = none
101 branches.inactive = none
102
102
103 tags.normal = green
103 tags.normal = green
104 tags.local = black bold
104 tags.local = black bold
105
105
106 rebase.rebased = blue
106 rebase.rebased = blue
107 rebase.remaining = red bold
107 rebase.remaining = red bold
108
108
109 shelve.age = cyan
109 shelve.age = cyan
110 shelve.newest = green bold
110 shelve.newest = green bold
111 shelve.name = blue bold
111 shelve.name = blue bold
112
112
113 histedit.remaining = red bold
113 histedit.remaining = red bold
114
114
115 Custom colors
115 Custom colors
116 -------------
116 -------------
117
117
118 Because there are only eight standard colors, this module allows you
118 Because there are only eight standard colors, this module allows you
119 to define color names for other color slots which might be available
119 to define color names for other color slots which might be available
120 for your terminal type, assuming terminfo mode. For instance::
120 for your terminal type, assuming terminfo mode. For instance::
121
121
122 color.brightblue = 12
122 color.brightblue = 12
123 color.pink = 207
123 color.pink = 207
124 color.orange = 202
124 color.orange = 202
125
125
126 to set 'brightblue' to color slot 12 (useful for 16 color terminals
126 to set 'brightblue' to color slot 12 (useful for 16 color terminals
127 that have brighter colors defined in the upper eight) and, 'pink' and
127 that have brighter colors defined in the upper eight) and, 'pink' and
128 'orange' to colors in 256-color xterm's default color cube. These
128 'orange' to colors in 256-color xterm's default color cube. These
129 defined colors may then be used as any of the pre-defined eight,
129 defined colors may then be used as any of the pre-defined eight,
130 including appending '_background' to set the background to that color.
130 including appending '_background' to set the background to that color.
131
131
132 Modes
132 Modes
133 -----
133 -----
134
134
135 By default, the color extension will use ANSI mode (or win32 mode on
135 By default, the color extension will use ANSI mode (or win32 mode on
136 Windows) if it detects a terminal. To override auto mode (to enable
136 Windows) if it detects a terminal. To override auto mode (to enable
137 terminfo mode, for example), set the following configuration option::
137 terminfo mode, for example), set the following configuration option::
138
138
139 [color]
139 [color]
140 mode = terminfo
140 mode = terminfo
141
141
142 Any value other than 'ansi', 'win32', 'terminfo', or 'auto' will
142 Any value other than 'ansi', 'win32', 'terminfo', or 'auto' will
143 disable color.
143 disable color.
144
144
145 Note that on some systems, terminfo mode may cause problems when using
145 Note that on some systems, terminfo mode may cause problems when using
146 color with the pager extension and less -R. less with the -R option
146 color with the pager extension and less -R. less with the -R option
147 will only display ECMA-48 color codes, and terminfo mode may sometimes
147 will only display ECMA-48 color codes, and terminfo mode may sometimes
148 emit codes that less doesn't understand. You can work around this by
148 emit codes that less doesn't understand. You can work around this by
149 either using ansi mode (or auto mode), or by using less -r (which will
149 either using ansi mode (or auto mode), or by using less -r (which will
150 pass through all terminal control codes, not just color control
150 pass through all terminal control codes, not just color control
151 codes).
151 codes).
152
152
153 On some systems (such as MSYS in Windows), the terminal may support
153 On some systems (such as MSYS in Windows), the terminal may support
154 a different color mode than the pager (activated via the "pager"
154 a different color mode than the pager (activated via the "pager"
155 extension). It is possible to define separate modes depending on whether
155 extension). It is possible to define separate modes depending on whether
156 the pager is active::
156 the pager is active::
157
157
158 [color]
158 [color]
159 mode = auto
159 mode = auto
160 pagermode = ansi
160 pagermode = ansi
161
161
162 If ``pagermode`` is not defined, the ``mode`` will be used.
162 If ``pagermode`` is not defined, the ``mode`` will be used.
163 '''
163 '''
164
164
165 from __future__ import absolute_import
165 from __future__ import absolute_import
166
166
167 try:
167 try:
168 import curses
168 import curses
169 curses.COLOR_BLACK # force import
169 curses.COLOR_BLACK # force import
170 except ImportError:
170 except ImportError:
171 curses = None
171 curses = None
172
172
173 from mercurial.i18n import _
173 from mercurial.i18n import _
174 from mercurial import (
174 from mercurial import (
175 cmdutil,
175 cmdutil,
176 color,
176 color,
177 commands,
177 commands,
178 dispatch,
179 extensions,
180 ui as uimod,
181 )
178 )
182
179
183 cmdtable = {}
180 cmdtable = {}
184 command = cmdutil.command(cmdtable)
181 command = cmdutil.command(cmdtable)
185 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
182 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
186 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
183 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
187 # be specifying the version(s) of Mercurial they are tested with, or
184 # be specifying the version(s) of Mercurial they are tested with, or
188 # leave the attribute unspecified.
185 # leave the attribute unspecified.
189 testedwith = 'ships-with-hg-core'
186 testedwith = 'ships-with-hg-core'
190
187
191 def uisetup(ui):
192 def colorcmd(orig, ui_, opts, cmd, cmdfunc):
193 mode = color._modesetup(ui_, opts['color'])
194 uimod.ui._colormode = mode
195 if mode and mode != 'debug':
196 color.configstyles(ui_)
197 return orig(ui_, opts, cmd, cmdfunc)
198 extensions.wrapfunction(dispatch, '_runcommand', colorcmd)
199
200 def extsetup(ui):
188 def extsetup(ui):
201 # change default color config
189 # change default color config
202 for idx, entry in enumerate(commands.globalopts):
190 for idx, entry in enumerate(commands.globalopts):
203 if entry[1] == 'color':
191 if entry[1] == 'color':
204 patch = ('auto', entry[3].replace(' (EXPERIMENTAL)', ''))
192 patch = ('auto', entry[3].replace(' (EXPERIMENTAL)', ''))
205 new = entry[:2] + patch + entry[4:]
193 new = entry[:2] + patch + entry[4:]
206 commands.globalopts[idx] = new
194 commands.globalopts[idx] = new
207 break
195 break
208
196
209 @command('debugcolor',
197 @command('debugcolor',
210 [('', 'style', None, _('show all configured styles'))],
198 [('', 'style', None, _('show all configured styles'))],
211 'hg debugcolor')
199 'hg debugcolor')
212 def debugcolor(ui, repo, **opts):
200 def debugcolor(ui, repo, **opts):
213 """show available color, effects or style"""
201 """show available color, effects or style"""
214 ui.write(('color mode: %s\n') % ui._colormode)
202 ui.write(('color mode: %s\n') % ui._colormode)
215 if opts.get('style'):
203 if opts.get('style'):
216 return _debugdisplaystyle(ui)
204 return _debugdisplaystyle(ui)
217 else:
205 else:
218 return _debugdisplaycolor(ui)
206 return _debugdisplaycolor(ui)
219
207
220 def _debugdisplaycolor(ui):
208 def _debugdisplaycolor(ui):
221 oldstyle = color._styles.copy()
209 oldstyle = color._styles.copy()
222 try:
210 try:
223 color._styles.clear()
211 color._styles.clear()
224 for effect in color._effects.keys():
212 for effect in color._effects.keys():
225 color._styles[effect] = effect
213 color._styles[effect] = effect
226 if color._terminfo_params:
214 if color._terminfo_params:
227 for k, v in ui.configitems('color'):
215 for k, v in ui.configitems('color'):
228 if k.startswith('color.'):
216 if k.startswith('color.'):
229 color._styles[k] = k[6:]
217 color._styles[k] = k[6:]
230 elif k.startswith('terminfo.'):
218 elif k.startswith('terminfo.'):
231 color._styles[k] = k[9:]
219 color._styles[k] = k[9:]
232 ui.write(_('available colors:\n'))
220 ui.write(_('available colors:\n'))
233 # sort label with a '_' after the other to group '_background' entry.
221 # sort label with a '_' after the other to group '_background' entry.
234 items = sorted(color._styles.items(),
222 items = sorted(color._styles.items(),
235 key=lambda i: ('_' in i[0], i[0], i[1]))
223 key=lambda i: ('_' in i[0], i[0], i[1]))
236 for colorname, label in items:
224 for colorname, label in items:
237 ui.write(('%s\n') % colorname, label=label)
225 ui.write(('%s\n') % colorname, label=label)
238 finally:
226 finally:
239 color._styles.clear()
227 color._styles.clear()
240 color._styles.update(oldstyle)
228 color._styles.update(oldstyle)
241
229
242 def _debugdisplaystyle(ui):
230 def _debugdisplaystyle(ui):
243 ui.write(_('available style:\n'))
231 ui.write(_('available style:\n'))
244 width = max(len(s) for s in color._styles)
232 width = max(len(s) for s in color._styles)
245 for label, effects in sorted(color._styles.items()):
233 for label, effects in sorted(color._styles.items()):
246 ui.write('%s' % label, label=label)
234 ui.write('%s' % label, label=label)
247 if effects:
235 if effects:
248 # 50
236 # 50
249 ui.write(': ')
237 ui.write(': ')
250 ui.write(' ' * (max(0, width - len(label))))
238 ui.write(' ' * (max(0, width - len(label))))
251 ui.write(', '.join(ui.label(e, e) for e in effects.split()))
239 ui.write(', '.join(ui.label(e, e) for e in effects.split()))
252 ui.write('\n')
240 ui.write('\n')
@@ -1,446 +1,457
1 # utility for color output for Mercurial commands
1 # utility for color output for Mercurial commands
2 #
2 #
3 # Copyright (C) 2007 Kevin Christen <kevin.christen@gmail.com> and other
3 # Copyright (C) 2007 Kevin Christen <kevin.christen@gmail.com> and other
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 from .i18n import _
10 from .i18n import _
11
11
12 from . import (
12 from . import (
13 encoding,
13 encoding,
14 pycompat,
14 pycompat,
15 util
15 util
16 )
16 )
17
17
18 try:
18 try:
19 import curses
19 import curses
20 # Mapping from effect name to terminfo attribute name (or raw code) or
20 # Mapping from effect name to terminfo attribute name (or raw code) or
21 # color number. This will also force-load the curses module.
21 # color number. This will also force-load the curses module.
22 _terminfo_params = {'none': (True, 'sgr0', ''),
22 _terminfo_params = {'none': (True, 'sgr0', ''),
23 'standout': (True, 'smso', ''),
23 'standout': (True, 'smso', ''),
24 'underline': (True, 'smul', ''),
24 'underline': (True, 'smul', ''),
25 'reverse': (True, 'rev', ''),
25 'reverse': (True, 'rev', ''),
26 'inverse': (True, 'rev', ''),
26 'inverse': (True, 'rev', ''),
27 'blink': (True, 'blink', ''),
27 'blink': (True, 'blink', ''),
28 'dim': (True, 'dim', ''),
28 'dim': (True, 'dim', ''),
29 'bold': (True, 'bold', ''),
29 'bold': (True, 'bold', ''),
30 'invisible': (True, 'invis', ''),
30 'invisible': (True, 'invis', ''),
31 'italic': (True, 'sitm', ''),
31 'italic': (True, 'sitm', ''),
32 'black': (False, curses.COLOR_BLACK, ''),
32 'black': (False, curses.COLOR_BLACK, ''),
33 'red': (False, curses.COLOR_RED, ''),
33 'red': (False, curses.COLOR_RED, ''),
34 'green': (False, curses.COLOR_GREEN, ''),
34 'green': (False, curses.COLOR_GREEN, ''),
35 'yellow': (False, curses.COLOR_YELLOW, ''),
35 'yellow': (False, curses.COLOR_YELLOW, ''),
36 'blue': (False, curses.COLOR_BLUE, ''),
36 'blue': (False, curses.COLOR_BLUE, ''),
37 'magenta': (False, curses.COLOR_MAGENTA, ''),
37 'magenta': (False, curses.COLOR_MAGENTA, ''),
38 'cyan': (False, curses.COLOR_CYAN, ''),
38 'cyan': (False, curses.COLOR_CYAN, ''),
39 'white': (False, curses.COLOR_WHITE, '')}
39 'white': (False, curses.COLOR_WHITE, '')}
40 except ImportError:
40 except ImportError:
41 curses = None
41 curses = None
42 _terminfo_params = {}
42 _terminfo_params = {}
43
43
44 # start and stop parameters for effects
44 # start and stop parameters for effects
45 _effects = {'none': 0,
45 _effects = {'none': 0,
46 'black': 30,
46 'black': 30,
47 'red': 31,
47 'red': 31,
48 'green': 32,
48 'green': 32,
49 'yellow': 33,
49 'yellow': 33,
50 'blue': 34,
50 'blue': 34,
51 'magenta': 35,
51 'magenta': 35,
52 'cyan': 36,
52 'cyan': 36,
53 'white': 37,
53 'white': 37,
54 'bold': 1,
54 'bold': 1,
55 'italic': 3,
55 'italic': 3,
56 'underline': 4,
56 'underline': 4,
57 'inverse': 7,
57 'inverse': 7,
58 'dim': 2,
58 'dim': 2,
59 'black_background': 40,
59 'black_background': 40,
60 'red_background': 41,
60 'red_background': 41,
61 'green_background': 42,
61 'green_background': 42,
62 'yellow_background': 43,
62 'yellow_background': 43,
63 'blue_background': 44,
63 'blue_background': 44,
64 'purple_background': 45,
64 'purple_background': 45,
65 'cyan_background': 46,
65 'cyan_background': 46,
66 'white_background': 47}
66 'white_background': 47}
67
67
68 _styles = {'grep.match': 'red bold',
68 _styles = {'grep.match': 'red bold',
69 'grep.linenumber': 'green',
69 'grep.linenumber': 'green',
70 'grep.rev': 'green',
70 'grep.rev': 'green',
71 'grep.change': 'green',
71 'grep.change': 'green',
72 'grep.sep': 'cyan',
72 'grep.sep': 'cyan',
73 'grep.filename': 'magenta',
73 'grep.filename': 'magenta',
74 'grep.user': 'magenta',
74 'grep.user': 'magenta',
75 'grep.date': 'magenta',
75 'grep.date': 'magenta',
76 'bookmarks.active': 'green',
76 'bookmarks.active': 'green',
77 'branches.active': 'none',
77 'branches.active': 'none',
78 'branches.closed': 'black bold',
78 'branches.closed': 'black bold',
79 'branches.current': 'green',
79 'branches.current': 'green',
80 'branches.inactive': 'none',
80 'branches.inactive': 'none',
81 'diff.changed': 'white',
81 'diff.changed': 'white',
82 'diff.deleted': 'red',
82 'diff.deleted': 'red',
83 'diff.diffline': 'bold',
83 'diff.diffline': 'bold',
84 'diff.extended': 'cyan bold',
84 'diff.extended': 'cyan bold',
85 'diff.file_a': 'red bold',
85 'diff.file_a': 'red bold',
86 'diff.file_b': 'green bold',
86 'diff.file_b': 'green bold',
87 'diff.hunk': 'magenta',
87 'diff.hunk': 'magenta',
88 'diff.inserted': 'green',
88 'diff.inserted': 'green',
89 'diff.tab': '',
89 'diff.tab': '',
90 'diff.trailingwhitespace': 'bold red_background',
90 'diff.trailingwhitespace': 'bold red_background',
91 'changeset.public' : '',
91 'changeset.public' : '',
92 'changeset.draft' : '',
92 'changeset.draft' : '',
93 'changeset.secret' : '',
93 'changeset.secret' : '',
94 'diffstat.deleted': 'red',
94 'diffstat.deleted': 'red',
95 'diffstat.inserted': 'green',
95 'diffstat.inserted': 'green',
96 'histedit.remaining': 'red bold',
96 'histedit.remaining': 'red bold',
97 'ui.prompt': 'yellow',
97 'ui.prompt': 'yellow',
98 'log.changeset': 'yellow',
98 'log.changeset': 'yellow',
99 'patchbomb.finalsummary': '',
99 'patchbomb.finalsummary': '',
100 'patchbomb.from': 'magenta',
100 'patchbomb.from': 'magenta',
101 'patchbomb.to': 'cyan',
101 'patchbomb.to': 'cyan',
102 'patchbomb.subject': 'green',
102 'patchbomb.subject': 'green',
103 'patchbomb.diffstats': '',
103 'patchbomb.diffstats': '',
104 'rebase.rebased': 'blue',
104 'rebase.rebased': 'blue',
105 'rebase.remaining': 'red bold',
105 'rebase.remaining': 'red bold',
106 'resolve.resolved': 'green bold',
106 'resolve.resolved': 'green bold',
107 'resolve.unresolved': 'red bold',
107 'resolve.unresolved': 'red bold',
108 'shelve.age': 'cyan',
108 'shelve.age': 'cyan',
109 'shelve.newest': 'green bold',
109 'shelve.newest': 'green bold',
110 'shelve.name': 'blue bold',
110 'shelve.name': 'blue bold',
111 'status.added': 'green bold',
111 'status.added': 'green bold',
112 'status.clean': 'none',
112 'status.clean': 'none',
113 'status.copied': 'none',
113 'status.copied': 'none',
114 'status.deleted': 'cyan bold underline',
114 'status.deleted': 'cyan bold underline',
115 'status.ignored': 'black bold',
115 'status.ignored': 'black bold',
116 'status.modified': 'blue bold',
116 'status.modified': 'blue bold',
117 'status.removed': 'red bold',
117 'status.removed': 'red bold',
118 'status.unknown': 'magenta bold underline',
118 'status.unknown': 'magenta bold underline',
119 'tags.normal': 'green',
119 'tags.normal': 'green',
120 'tags.local': 'black bold'}
120 'tags.local': 'black bold'}
121
121
122 def loadcolortable(ui, extname, colortable):
122 def loadcolortable(ui, extname, colortable):
123 _styles.update(colortable)
123 _styles.update(colortable)
124
124
125 def _terminfosetup(ui, mode):
125 def _terminfosetup(ui, mode):
126 '''Initialize terminfo data and the terminal if we're in terminfo mode.'''
126 '''Initialize terminfo data and the terminal if we're in terminfo mode.'''
127
127
128 # If we failed to load curses, we go ahead and return.
128 # If we failed to load curses, we go ahead and return.
129 if curses is None:
129 if curses is None:
130 return
130 return
131 # Otherwise, see what the config file says.
131 # Otherwise, see what the config file says.
132 if mode not in ('auto', 'terminfo'):
132 if mode not in ('auto', 'terminfo'):
133 return
133 return
134
134
135 for key, val in ui.configitems('color'):
135 for key, val in ui.configitems('color'):
136 if key.startswith('color.'):
136 if key.startswith('color.'):
137 newval = (False, int(val), '')
137 newval = (False, int(val), '')
138 _terminfo_params[key[6:]] = newval
138 _terminfo_params[key[6:]] = newval
139 elif key.startswith('terminfo.'):
139 elif key.startswith('terminfo.'):
140 newval = (True, '', val.replace('\\E', '\x1b'))
140 newval = (True, '', val.replace('\\E', '\x1b'))
141 _terminfo_params[key[9:]] = newval
141 _terminfo_params[key[9:]] = newval
142 try:
142 try:
143 curses.setupterm()
143 curses.setupterm()
144 except curses.error as e:
144 except curses.error as e:
145 _terminfo_params.clear()
145 _terminfo_params.clear()
146 return
146 return
147
147
148 for key, (b, e, c) in _terminfo_params.items():
148 for key, (b, e, c) in _terminfo_params.items():
149 if not b:
149 if not b:
150 continue
150 continue
151 if not c and not curses.tigetstr(e):
151 if not c and not curses.tigetstr(e):
152 # Most terminals don't support dim, invis, etc, so don't be
152 # Most terminals don't support dim, invis, etc, so don't be
153 # noisy and use ui.debug().
153 # noisy and use ui.debug().
154 ui.debug("no terminfo entry for %s\n" % e)
154 ui.debug("no terminfo entry for %s\n" % e)
155 del _terminfo_params[key]
155 del _terminfo_params[key]
156 if not curses.tigetstr('setaf') or not curses.tigetstr('setab'):
156 if not curses.tigetstr('setaf') or not curses.tigetstr('setab'):
157 # Only warn about missing terminfo entries if we explicitly asked for
157 # Only warn about missing terminfo entries if we explicitly asked for
158 # terminfo mode.
158 # terminfo mode.
159 if mode == "terminfo":
159 if mode == "terminfo":
160 ui.warn(_("no terminfo entry for setab/setaf: reverting to "
160 ui.warn(_("no terminfo entry for setab/setaf: reverting to "
161 "ECMA-48 color\n"))
161 "ECMA-48 color\n"))
162 _terminfo_params.clear()
162 _terminfo_params.clear()
163
163
164 def setup(ui, coloropts):
165 """configure color on a ui
166
167 The 'coloropts' argument is the value of the '--color' command line
168 argument. That function both set the colormode for the ui object and read
169 the configuration looking for custom colors and effect definitions."""
170 mode = _modesetup(ui, coloropts)
171 ui.__class__._colormode = mode
172 if mode and mode != 'debug':
173 configstyles(ui)
174
164 def _modesetup(ui, coloropt):
175 def _modesetup(ui, coloropt):
165 if ui.plain():
176 if ui.plain():
166 return None
177 return None
167 if coloropt == 'debug':
178 if coloropt == 'debug':
168 return 'debug'
179 return 'debug'
169
180
170 auto = (coloropt == 'auto')
181 auto = (coloropt == 'auto')
171 always = not auto and util.parsebool(coloropt)
182 always = not auto and util.parsebool(coloropt)
172 if not always and not auto:
183 if not always and not auto:
173 return None
184 return None
174
185
175 formatted = (always or (encoding.environ.get('TERM') != 'dumb'
186 formatted = (always or (encoding.environ.get('TERM') != 'dumb'
176 and ui.formatted()))
187 and ui.formatted()))
177
188
178 mode = ui.config('color', 'mode', 'auto')
189 mode = ui.config('color', 'mode', 'auto')
179
190
180 # If pager is active, color.pagermode overrides color.mode.
191 # If pager is active, color.pagermode overrides color.mode.
181 if getattr(ui, 'pageractive', False):
192 if getattr(ui, 'pageractive', False):
182 mode = ui.config('color', 'pagermode', mode)
193 mode = ui.config('color', 'pagermode', mode)
183
194
184 realmode = mode
195 realmode = mode
185 if mode == 'auto':
196 if mode == 'auto':
186 if pycompat.osname == 'nt':
197 if pycompat.osname == 'nt':
187 term = encoding.environ.get('TERM')
198 term = encoding.environ.get('TERM')
188 # TERM won't be defined in a vanilla cmd.exe environment.
199 # TERM won't be defined in a vanilla cmd.exe environment.
189
200
190 # UNIX-like environments on Windows such as Cygwin and MSYS will
201 # UNIX-like environments on Windows such as Cygwin and MSYS will
191 # set TERM. They appear to make a best effort attempt at setting it
202 # set TERM. They appear to make a best effort attempt at setting it
192 # to something appropriate. However, not all environments with TERM
203 # to something appropriate. However, not all environments with TERM
193 # defined support ANSI. Since "ansi" could result in terminal
204 # defined support ANSI. Since "ansi" could result in terminal
194 # gibberish, we error on the side of selecting "win32". However, if
205 # gibberish, we error on the side of selecting "win32". However, if
195 # w32effects is not defined, we almost certainly don't support
206 # w32effects is not defined, we almost certainly don't support
196 # "win32", so don't even try.
207 # "win32", so don't even try.
197 if (term and 'xterm' in term) or not w32effects:
208 if (term and 'xterm' in term) or not w32effects:
198 realmode = 'ansi'
209 realmode = 'ansi'
199 else:
210 else:
200 realmode = 'win32'
211 realmode = 'win32'
201 else:
212 else:
202 realmode = 'ansi'
213 realmode = 'ansi'
203
214
204 def modewarn():
215 def modewarn():
205 # only warn if color.mode was explicitly set and we're in
216 # only warn if color.mode was explicitly set and we're in
206 # a formatted terminal
217 # a formatted terminal
207 if mode == realmode and ui.formatted():
218 if mode == realmode and ui.formatted():
208 ui.warn(_('warning: failed to set color mode to %s\n') % mode)
219 ui.warn(_('warning: failed to set color mode to %s\n') % mode)
209
220
210 if realmode == 'win32':
221 if realmode == 'win32':
211 _terminfo_params.clear()
222 _terminfo_params.clear()
212 if not w32effects:
223 if not w32effects:
213 modewarn()
224 modewarn()
214 return None
225 return None
215 _effects.update(w32effects)
226 _effects.update(w32effects)
216 elif realmode == 'ansi':
227 elif realmode == 'ansi':
217 _terminfo_params.clear()
228 _terminfo_params.clear()
218 elif realmode == 'terminfo':
229 elif realmode == 'terminfo':
219 _terminfosetup(ui, mode)
230 _terminfosetup(ui, mode)
220 if not _terminfo_params:
231 if not _terminfo_params:
221 ## FIXME Shouldn't we return None in this case too?
232 ## FIXME Shouldn't we return None in this case too?
222 modewarn()
233 modewarn()
223 realmode = 'ansi'
234 realmode = 'ansi'
224 else:
235 else:
225 return None
236 return None
226
237
227 if always or (auto and formatted):
238 if always or (auto and formatted):
228 return realmode
239 return realmode
229 return None
240 return None
230
241
231 def configstyles(ui):
242 def configstyles(ui):
232 for status, cfgeffects in ui.configitems('color'):
243 for status, cfgeffects in ui.configitems('color'):
233 if '.' not in status or status.startswith(('color.', 'terminfo.')):
244 if '.' not in status or status.startswith(('color.', 'terminfo.')):
234 continue
245 continue
235 cfgeffects = ui.configlist('color', status)
246 cfgeffects = ui.configlist('color', status)
236 if cfgeffects:
247 if cfgeffects:
237 good = []
248 good = []
238 for e in cfgeffects:
249 for e in cfgeffects:
239 if valideffect(e):
250 if valideffect(e):
240 good.append(e)
251 good.append(e)
241 else:
252 else:
242 ui.warn(_("ignoring unknown color/effect %r "
253 ui.warn(_("ignoring unknown color/effect %r "
243 "(configured in color.%s)\n")
254 "(configured in color.%s)\n")
244 % (e, status))
255 % (e, status))
245 _styles[status] = ' '.join(good)
256 _styles[status] = ' '.join(good)
246
257
247 def valideffect(effect):
258 def valideffect(effect):
248 'Determine if the effect is valid or not.'
259 'Determine if the effect is valid or not.'
249 return ((not _terminfo_params and effect in _effects)
260 return ((not _terminfo_params and effect in _effects)
250 or (effect in _terminfo_params
261 or (effect in _terminfo_params
251 or effect[:-11] in _terminfo_params))
262 or effect[:-11] in _terminfo_params))
252
263
253 def _effect_str(effect):
264 def _effect_str(effect):
254 '''Helper function for render_effects().'''
265 '''Helper function for render_effects().'''
255
266
256 bg = False
267 bg = False
257 if effect.endswith('_background'):
268 if effect.endswith('_background'):
258 bg = True
269 bg = True
259 effect = effect[:-11]
270 effect = effect[:-11]
260 try:
271 try:
261 attr, val, termcode = _terminfo_params[effect]
272 attr, val, termcode = _terminfo_params[effect]
262 except KeyError:
273 except KeyError:
263 return ''
274 return ''
264 if attr:
275 if attr:
265 if termcode:
276 if termcode:
266 return termcode
277 return termcode
267 else:
278 else:
268 return curses.tigetstr(val)
279 return curses.tigetstr(val)
269 elif bg:
280 elif bg:
270 return curses.tparm(curses.tigetstr('setab'), val)
281 return curses.tparm(curses.tigetstr('setab'), val)
271 else:
282 else:
272 return curses.tparm(curses.tigetstr('setaf'), val)
283 return curses.tparm(curses.tigetstr('setaf'), val)
273
284
274 def _render_effects(text, effects):
285 def _render_effects(text, effects):
275 'Wrap text in commands to turn on each effect.'
286 'Wrap text in commands to turn on each effect.'
276 if not text:
287 if not text:
277 return text
288 return text
278 if _terminfo_params:
289 if _terminfo_params:
279 start = ''.join(_effect_str(effect)
290 start = ''.join(_effect_str(effect)
280 for effect in ['none'] + effects.split())
291 for effect in ['none'] + effects.split())
281 stop = _effect_str('none')
292 stop = _effect_str('none')
282 else:
293 else:
283 start = [str(_effects[e]) for e in ['none'] + effects.split()]
294 start = [str(_effects[e]) for e in ['none'] + effects.split()]
284 start = '\033[' + ';'.join(start) + 'm'
295 start = '\033[' + ';'.join(start) + 'm'
285 stop = '\033[' + str(_effects['none']) + 'm'
296 stop = '\033[' + str(_effects['none']) + 'm'
286 return ''.join([start, text, stop])
297 return ''.join([start, text, stop])
287
298
288 def colorlabel(ui, msg, label):
299 def colorlabel(ui, msg, label):
289 """add color control code according to the mode"""
300 """add color control code according to the mode"""
290 if ui._colormode == 'debug':
301 if ui._colormode == 'debug':
291 if label and msg:
302 if label and msg:
292 if msg[-1] == '\n':
303 if msg[-1] == '\n':
293 msg = "[%s|%s]\n" % (label, msg[:-1])
304 msg = "[%s|%s]\n" % (label, msg[:-1])
294 else:
305 else:
295 msg = "[%s|%s]" % (label, msg)
306 msg = "[%s|%s]" % (label, msg)
296 elif ui._colormode is not None:
307 elif ui._colormode is not None:
297 effects = []
308 effects = []
298 for l in label.split():
309 for l in label.split():
299 s = _styles.get(l, '')
310 s = _styles.get(l, '')
300 if s:
311 if s:
301 effects.append(s)
312 effects.append(s)
302 elif valideffect(l):
313 elif valideffect(l):
303 effects.append(l)
314 effects.append(l)
304 effects = ' '.join(effects)
315 effects = ' '.join(effects)
305 if effects:
316 if effects:
306 msg = '\n'.join([_render_effects(line, effects)
317 msg = '\n'.join([_render_effects(line, effects)
307 for line in msg.split('\n')])
318 for line in msg.split('\n')])
308 return msg
319 return msg
309
320
310 w32effects = None
321 w32effects = None
311 if pycompat.osname == 'nt':
322 if pycompat.osname == 'nt':
312 import ctypes
323 import ctypes
313 import re
324 import re
314
325
315 _kernel32 = ctypes.windll.kernel32
326 _kernel32 = ctypes.windll.kernel32
316
327
317 _WORD = ctypes.c_ushort
328 _WORD = ctypes.c_ushort
318
329
319 _INVALID_HANDLE_VALUE = -1
330 _INVALID_HANDLE_VALUE = -1
320
331
321 class _COORD(ctypes.Structure):
332 class _COORD(ctypes.Structure):
322 _fields_ = [('X', ctypes.c_short),
333 _fields_ = [('X', ctypes.c_short),
323 ('Y', ctypes.c_short)]
334 ('Y', ctypes.c_short)]
324
335
325 class _SMALL_RECT(ctypes.Structure):
336 class _SMALL_RECT(ctypes.Structure):
326 _fields_ = [('Left', ctypes.c_short),
337 _fields_ = [('Left', ctypes.c_short),
327 ('Top', ctypes.c_short),
338 ('Top', ctypes.c_short),
328 ('Right', ctypes.c_short),
339 ('Right', ctypes.c_short),
329 ('Bottom', ctypes.c_short)]
340 ('Bottom', ctypes.c_short)]
330
341
331 class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
342 class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
332 _fields_ = [('dwSize', _COORD),
343 _fields_ = [('dwSize', _COORD),
333 ('dwCursorPosition', _COORD),
344 ('dwCursorPosition', _COORD),
334 ('wAttributes', _WORD),
345 ('wAttributes', _WORD),
335 ('srWindow', _SMALL_RECT),
346 ('srWindow', _SMALL_RECT),
336 ('dwMaximumWindowSize', _COORD)]
347 ('dwMaximumWindowSize', _COORD)]
337
348
338 _STD_OUTPUT_HANDLE = 0xfffffff5 # (DWORD)-11
349 _STD_OUTPUT_HANDLE = 0xfffffff5 # (DWORD)-11
339 _STD_ERROR_HANDLE = 0xfffffff4 # (DWORD)-12
350 _STD_ERROR_HANDLE = 0xfffffff4 # (DWORD)-12
340
351
341 _FOREGROUND_BLUE = 0x0001
352 _FOREGROUND_BLUE = 0x0001
342 _FOREGROUND_GREEN = 0x0002
353 _FOREGROUND_GREEN = 0x0002
343 _FOREGROUND_RED = 0x0004
354 _FOREGROUND_RED = 0x0004
344 _FOREGROUND_INTENSITY = 0x0008
355 _FOREGROUND_INTENSITY = 0x0008
345
356
346 _BACKGROUND_BLUE = 0x0010
357 _BACKGROUND_BLUE = 0x0010
347 _BACKGROUND_GREEN = 0x0020
358 _BACKGROUND_GREEN = 0x0020
348 _BACKGROUND_RED = 0x0040
359 _BACKGROUND_RED = 0x0040
349 _BACKGROUND_INTENSITY = 0x0080
360 _BACKGROUND_INTENSITY = 0x0080
350
361
351 _COMMON_LVB_REVERSE_VIDEO = 0x4000
362 _COMMON_LVB_REVERSE_VIDEO = 0x4000
352 _COMMON_LVB_UNDERSCORE = 0x8000
363 _COMMON_LVB_UNDERSCORE = 0x8000
353
364
354 # http://msdn.microsoft.com/en-us/library/ms682088%28VS.85%29.aspx
365 # http://msdn.microsoft.com/en-us/library/ms682088%28VS.85%29.aspx
355 w32effects = {
366 w32effects = {
356 'none': -1,
367 'none': -1,
357 'black': 0,
368 'black': 0,
358 'red': _FOREGROUND_RED,
369 'red': _FOREGROUND_RED,
359 'green': _FOREGROUND_GREEN,
370 'green': _FOREGROUND_GREEN,
360 'yellow': _FOREGROUND_RED | _FOREGROUND_GREEN,
371 'yellow': _FOREGROUND_RED | _FOREGROUND_GREEN,
361 'blue': _FOREGROUND_BLUE,
372 'blue': _FOREGROUND_BLUE,
362 'magenta': _FOREGROUND_BLUE | _FOREGROUND_RED,
373 'magenta': _FOREGROUND_BLUE | _FOREGROUND_RED,
363 'cyan': _FOREGROUND_BLUE | _FOREGROUND_GREEN,
374 'cyan': _FOREGROUND_BLUE | _FOREGROUND_GREEN,
364 'white': _FOREGROUND_RED | _FOREGROUND_GREEN | _FOREGROUND_BLUE,
375 'white': _FOREGROUND_RED | _FOREGROUND_GREEN | _FOREGROUND_BLUE,
365 'bold': _FOREGROUND_INTENSITY,
376 'bold': _FOREGROUND_INTENSITY,
366 'black_background': 0x100, # unused value > 0x0f
377 'black_background': 0x100, # unused value > 0x0f
367 'red_background': _BACKGROUND_RED,
378 'red_background': _BACKGROUND_RED,
368 'green_background': _BACKGROUND_GREEN,
379 'green_background': _BACKGROUND_GREEN,
369 'yellow_background': _BACKGROUND_RED | _BACKGROUND_GREEN,
380 'yellow_background': _BACKGROUND_RED | _BACKGROUND_GREEN,
370 'blue_background': _BACKGROUND_BLUE,
381 'blue_background': _BACKGROUND_BLUE,
371 'purple_background': _BACKGROUND_BLUE | _BACKGROUND_RED,
382 'purple_background': _BACKGROUND_BLUE | _BACKGROUND_RED,
372 'cyan_background': _BACKGROUND_BLUE | _BACKGROUND_GREEN,
383 'cyan_background': _BACKGROUND_BLUE | _BACKGROUND_GREEN,
373 'white_background': (_BACKGROUND_RED | _BACKGROUND_GREEN |
384 'white_background': (_BACKGROUND_RED | _BACKGROUND_GREEN |
374 _BACKGROUND_BLUE),
385 _BACKGROUND_BLUE),
375 'bold_background': _BACKGROUND_INTENSITY,
386 'bold_background': _BACKGROUND_INTENSITY,
376 'underline': _COMMON_LVB_UNDERSCORE, # double-byte charsets only
387 'underline': _COMMON_LVB_UNDERSCORE, # double-byte charsets only
377 'inverse': _COMMON_LVB_REVERSE_VIDEO, # double-byte charsets only
388 'inverse': _COMMON_LVB_REVERSE_VIDEO, # double-byte charsets only
378 }
389 }
379
390
380 passthrough = set([_FOREGROUND_INTENSITY,
391 passthrough = set([_FOREGROUND_INTENSITY,
381 _BACKGROUND_INTENSITY,
392 _BACKGROUND_INTENSITY,
382 _COMMON_LVB_UNDERSCORE,
393 _COMMON_LVB_UNDERSCORE,
383 _COMMON_LVB_REVERSE_VIDEO])
394 _COMMON_LVB_REVERSE_VIDEO])
384
395
385 stdout = _kernel32.GetStdHandle(
396 stdout = _kernel32.GetStdHandle(
386 _STD_OUTPUT_HANDLE) # don't close the handle returned
397 _STD_OUTPUT_HANDLE) # don't close the handle returned
387 if stdout is None or stdout == _INVALID_HANDLE_VALUE:
398 if stdout is None or stdout == _INVALID_HANDLE_VALUE:
388 w32effects = None
399 w32effects = None
389 else:
400 else:
390 csbi = _CONSOLE_SCREEN_BUFFER_INFO()
401 csbi = _CONSOLE_SCREEN_BUFFER_INFO()
391 if not _kernel32.GetConsoleScreenBufferInfo(
402 if not _kernel32.GetConsoleScreenBufferInfo(
392 stdout, ctypes.byref(csbi)):
403 stdout, ctypes.byref(csbi)):
393 # stdout may not support GetConsoleScreenBufferInfo()
404 # stdout may not support GetConsoleScreenBufferInfo()
394 # when called from subprocess or redirected
405 # when called from subprocess or redirected
395 w32effects = None
406 w32effects = None
396 else:
407 else:
397 origattr = csbi.wAttributes
408 origattr = csbi.wAttributes
398 ansire = re.compile('\033\[([^m]*)m([^\033]*)(.*)',
409 ansire = re.compile('\033\[([^m]*)m([^\033]*)(.*)',
399 re.MULTILINE | re.DOTALL)
410 re.MULTILINE | re.DOTALL)
400
411
401 def win32print(writefunc, *msgs, **opts):
412 def win32print(writefunc, *msgs, **opts):
402 for text in msgs:
413 for text in msgs:
403 _win32print(text, writefunc, **opts)
414 _win32print(text, writefunc, **opts)
404
415
405 def _win32print(text, writefunc, **opts):
416 def _win32print(text, writefunc, **opts):
406 label = opts.get('label', '')
417 label = opts.get('label', '')
407 attr = origattr
418 attr = origattr
408
419
409 def mapcolor(val, attr):
420 def mapcolor(val, attr):
410 if val == -1:
421 if val == -1:
411 return origattr
422 return origattr
412 elif val in passthrough:
423 elif val in passthrough:
413 return attr | val
424 return attr | val
414 elif val > 0x0f:
425 elif val > 0x0f:
415 return (val & 0x70) | (attr & 0x8f)
426 return (val & 0x70) | (attr & 0x8f)
416 else:
427 else:
417 return (val & 0x07) | (attr & 0xf8)
428 return (val & 0x07) | (attr & 0xf8)
418
429
419 # determine console attributes based on labels
430 # determine console attributes based on labels
420 for l in label.split():
431 for l in label.split():
421 style = _styles.get(l, '')
432 style = _styles.get(l, '')
422 for effect in style.split():
433 for effect in style.split():
423 try:
434 try:
424 attr = mapcolor(w32effects[effect], attr)
435 attr = mapcolor(w32effects[effect], attr)
425 except KeyError:
436 except KeyError:
426 # w32effects could not have certain attributes so we skip
437 # w32effects could not have certain attributes so we skip
427 # them if not found
438 # them if not found
428 pass
439 pass
429 # hack to ensure regexp finds data
440 # hack to ensure regexp finds data
430 if not text.startswith('\033['):
441 if not text.startswith('\033['):
431 text = '\033[m' + text
442 text = '\033[m' + text
432
443
433 # Look for ANSI-like codes embedded in text
444 # Look for ANSI-like codes embedded in text
434 m = re.match(ansire, text)
445 m = re.match(ansire, text)
435
446
436 try:
447 try:
437 while m:
448 while m:
438 for sattr in m.group(1).split(';'):
449 for sattr in m.group(1).split(';'):
439 if sattr:
450 if sattr:
440 attr = mapcolor(int(sattr), attr)
451 attr = mapcolor(int(sattr), attr)
441 _kernel32.SetConsoleTextAttribute(stdout, attr)
452 _kernel32.SetConsoleTextAttribute(stdout, attr)
442 writefunc(m.group(2), **opts)
453 writefunc(m.group(2), **opts)
443 m = re.match(ansire, m.group(3))
454 m = re.match(ansire, m.group(3))
444 finally:
455 finally:
445 # Explicitly reset original attributes
456 # Explicitly reset original attributes
446 _kernel32.SetConsoleTextAttribute(stdout, origattr)
457 _kernel32.SetConsoleTextAttribute(stdout, origattr)
@@ -1,903 +1,907
1 # dispatch.py - command dispatching for mercurial
1 # dispatch.py - command dispatching for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import, print_function
8 from __future__ import absolute_import, print_function
9
9
10 import atexit
10 import atexit
11 import difflib
11 import difflib
12 import errno
12 import errno
13 import getopt
13 import getopt
14 import os
14 import os
15 import pdb
15 import pdb
16 import re
16 import re
17 import signal
17 import signal
18 import sys
18 import sys
19 import time
19 import time
20 import traceback
20 import traceback
21
21
22
22
23 from .i18n import _
23 from .i18n import _
24
24
25 from . import (
25 from . import (
26 cmdutil,
26 cmdutil,
27 color,
27 color,
28 commands,
28 commands,
29 debugcommands,
29 debugcommands,
30 demandimport,
30 demandimport,
31 encoding,
31 encoding,
32 error,
32 error,
33 extensions,
33 extensions,
34 fancyopts,
34 fancyopts,
35 fileset,
35 fileset,
36 help,
36 help,
37 hg,
37 hg,
38 hook,
38 hook,
39 profiling,
39 profiling,
40 pycompat,
40 pycompat,
41 revset,
41 revset,
42 scmutil,
42 scmutil,
43 templatefilters,
43 templatefilters,
44 templatekw,
44 templatekw,
45 templater,
45 templater,
46 ui as uimod,
46 ui as uimod,
47 util,
47 util,
48 )
48 )
49
49
50 class request(object):
50 class request(object):
51 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
51 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
52 ferr=None):
52 ferr=None):
53 self.args = args
53 self.args = args
54 self.ui = ui
54 self.ui = ui
55 self.repo = repo
55 self.repo = repo
56
56
57 # input/output/error streams
57 # input/output/error streams
58 self.fin = fin
58 self.fin = fin
59 self.fout = fout
59 self.fout = fout
60 self.ferr = ferr
60 self.ferr = ferr
61
61
62 def run():
62 def run():
63 "run the command in sys.argv"
63 "run the command in sys.argv"
64 sys.exit((dispatch(request(pycompat.sysargv[1:])) or 0) & 255)
64 sys.exit((dispatch(request(pycompat.sysargv[1:])) or 0) & 255)
65
65
66 def _getsimilar(symbols, value):
66 def _getsimilar(symbols, value):
67 sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio()
67 sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio()
68 # The cutoff for similarity here is pretty arbitrary. It should
68 # The cutoff for similarity here is pretty arbitrary. It should
69 # probably be investigated and tweaked.
69 # probably be investigated and tweaked.
70 return [s for s in symbols if sim(s) > 0.6]
70 return [s for s in symbols if sim(s) > 0.6]
71
71
72 def _reportsimilar(write, similar):
72 def _reportsimilar(write, similar):
73 if len(similar) == 1:
73 if len(similar) == 1:
74 write(_("(did you mean %s?)\n") % similar[0])
74 write(_("(did you mean %s?)\n") % similar[0])
75 elif similar:
75 elif similar:
76 ss = ", ".join(sorted(similar))
76 ss = ", ".join(sorted(similar))
77 write(_("(did you mean one of %s?)\n") % ss)
77 write(_("(did you mean one of %s?)\n") % ss)
78
78
79 def _formatparse(write, inst):
79 def _formatparse(write, inst):
80 similar = []
80 similar = []
81 if isinstance(inst, error.UnknownIdentifier):
81 if isinstance(inst, error.UnknownIdentifier):
82 # make sure to check fileset first, as revset can invoke fileset
82 # make sure to check fileset first, as revset can invoke fileset
83 similar = _getsimilar(inst.symbols, inst.function)
83 similar = _getsimilar(inst.symbols, inst.function)
84 if len(inst.args) > 1:
84 if len(inst.args) > 1:
85 write(_("hg: parse error at %s: %s\n") %
85 write(_("hg: parse error at %s: %s\n") %
86 (inst.args[1], inst.args[0]))
86 (inst.args[1], inst.args[0]))
87 if (inst.args[0][0] == ' '):
87 if (inst.args[0][0] == ' '):
88 write(_("unexpected leading whitespace\n"))
88 write(_("unexpected leading whitespace\n"))
89 else:
89 else:
90 write(_("hg: parse error: %s\n") % inst.args[0])
90 write(_("hg: parse error: %s\n") % inst.args[0])
91 _reportsimilar(write, similar)
91 _reportsimilar(write, similar)
92 if inst.hint:
92 if inst.hint:
93 write(_("(%s)\n") % inst.hint)
93 write(_("(%s)\n") % inst.hint)
94
94
95 def dispatch(req):
95 def dispatch(req):
96 "run the command specified in req.args"
96 "run the command specified in req.args"
97 if req.ferr:
97 if req.ferr:
98 ferr = req.ferr
98 ferr = req.ferr
99 elif req.ui:
99 elif req.ui:
100 ferr = req.ui.ferr
100 ferr = req.ui.ferr
101 else:
101 else:
102 ferr = util.stderr
102 ferr = util.stderr
103
103
104 try:
104 try:
105 if not req.ui:
105 if not req.ui:
106 req.ui = uimod.ui.load()
106 req.ui = uimod.ui.load()
107 if '--traceback' in req.args:
107 if '--traceback' in req.args:
108 req.ui.setconfig('ui', 'traceback', 'on', '--traceback')
108 req.ui.setconfig('ui', 'traceback', 'on', '--traceback')
109
109
110 # set ui streams from the request
110 # set ui streams from the request
111 if req.fin:
111 if req.fin:
112 req.ui.fin = req.fin
112 req.ui.fin = req.fin
113 if req.fout:
113 if req.fout:
114 req.ui.fout = req.fout
114 req.ui.fout = req.fout
115 if req.ferr:
115 if req.ferr:
116 req.ui.ferr = req.ferr
116 req.ui.ferr = req.ferr
117 except error.Abort as inst:
117 except error.Abort as inst:
118 ferr.write(_("abort: %s\n") % inst)
118 ferr.write(_("abort: %s\n") % inst)
119 if inst.hint:
119 if inst.hint:
120 ferr.write(_("(%s)\n") % inst.hint)
120 ferr.write(_("(%s)\n") % inst.hint)
121 return -1
121 return -1
122 except error.ParseError as inst:
122 except error.ParseError as inst:
123 _formatparse(ferr.write, inst)
123 _formatparse(ferr.write, inst)
124 return -1
124 return -1
125
125
126 msg = ' '.join(' ' in a and repr(a) or a for a in req.args)
126 msg = ' '.join(' ' in a and repr(a) or a for a in req.args)
127 starttime = util.timer()
127 starttime = util.timer()
128 ret = None
128 ret = None
129 try:
129 try:
130 ret = _runcatch(req)
130 ret = _runcatch(req)
131 except KeyboardInterrupt:
131 except KeyboardInterrupt:
132 try:
132 try:
133 req.ui.warn(_("interrupted!\n"))
133 req.ui.warn(_("interrupted!\n"))
134 except IOError as inst:
134 except IOError as inst:
135 if inst.errno != errno.EPIPE:
135 if inst.errno != errno.EPIPE:
136 raise
136 raise
137 ret = -1
137 ret = -1
138 finally:
138 finally:
139 duration = util.timer() - starttime
139 duration = util.timer() - starttime
140 req.ui.flush()
140 req.ui.flush()
141 if req.ui.logblockedtimes:
141 if req.ui.logblockedtimes:
142 req.ui._blockedtimes['command_duration'] = duration * 1000
142 req.ui._blockedtimes['command_duration'] = duration * 1000
143 req.ui.log('uiblocked', 'ui blocked ms', **req.ui._blockedtimes)
143 req.ui.log('uiblocked', 'ui blocked ms', **req.ui._blockedtimes)
144 req.ui.log("commandfinish", "%s exited %s after %0.2f seconds\n",
144 req.ui.log("commandfinish", "%s exited %s after %0.2f seconds\n",
145 msg, ret or 0, duration)
145 msg, ret or 0, duration)
146 return ret
146 return ret
147
147
148 def _runcatch(req):
148 def _runcatch(req):
149 def catchterm(*args):
149 def catchterm(*args):
150 raise error.SignalInterrupt
150 raise error.SignalInterrupt
151
151
152 ui = req.ui
152 ui = req.ui
153 try:
153 try:
154 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
154 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
155 num = getattr(signal, name, None)
155 num = getattr(signal, name, None)
156 if num:
156 if num:
157 signal.signal(num, catchterm)
157 signal.signal(num, catchterm)
158 except ValueError:
158 except ValueError:
159 pass # happens if called in a thread
159 pass # happens if called in a thread
160
160
161 def _runcatchfunc():
161 def _runcatchfunc():
162 try:
162 try:
163 debugger = 'pdb'
163 debugger = 'pdb'
164 debugtrace = {
164 debugtrace = {
165 'pdb' : pdb.set_trace
165 'pdb' : pdb.set_trace
166 }
166 }
167 debugmortem = {
167 debugmortem = {
168 'pdb' : pdb.post_mortem
168 'pdb' : pdb.post_mortem
169 }
169 }
170
170
171 # read --config before doing anything else
171 # read --config before doing anything else
172 # (e.g. to change trust settings for reading .hg/hgrc)
172 # (e.g. to change trust settings for reading .hg/hgrc)
173 cfgs = _parseconfig(req.ui, _earlygetopt(['--config'], req.args))
173 cfgs = _parseconfig(req.ui, _earlygetopt(['--config'], req.args))
174
174
175 if req.repo:
175 if req.repo:
176 # copy configs that were passed on the cmdline (--config) to
176 # copy configs that were passed on the cmdline (--config) to
177 # the repo ui
177 # the repo ui
178 for sec, name, val in cfgs:
178 for sec, name, val in cfgs:
179 req.repo.ui.setconfig(sec, name, val, source='--config')
179 req.repo.ui.setconfig(sec, name, val, source='--config')
180
180
181 # developer config: ui.debugger
181 # developer config: ui.debugger
182 debugger = ui.config("ui", "debugger")
182 debugger = ui.config("ui", "debugger")
183 debugmod = pdb
183 debugmod = pdb
184 if not debugger or ui.plain():
184 if not debugger or ui.plain():
185 # if we are in HGPLAIN mode, then disable custom debugging
185 # if we are in HGPLAIN mode, then disable custom debugging
186 debugger = 'pdb'
186 debugger = 'pdb'
187 elif '--debugger' in req.args:
187 elif '--debugger' in req.args:
188 # This import can be slow for fancy debuggers, so only
188 # This import can be slow for fancy debuggers, so only
189 # do it when absolutely necessary, i.e. when actual
189 # do it when absolutely necessary, i.e. when actual
190 # debugging has been requested
190 # debugging has been requested
191 with demandimport.deactivated():
191 with demandimport.deactivated():
192 try:
192 try:
193 debugmod = __import__(debugger)
193 debugmod = __import__(debugger)
194 except ImportError:
194 except ImportError:
195 pass # Leave debugmod = pdb
195 pass # Leave debugmod = pdb
196
196
197 debugtrace[debugger] = debugmod.set_trace
197 debugtrace[debugger] = debugmod.set_trace
198 debugmortem[debugger] = debugmod.post_mortem
198 debugmortem[debugger] = debugmod.post_mortem
199
199
200 # enter the debugger before command execution
200 # enter the debugger before command execution
201 if '--debugger' in req.args:
201 if '--debugger' in req.args:
202 ui.warn(_("entering debugger - "
202 ui.warn(_("entering debugger - "
203 "type c to continue starting hg or h for help\n"))
203 "type c to continue starting hg or h for help\n"))
204
204
205 if (debugger != 'pdb' and
205 if (debugger != 'pdb' and
206 debugtrace[debugger] == debugtrace['pdb']):
206 debugtrace[debugger] == debugtrace['pdb']):
207 ui.warn(_("%s debugger specified "
207 ui.warn(_("%s debugger specified "
208 "but its module was not found\n") % debugger)
208 "but its module was not found\n") % debugger)
209 with demandimport.deactivated():
209 with demandimport.deactivated():
210 debugtrace[debugger]()
210 debugtrace[debugger]()
211 try:
211 try:
212 return _dispatch(req)
212 return _dispatch(req)
213 finally:
213 finally:
214 ui.flush()
214 ui.flush()
215 except: # re-raises
215 except: # re-raises
216 # enter the debugger when we hit an exception
216 # enter the debugger when we hit an exception
217 if '--debugger' in req.args:
217 if '--debugger' in req.args:
218 traceback.print_exc()
218 traceback.print_exc()
219 debugmortem[debugger](sys.exc_info()[2])
219 debugmortem[debugger](sys.exc_info()[2])
220 ui.traceback()
220 ui.traceback()
221 raise
221 raise
222
222
223 return callcatch(ui, _runcatchfunc)
223 return callcatch(ui, _runcatchfunc)
224
224
225 def callcatch(ui, func):
225 def callcatch(ui, func):
226 """like scmutil.callcatch but handles more high-level exceptions about
226 """like scmutil.callcatch but handles more high-level exceptions about
227 config parsing and commands. besides, use handlecommandexception to handle
227 config parsing and commands. besides, use handlecommandexception to handle
228 uncaught exceptions.
228 uncaught exceptions.
229 """
229 """
230 try:
230 try:
231 return scmutil.callcatch(ui, func)
231 return scmutil.callcatch(ui, func)
232 except error.AmbiguousCommand as inst:
232 except error.AmbiguousCommand as inst:
233 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
233 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
234 (inst.args[0], " ".join(inst.args[1])))
234 (inst.args[0], " ".join(inst.args[1])))
235 except error.CommandError as inst:
235 except error.CommandError as inst:
236 if inst.args[0]:
236 if inst.args[0]:
237 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
237 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
238 commands.help_(ui, inst.args[0], full=False, command=True)
238 commands.help_(ui, inst.args[0], full=False, command=True)
239 else:
239 else:
240 ui.warn(_("hg: %s\n") % inst.args[1])
240 ui.warn(_("hg: %s\n") % inst.args[1])
241 commands.help_(ui, 'shortlist')
241 commands.help_(ui, 'shortlist')
242 except error.ParseError as inst:
242 except error.ParseError as inst:
243 _formatparse(ui.warn, inst)
243 _formatparse(ui.warn, inst)
244 return -1
244 return -1
245 except error.UnknownCommand as inst:
245 except error.UnknownCommand as inst:
246 nocmdmsg = _("hg: unknown command '%s'\n") % inst.args[0]
246 nocmdmsg = _("hg: unknown command '%s'\n") % inst.args[0]
247 try:
247 try:
248 # check if the command is in a disabled extension
248 # check if the command is in a disabled extension
249 # (but don't check for extensions themselves)
249 # (but don't check for extensions themselves)
250 formatted = help.formattedhelp(ui, inst.args[0], unknowncmd=True)
250 formatted = help.formattedhelp(ui, inst.args[0], unknowncmd=True)
251 ui.warn(nocmdmsg)
251 ui.warn(nocmdmsg)
252 ui.write(formatted)
252 ui.write(formatted)
253 except (error.UnknownCommand, error.Abort):
253 except (error.UnknownCommand, error.Abort):
254 suggested = False
254 suggested = False
255 if len(inst.args) == 2:
255 if len(inst.args) == 2:
256 sim = _getsimilar(inst.args[1], inst.args[0])
256 sim = _getsimilar(inst.args[1], inst.args[0])
257 if sim:
257 if sim:
258 ui.warn(nocmdmsg)
258 ui.warn(nocmdmsg)
259 _reportsimilar(ui.warn, sim)
259 _reportsimilar(ui.warn, sim)
260 suggested = True
260 suggested = True
261 if not suggested:
261 if not suggested:
262 ui.pager('help')
262 ui.pager('help')
263 ui.warn(nocmdmsg)
263 ui.warn(nocmdmsg)
264 commands.help_(ui, 'shortlist')
264 commands.help_(ui, 'shortlist')
265 except IOError:
265 except IOError:
266 raise
266 raise
267 except KeyboardInterrupt:
267 except KeyboardInterrupt:
268 raise
268 raise
269 except: # probably re-raises
269 except: # probably re-raises
270 if not handlecommandexception(ui):
270 if not handlecommandexception(ui):
271 raise
271 raise
272
272
273 return -1
273 return -1
274
274
275 def aliasargs(fn, givenargs):
275 def aliasargs(fn, givenargs):
276 args = getattr(fn, 'args', [])
276 args = getattr(fn, 'args', [])
277 if args:
277 if args:
278 cmd = ' '.join(map(util.shellquote, args))
278 cmd = ' '.join(map(util.shellquote, args))
279
279
280 nums = []
280 nums = []
281 def replacer(m):
281 def replacer(m):
282 num = int(m.group(1)) - 1
282 num = int(m.group(1)) - 1
283 nums.append(num)
283 nums.append(num)
284 if num < len(givenargs):
284 if num < len(givenargs):
285 return givenargs[num]
285 return givenargs[num]
286 raise error.Abort(_('too few arguments for command alias'))
286 raise error.Abort(_('too few arguments for command alias'))
287 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
287 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
288 givenargs = [x for i, x in enumerate(givenargs)
288 givenargs = [x for i, x in enumerate(givenargs)
289 if i not in nums]
289 if i not in nums]
290 args = pycompat.shlexsplit(cmd)
290 args = pycompat.shlexsplit(cmd)
291 return args + givenargs
291 return args + givenargs
292
292
293 def aliasinterpolate(name, args, cmd):
293 def aliasinterpolate(name, args, cmd):
294 '''interpolate args into cmd for shell aliases
294 '''interpolate args into cmd for shell aliases
295
295
296 This also handles $0, $@ and "$@".
296 This also handles $0, $@ and "$@".
297 '''
297 '''
298 # util.interpolate can't deal with "$@" (with quotes) because it's only
298 # util.interpolate can't deal with "$@" (with quotes) because it's only
299 # built to match prefix + patterns.
299 # built to match prefix + patterns.
300 replacemap = dict(('$%d' % (i + 1), arg) for i, arg in enumerate(args))
300 replacemap = dict(('$%d' % (i + 1), arg) for i, arg in enumerate(args))
301 replacemap['$0'] = name
301 replacemap['$0'] = name
302 replacemap['$$'] = '$'
302 replacemap['$$'] = '$'
303 replacemap['$@'] = ' '.join(args)
303 replacemap['$@'] = ' '.join(args)
304 # Typical Unix shells interpolate "$@" (with quotes) as all the positional
304 # Typical Unix shells interpolate "$@" (with quotes) as all the positional
305 # parameters, separated out into words. Emulate the same behavior here by
305 # parameters, separated out into words. Emulate the same behavior here by
306 # quoting the arguments individually. POSIX shells will then typically
306 # quoting the arguments individually. POSIX shells will then typically
307 # tokenize each argument into exactly one word.
307 # tokenize each argument into exactly one word.
308 replacemap['"$@"'] = ' '.join(util.shellquote(arg) for arg in args)
308 replacemap['"$@"'] = ' '.join(util.shellquote(arg) for arg in args)
309 # escape '\$' for regex
309 # escape '\$' for regex
310 regex = '|'.join(replacemap.keys()).replace('$', r'\$')
310 regex = '|'.join(replacemap.keys()).replace('$', r'\$')
311 r = re.compile(regex)
311 r = re.compile(regex)
312 return r.sub(lambda x: replacemap[x.group()], cmd)
312 return r.sub(lambda x: replacemap[x.group()], cmd)
313
313
314 class cmdalias(object):
314 class cmdalias(object):
315 def __init__(self, name, definition, cmdtable, source):
315 def __init__(self, name, definition, cmdtable, source):
316 self.name = self.cmd = name
316 self.name = self.cmd = name
317 self.cmdname = ''
317 self.cmdname = ''
318 self.definition = definition
318 self.definition = definition
319 self.fn = None
319 self.fn = None
320 self.givenargs = []
320 self.givenargs = []
321 self.opts = []
321 self.opts = []
322 self.help = ''
322 self.help = ''
323 self.badalias = None
323 self.badalias = None
324 self.unknowncmd = False
324 self.unknowncmd = False
325 self.source = source
325 self.source = source
326
326
327 try:
327 try:
328 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
328 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
329 for alias, e in cmdtable.iteritems():
329 for alias, e in cmdtable.iteritems():
330 if e is entry:
330 if e is entry:
331 self.cmd = alias
331 self.cmd = alias
332 break
332 break
333 self.shadows = True
333 self.shadows = True
334 except error.UnknownCommand:
334 except error.UnknownCommand:
335 self.shadows = False
335 self.shadows = False
336
336
337 if not self.definition:
337 if not self.definition:
338 self.badalias = _("no definition for alias '%s'") % self.name
338 self.badalias = _("no definition for alias '%s'") % self.name
339 return
339 return
340
340
341 if self.definition.startswith('!'):
341 if self.definition.startswith('!'):
342 self.shell = True
342 self.shell = True
343 def fn(ui, *args):
343 def fn(ui, *args):
344 env = {'HG_ARGS': ' '.join((self.name,) + args)}
344 env = {'HG_ARGS': ' '.join((self.name,) + args)}
345 def _checkvar(m):
345 def _checkvar(m):
346 if m.groups()[0] == '$':
346 if m.groups()[0] == '$':
347 return m.group()
347 return m.group()
348 elif int(m.groups()[0]) <= len(args):
348 elif int(m.groups()[0]) <= len(args):
349 return m.group()
349 return m.group()
350 else:
350 else:
351 ui.debug("No argument found for substitution "
351 ui.debug("No argument found for substitution "
352 "of %i variable in alias '%s' definition."
352 "of %i variable in alias '%s' definition."
353 % (int(m.groups()[0]), self.name))
353 % (int(m.groups()[0]), self.name))
354 return ''
354 return ''
355 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
355 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
356 cmd = aliasinterpolate(self.name, args, cmd)
356 cmd = aliasinterpolate(self.name, args, cmd)
357 return ui.system(cmd, environ=env)
357 return ui.system(cmd, environ=env)
358 self.fn = fn
358 self.fn = fn
359 return
359 return
360
360
361 try:
361 try:
362 args = pycompat.shlexsplit(self.definition)
362 args = pycompat.shlexsplit(self.definition)
363 except ValueError as inst:
363 except ValueError as inst:
364 self.badalias = (_("error in definition for alias '%s': %s")
364 self.badalias = (_("error in definition for alias '%s': %s")
365 % (self.name, inst))
365 % (self.name, inst))
366 return
366 return
367 self.cmdname = cmd = args.pop(0)
367 self.cmdname = cmd = args.pop(0)
368 self.givenargs = args
368 self.givenargs = args
369
369
370 for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"):
370 for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"):
371 if _earlygetopt([invalidarg], args):
371 if _earlygetopt([invalidarg], args):
372 self.badalias = (_("error in definition for alias '%s': %s may "
372 self.badalias = (_("error in definition for alias '%s': %s may "
373 "only be given on the command line")
373 "only be given on the command line")
374 % (self.name, invalidarg))
374 % (self.name, invalidarg))
375 return
375 return
376
376
377 try:
377 try:
378 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
378 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
379 if len(tableentry) > 2:
379 if len(tableentry) > 2:
380 self.fn, self.opts, self.help = tableentry
380 self.fn, self.opts, self.help = tableentry
381 else:
381 else:
382 self.fn, self.opts = tableentry
382 self.fn, self.opts = tableentry
383
383
384 if self.help.startswith("hg " + cmd):
384 if self.help.startswith("hg " + cmd):
385 # drop prefix in old-style help lines so hg shows the alias
385 # drop prefix in old-style help lines so hg shows the alias
386 self.help = self.help[4 + len(cmd):]
386 self.help = self.help[4 + len(cmd):]
387 self.__doc__ = self.fn.__doc__
387 self.__doc__ = self.fn.__doc__
388
388
389 except error.UnknownCommand:
389 except error.UnknownCommand:
390 self.badalias = (_("alias '%s' resolves to unknown command '%s'")
390 self.badalias = (_("alias '%s' resolves to unknown command '%s'")
391 % (self.name, cmd))
391 % (self.name, cmd))
392 self.unknowncmd = True
392 self.unknowncmd = True
393 except error.AmbiguousCommand:
393 except error.AmbiguousCommand:
394 self.badalias = (_("alias '%s' resolves to ambiguous command '%s'")
394 self.badalias = (_("alias '%s' resolves to ambiguous command '%s'")
395 % (self.name, cmd))
395 % (self.name, cmd))
396
396
397 @property
397 @property
398 def args(self):
398 def args(self):
399 args = map(util.expandpath, self.givenargs)
399 args = map(util.expandpath, self.givenargs)
400 return aliasargs(self.fn, args)
400 return aliasargs(self.fn, args)
401
401
402 def __getattr__(self, name):
402 def __getattr__(self, name):
403 adefaults = {'norepo': True, 'optionalrepo': False, 'inferrepo': False}
403 adefaults = {'norepo': True, 'optionalrepo': False, 'inferrepo': False}
404 if name not in adefaults:
404 if name not in adefaults:
405 raise AttributeError(name)
405 raise AttributeError(name)
406 if self.badalias or util.safehasattr(self, 'shell'):
406 if self.badalias or util.safehasattr(self, 'shell'):
407 return adefaults[name]
407 return adefaults[name]
408 return getattr(self.fn, name)
408 return getattr(self.fn, name)
409
409
410 def __call__(self, ui, *args, **opts):
410 def __call__(self, ui, *args, **opts):
411 if self.badalias:
411 if self.badalias:
412 hint = None
412 hint = None
413 if self.unknowncmd:
413 if self.unknowncmd:
414 try:
414 try:
415 # check if the command is in a disabled extension
415 # check if the command is in a disabled extension
416 cmd, ext = extensions.disabledcmd(ui, self.cmdname)[:2]
416 cmd, ext = extensions.disabledcmd(ui, self.cmdname)[:2]
417 hint = _("'%s' is provided by '%s' extension") % (cmd, ext)
417 hint = _("'%s' is provided by '%s' extension") % (cmd, ext)
418 except error.UnknownCommand:
418 except error.UnknownCommand:
419 pass
419 pass
420 raise error.Abort(self.badalias, hint=hint)
420 raise error.Abort(self.badalias, hint=hint)
421 if self.shadows:
421 if self.shadows:
422 ui.debug("alias '%s' shadows command '%s'\n" %
422 ui.debug("alias '%s' shadows command '%s'\n" %
423 (self.name, self.cmdname))
423 (self.name, self.cmdname))
424
424
425 ui.log('commandalias', "alias '%s' expands to '%s'\n",
425 ui.log('commandalias', "alias '%s' expands to '%s'\n",
426 self.name, self.definition)
426 self.name, self.definition)
427 if util.safehasattr(self, 'shell'):
427 if util.safehasattr(self, 'shell'):
428 return self.fn(ui, *args, **opts)
428 return self.fn(ui, *args, **opts)
429 else:
429 else:
430 try:
430 try:
431 return util.checksignature(self.fn)(ui, *args, **opts)
431 return util.checksignature(self.fn)(ui, *args, **opts)
432 except error.SignatureError:
432 except error.SignatureError:
433 args = ' '.join([self.cmdname] + self.args)
433 args = ' '.join([self.cmdname] + self.args)
434 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
434 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
435 raise
435 raise
436
436
437 def addaliases(ui, cmdtable):
437 def addaliases(ui, cmdtable):
438 # aliases are processed after extensions have been loaded, so they
438 # aliases are processed after extensions have been loaded, so they
439 # may use extension commands. Aliases can also use other alias definitions,
439 # may use extension commands. Aliases can also use other alias definitions,
440 # but only if they have been defined prior to the current definition.
440 # but only if they have been defined prior to the current definition.
441 for alias, definition in ui.configitems('alias'):
441 for alias, definition in ui.configitems('alias'):
442 source = ui.configsource('alias', alias)
442 source = ui.configsource('alias', alias)
443 aliasdef = cmdalias(alias, definition, cmdtable, source)
443 aliasdef = cmdalias(alias, definition, cmdtable, source)
444
444
445 try:
445 try:
446 olddef = cmdtable[aliasdef.cmd][0]
446 olddef = cmdtable[aliasdef.cmd][0]
447 if olddef.definition == aliasdef.definition:
447 if olddef.definition == aliasdef.definition:
448 continue
448 continue
449 except (KeyError, AttributeError):
449 except (KeyError, AttributeError):
450 # definition might not exist or it might not be a cmdalias
450 # definition might not exist or it might not be a cmdalias
451 pass
451 pass
452
452
453 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
453 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
454
454
455 def _parse(ui, args):
455 def _parse(ui, args):
456 options = {}
456 options = {}
457 cmdoptions = {}
457 cmdoptions = {}
458
458
459 try:
459 try:
460 args = fancyopts.fancyopts(args, commands.globalopts, options)
460 args = fancyopts.fancyopts(args, commands.globalopts, options)
461 except getopt.GetoptError as inst:
461 except getopt.GetoptError as inst:
462 raise error.CommandError(None, inst)
462 raise error.CommandError(None, inst)
463
463
464 if args:
464 if args:
465 cmd, args = args[0], args[1:]
465 cmd, args = args[0], args[1:]
466 aliases, entry = cmdutil.findcmd(cmd, commands.table,
466 aliases, entry = cmdutil.findcmd(cmd, commands.table,
467 ui.configbool("ui", "strict"))
467 ui.configbool("ui", "strict"))
468 cmd = aliases[0]
468 cmd = aliases[0]
469 args = aliasargs(entry[0], args)
469 args = aliasargs(entry[0], args)
470 defaults = ui.config("defaults", cmd)
470 defaults = ui.config("defaults", cmd)
471 if defaults:
471 if defaults:
472 args = map(util.expandpath, pycompat.shlexsplit(defaults)) + args
472 args = map(util.expandpath, pycompat.shlexsplit(defaults)) + args
473 c = list(entry[1])
473 c = list(entry[1])
474 else:
474 else:
475 cmd = None
475 cmd = None
476 c = []
476 c = []
477
477
478 # combine global options into local
478 # combine global options into local
479 for o in commands.globalopts:
479 for o in commands.globalopts:
480 c.append((o[0], o[1], options[o[1]], o[3]))
480 c.append((o[0], o[1], options[o[1]], o[3]))
481
481
482 try:
482 try:
483 args = fancyopts.fancyopts(args, c, cmdoptions, gnu=True)
483 args = fancyopts.fancyopts(args, c, cmdoptions, gnu=True)
484 except getopt.GetoptError as inst:
484 except getopt.GetoptError as inst:
485 raise error.CommandError(cmd, inst)
485 raise error.CommandError(cmd, inst)
486
486
487 # separate global options back out
487 # separate global options back out
488 for o in commands.globalopts:
488 for o in commands.globalopts:
489 n = o[1]
489 n = o[1]
490 options[n] = cmdoptions[n]
490 options[n] = cmdoptions[n]
491 del cmdoptions[n]
491 del cmdoptions[n]
492
492
493 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
493 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
494
494
495 def _parseconfig(ui, config):
495 def _parseconfig(ui, config):
496 """parse the --config options from the command line"""
496 """parse the --config options from the command line"""
497 configs = []
497 configs = []
498
498
499 for cfg in config:
499 for cfg in config:
500 try:
500 try:
501 name, value = [cfgelem.strip()
501 name, value = [cfgelem.strip()
502 for cfgelem in cfg.split('=', 1)]
502 for cfgelem in cfg.split('=', 1)]
503 section, name = name.split('.', 1)
503 section, name = name.split('.', 1)
504 if not section or not name:
504 if not section or not name:
505 raise IndexError
505 raise IndexError
506 ui.setconfig(section, name, value, '--config')
506 ui.setconfig(section, name, value, '--config')
507 configs.append((section, name, value))
507 configs.append((section, name, value))
508 except (IndexError, ValueError):
508 except (IndexError, ValueError):
509 raise error.Abort(_('malformed --config option: %r '
509 raise error.Abort(_('malformed --config option: %r '
510 '(use --config section.name=value)') % cfg)
510 '(use --config section.name=value)') % cfg)
511
511
512 return configs
512 return configs
513
513
514 def _earlygetopt(aliases, args):
514 def _earlygetopt(aliases, args):
515 """Return list of values for an option (or aliases).
515 """Return list of values for an option (or aliases).
516
516
517 The values are listed in the order they appear in args.
517 The values are listed in the order they appear in args.
518 The options and values are removed from args.
518 The options and values are removed from args.
519
519
520 >>> args = ['x', '--cwd', 'foo', 'y']
520 >>> args = ['x', '--cwd', 'foo', 'y']
521 >>> _earlygetopt(['--cwd'], args), args
521 >>> _earlygetopt(['--cwd'], args), args
522 (['foo'], ['x', 'y'])
522 (['foo'], ['x', 'y'])
523
523
524 >>> args = ['x', '--cwd=bar', 'y']
524 >>> args = ['x', '--cwd=bar', 'y']
525 >>> _earlygetopt(['--cwd'], args), args
525 >>> _earlygetopt(['--cwd'], args), args
526 (['bar'], ['x', 'y'])
526 (['bar'], ['x', 'y'])
527
527
528 >>> args = ['x', '-R', 'foo', 'y']
528 >>> args = ['x', '-R', 'foo', 'y']
529 >>> _earlygetopt(['-R'], args), args
529 >>> _earlygetopt(['-R'], args), args
530 (['foo'], ['x', 'y'])
530 (['foo'], ['x', 'y'])
531
531
532 >>> args = ['x', '-Rbar', 'y']
532 >>> args = ['x', '-Rbar', 'y']
533 >>> _earlygetopt(['-R'], args), args
533 >>> _earlygetopt(['-R'], args), args
534 (['bar'], ['x', 'y'])
534 (['bar'], ['x', 'y'])
535 """
535 """
536 try:
536 try:
537 argcount = args.index("--")
537 argcount = args.index("--")
538 except ValueError:
538 except ValueError:
539 argcount = len(args)
539 argcount = len(args)
540 shortopts = [opt for opt in aliases if len(opt) == 2]
540 shortopts = [opt for opt in aliases if len(opt) == 2]
541 values = []
541 values = []
542 pos = 0
542 pos = 0
543 while pos < argcount:
543 while pos < argcount:
544 fullarg = arg = args[pos]
544 fullarg = arg = args[pos]
545 equals = arg.find('=')
545 equals = arg.find('=')
546 if equals > -1:
546 if equals > -1:
547 arg = arg[:equals]
547 arg = arg[:equals]
548 if arg in aliases:
548 if arg in aliases:
549 del args[pos]
549 del args[pos]
550 if equals > -1:
550 if equals > -1:
551 values.append(fullarg[equals + 1:])
551 values.append(fullarg[equals + 1:])
552 argcount -= 1
552 argcount -= 1
553 else:
553 else:
554 if pos + 1 >= argcount:
554 if pos + 1 >= argcount:
555 # ignore and let getopt report an error if there is no value
555 # ignore and let getopt report an error if there is no value
556 break
556 break
557 values.append(args.pop(pos))
557 values.append(args.pop(pos))
558 argcount -= 2
558 argcount -= 2
559 elif arg[:2] in shortopts:
559 elif arg[:2] in shortopts:
560 # short option can have no following space, e.g. hg log -Rfoo
560 # short option can have no following space, e.g. hg log -Rfoo
561 values.append(args.pop(pos)[2:])
561 values.append(args.pop(pos)[2:])
562 argcount -= 1
562 argcount -= 1
563 else:
563 else:
564 pos += 1
564 pos += 1
565 return values
565 return values
566
566
567 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
567 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
568 # run pre-hook, and abort if it fails
568 # run pre-hook, and abort if it fails
569 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
569 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
570 pats=cmdpats, opts=cmdoptions)
570 pats=cmdpats, opts=cmdoptions)
571 try:
571 try:
572 ret = _runcommand(ui, options, cmd, d)
572 ret = _runcommand(ui, options, cmd, d)
573 # run post-hook, passing command result
573 # run post-hook, passing command result
574 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
574 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
575 result=ret, pats=cmdpats, opts=cmdoptions)
575 result=ret, pats=cmdpats, opts=cmdoptions)
576 except Exception:
576 except Exception:
577 # run failure hook and re-raise
577 # run failure hook and re-raise
578 hook.hook(lui, repo, "fail-%s" % cmd, False, args=" ".join(fullargs),
578 hook.hook(lui, repo, "fail-%s" % cmd, False, args=" ".join(fullargs),
579 pats=cmdpats, opts=cmdoptions)
579 pats=cmdpats, opts=cmdoptions)
580 raise
580 raise
581 return ret
581 return ret
582
582
583 def _getlocal(ui, rpath, wd=None):
583 def _getlocal(ui, rpath, wd=None):
584 """Return (path, local ui object) for the given target path.
584 """Return (path, local ui object) for the given target path.
585
585
586 Takes paths in [cwd]/.hg/hgrc into account."
586 Takes paths in [cwd]/.hg/hgrc into account."
587 """
587 """
588 if wd is None:
588 if wd is None:
589 try:
589 try:
590 wd = pycompat.getcwd()
590 wd = pycompat.getcwd()
591 except OSError as e:
591 except OSError as e:
592 raise error.Abort(_("error getting current working directory: %s") %
592 raise error.Abort(_("error getting current working directory: %s") %
593 e.strerror)
593 e.strerror)
594 path = cmdutil.findrepo(wd) or ""
594 path = cmdutil.findrepo(wd) or ""
595 if not path:
595 if not path:
596 lui = ui
596 lui = ui
597 else:
597 else:
598 lui = ui.copy()
598 lui = ui.copy()
599 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
599 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
600
600
601 if rpath and rpath[-1]:
601 if rpath and rpath[-1]:
602 path = lui.expandpath(rpath[-1])
602 path = lui.expandpath(rpath[-1])
603 lui = ui.copy()
603 lui = ui.copy()
604 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
604 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
605
605
606 return path, lui
606 return path, lui
607
607
608 def _checkshellalias(lui, ui, args):
608 def _checkshellalias(lui, ui, args):
609 """Return the function to run the shell alias, if it is required"""
609 """Return the function to run the shell alias, if it is required"""
610 options = {}
610 options = {}
611
611
612 try:
612 try:
613 args = fancyopts.fancyopts(args, commands.globalopts, options)
613 args = fancyopts.fancyopts(args, commands.globalopts, options)
614 except getopt.GetoptError:
614 except getopt.GetoptError:
615 return
615 return
616
616
617 if not args:
617 if not args:
618 return
618 return
619
619
620 cmdtable = commands.table
620 cmdtable = commands.table
621
621
622 cmd = args[0]
622 cmd = args[0]
623 try:
623 try:
624 strict = ui.configbool("ui", "strict")
624 strict = ui.configbool("ui", "strict")
625 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
625 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
626 except (error.AmbiguousCommand, error.UnknownCommand):
626 except (error.AmbiguousCommand, error.UnknownCommand):
627 return
627 return
628
628
629 cmd = aliases[0]
629 cmd = aliases[0]
630 fn = entry[0]
630 fn = entry[0]
631
631
632 if cmd and util.safehasattr(fn, 'shell'):
632 if cmd and util.safehasattr(fn, 'shell'):
633 d = lambda: fn(ui, *args[1:])
633 d = lambda: fn(ui, *args[1:])
634 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
634 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
635 [], {})
635 [], {})
636
636
637 _loaded = set()
637 _loaded = set()
638
638
639 # list of (objname, loadermod, loadername) tuple:
639 # list of (objname, loadermod, loadername) tuple:
640 # - objname is the name of an object in extension module, from which
640 # - objname is the name of an object in extension module, from which
641 # extra information is loaded
641 # extra information is loaded
642 # - loadermod is the module where loader is placed
642 # - loadermod is the module where loader is placed
643 # - loadername is the name of the function, which takes (ui, extensionname,
643 # - loadername is the name of the function, which takes (ui, extensionname,
644 # extraobj) arguments
644 # extraobj) arguments
645 extraloaders = [
645 extraloaders = [
646 ('cmdtable', commands, 'loadcmdtable'),
646 ('cmdtable', commands, 'loadcmdtable'),
647 ('colortable', color, 'loadcolortable'),
647 ('colortable', color, 'loadcolortable'),
648 ('filesetpredicate', fileset, 'loadpredicate'),
648 ('filesetpredicate', fileset, 'loadpredicate'),
649 ('revsetpredicate', revset, 'loadpredicate'),
649 ('revsetpredicate', revset, 'loadpredicate'),
650 ('templatefilter', templatefilters, 'loadfilter'),
650 ('templatefilter', templatefilters, 'loadfilter'),
651 ('templatefunc', templater, 'loadfunction'),
651 ('templatefunc', templater, 'loadfunction'),
652 ('templatekeyword', templatekw, 'loadkeyword'),
652 ('templatekeyword', templatekw, 'loadkeyword'),
653 ]
653 ]
654
654
655 def _dispatch(req):
655 def _dispatch(req):
656 args = req.args
656 args = req.args
657 ui = req.ui
657 ui = req.ui
658
658
659 # check for cwd
659 # check for cwd
660 cwd = _earlygetopt(['--cwd'], args)
660 cwd = _earlygetopt(['--cwd'], args)
661 if cwd:
661 if cwd:
662 os.chdir(cwd[-1])
662 os.chdir(cwd[-1])
663
663
664 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
664 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
665 path, lui = _getlocal(ui, rpath)
665 path, lui = _getlocal(ui, rpath)
666
666
667 # Side-effect of accessing is debugcommands module is guaranteed to be
667 # Side-effect of accessing is debugcommands module is guaranteed to be
668 # imported and commands.table is populated.
668 # imported and commands.table is populated.
669 debugcommands.command
669 debugcommands.command
670
670
671 uis = set([ui, lui])
671 uis = set([ui, lui])
672
672
673 if req.repo:
673 if req.repo:
674 uis.add(req.repo.ui)
674 uis.add(req.repo.ui)
675
675
676 if '--profile' in args:
676 if '--profile' in args:
677 for ui_ in uis:
677 for ui_ in uis:
678 ui_.setconfig('profiling', 'enabled', 'true', '--profile')
678 ui_.setconfig('profiling', 'enabled', 'true', '--profile')
679
679
680 with profiling.maybeprofile(lui):
680 with profiling.maybeprofile(lui):
681 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
681 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
682 # reposetup. Programs like TortoiseHg will call _dispatch several
682 # reposetup. Programs like TortoiseHg will call _dispatch several
683 # times so we keep track of configured extensions in _loaded.
683 # times so we keep track of configured extensions in _loaded.
684 extensions.loadall(lui)
684 extensions.loadall(lui)
685 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
685 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
686 # Propagate any changes to lui.__class__ by extensions
686 # Propagate any changes to lui.__class__ by extensions
687 ui.__class__ = lui.__class__
687 ui.__class__ = lui.__class__
688
688
689 # (uisetup and extsetup are handled in extensions.loadall)
689 # (uisetup and extsetup are handled in extensions.loadall)
690
690
691 for name, module in exts:
691 for name, module in exts:
692 for objname, loadermod, loadername in extraloaders:
692 for objname, loadermod, loadername in extraloaders:
693 extraobj = getattr(module, objname, None)
693 extraobj = getattr(module, objname, None)
694 if extraobj is not None:
694 if extraobj is not None:
695 getattr(loadermod, loadername)(ui, name, extraobj)
695 getattr(loadermod, loadername)(ui, name, extraobj)
696 _loaded.add(name)
696 _loaded.add(name)
697
697
698 # (reposetup is handled in hg.repository)
698 # (reposetup is handled in hg.repository)
699
699
700 addaliases(lui, commands.table)
700 addaliases(lui, commands.table)
701
701
702 # All aliases and commands are completely defined, now.
702 # All aliases and commands are completely defined, now.
703 # Check abbreviation/ambiguity of shell alias.
703 # Check abbreviation/ambiguity of shell alias.
704 shellaliasfn = _checkshellalias(lui, ui, args)
704 shellaliasfn = _checkshellalias(lui, ui, args)
705 if shellaliasfn:
705 if shellaliasfn:
706 return shellaliasfn()
706 return shellaliasfn()
707
707
708 # check for fallback encoding
708 # check for fallback encoding
709 fallback = lui.config('ui', 'fallbackencoding')
709 fallback = lui.config('ui', 'fallbackencoding')
710 if fallback:
710 if fallback:
711 encoding.fallbackencoding = fallback
711 encoding.fallbackencoding = fallback
712
712
713 fullargs = args
713 fullargs = args
714 cmd, func, args, options, cmdoptions = _parse(lui, args)
714 cmd, func, args, options, cmdoptions = _parse(lui, args)
715
715
716 if options["config"]:
716 if options["config"]:
717 raise error.Abort(_("option --config may not be abbreviated!"))
717 raise error.Abort(_("option --config may not be abbreviated!"))
718 if options["cwd"]:
718 if options["cwd"]:
719 raise error.Abort(_("option --cwd may not be abbreviated!"))
719 raise error.Abort(_("option --cwd may not be abbreviated!"))
720 if options["repository"]:
720 if options["repository"]:
721 raise error.Abort(_(
721 raise error.Abort(_(
722 "option -R has to be separated from other options (e.g. not "
722 "option -R has to be separated from other options (e.g. not "
723 "-qR) and --repository may only be abbreviated as --repo!"))
723 "-qR) and --repository may only be abbreviated as --repo!"))
724
724
725 if options["encoding"]:
725 if options["encoding"]:
726 encoding.encoding = options["encoding"]
726 encoding.encoding = options["encoding"]
727 if options["encodingmode"]:
727 if options["encodingmode"]:
728 encoding.encodingmode = options["encodingmode"]
728 encoding.encodingmode = options["encodingmode"]
729 if options["time"]:
729 if options["time"]:
730 def get_times():
730 def get_times():
731 t = os.times()
731 t = os.times()
732 if t[4] == 0.0:
732 if t[4] == 0.0:
733 # Windows leaves this as zero, so use time.clock()
733 # Windows leaves this as zero, so use time.clock()
734 t = (t[0], t[1], t[2], t[3], time.clock())
734 t = (t[0], t[1], t[2], t[3], time.clock())
735 return t
735 return t
736 s = get_times()
736 s = get_times()
737 def print_time():
737 def print_time():
738 t = get_times()
738 t = get_times()
739 ui.warn(
739 ui.warn(
740 _("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
740 _("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
741 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
741 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
742 atexit.register(print_time)
742 atexit.register(print_time)
743
743
744 if options['verbose'] or options['debug'] or options['quiet']:
744 if options['verbose'] or options['debug'] or options['quiet']:
745 for opt in ('verbose', 'debug', 'quiet'):
745 for opt in ('verbose', 'debug', 'quiet'):
746 val = str(bool(options[opt]))
746 val = str(bool(options[opt]))
747 for ui_ in uis:
747 for ui_ in uis:
748 ui_.setconfig('ui', opt, val, '--' + opt)
748 ui_.setconfig('ui', opt, val, '--' + opt)
749
749
750 if options['traceback']:
750 if options['traceback']:
751 for ui_ in uis:
751 for ui_ in uis:
752 ui_.setconfig('ui', 'traceback', 'on', '--traceback')
752 ui_.setconfig('ui', 'traceback', 'on', '--traceback')
753
753
754 if options['noninteractive']:
754 if options['noninteractive']:
755 for ui_ in uis:
755 for ui_ in uis:
756 ui_.setconfig('ui', 'interactive', 'off', '-y')
756 ui_.setconfig('ui', 'interactive', 'off', '-y')
757
757
758 if util.parsebool(options['pager']):
758 if util.parsebool(options['pager']):
759 ui.pager('internal-always-' + cmd)
759 ui.pager('internal-always-' + cmd)
760 elif options['pager'] != 'auto':
760 elif options['pager'] != 'auto':
761 ui.disablepager()
761 ui.disablepager()
762
762
763 if cmdoptions.get('insecure', False):
763 if cmdoptions.get('insecure', False):
764 for ui_ in uis:
764 for ui_ in uis:
765 ui_.insecureconnections = True
765 ui_.insecureconnections = True
766
766
767 # setup color handling
768 for ui_ in uis:
769 color.setup(ui_, options['color'])
770
767 if options['version']:
771 if options['version']:
768 return commands.version_(ui)
772 return commands.version_(ui)
769 if options['help']:
773 if options['help']:
770 return commands.help_(ui, cmd, command=cmd is not None)
774 return commands.help_(ui, cmd, command=cmd is not None)
771 elif not cmd:
775 elif not cmd:
772 return commands.help_(ui, 'shortlist')
776 return commands.help_(ui, 'shortlist')
773
777
774 repo = None
778 repo = None
775 cmdpats = args[:]
779 cmdpats = args[:]
776 if not func.norepo:
780 if not func.norepo:
777 # use the repo from the request only if we don't have -R
781 # use the repo from the request only if we don't have -R
778 if not rpath and not cwd:
782 if not rpath and not cwd:
779 repo = req.repo
783 repo = req.repo
780
784
781 if repo:
785 if repo:
782 # set the descriptors of the repo ui to those of ui
786 # set the descriptors of the repo ui to those of ui
783 repo.ui.fin = ui.fin
787 repo.ui.fin = ui.fin
784 repo.ui.fout = ui.fout
788 repo.ui.fout = ui.fout
785 repo.ui.ferr = ui.ferr
789 repo.ui.ferr = ui.ferr
786 else:
790 else:
787 try:
791 try:
788 repo = hg.repository(ui, path=path)
792 repo = hg.repository(ui, path=path)
789 if not repo.local():
793 if not repo.local():
790 raise error.Abort(_("repository '%s' is not local")
794 raise error.Abort(_("repository '%s' is not local")
791 % path)
795 % path)
792 repo.ui.setconfig("bundle", "mainreporoot", repo.root,
796 repo.ui.setconfig("bundle", "mainreporoot", repo.root,
793 'repo')
797 'repo')
794 except error.RequirementError:
798 except error.RequirementError:
795 raise
799 raise
796 except error.RepoError:
800 except error.RepoError:
797 if rpath and rpath[-1]: # invalid -R path
801 if rpath and rpath[-1]: # invalid -R path
798 raise
802 raise
799 if not func.optionalrepo:
803 if not func.optionalrepo:
800 if func.inferrepo and args and not path:
804 if func.inferrepo and args and not path:
801 # try to infer -R from command args
805 # try to infer -R from command args
802 repos = map(cmdutil.findrepo, args)
806 repos = map(cmdutil.findrepo, args)
803 guess = repos[0]
807 guess = repos[0]
804 if guess and repos.count(guess) == len(repos):
808 if guess and repos.count(guess) == len(repos):
805 req.args = ['--repository', guess] + fullargs
809 req.args = ['--repository', guess] + fullargs
806 return _dispatch(req)
810 return _dispatch(req)
807 if not path:
811 if not path:
808 raise error.RepoError(_("no repository found in"
812 raise error.RepoError(_("no repository found in"
809 " '%s' (.hg not found)")
813 " '%s' (.hg not found)")
810 % pycompat.getcwd())
814 % pycompat.getcwd())
811 raise
815 raise
812 if repo:
816 if repo:
813 ui = repo.ui
817 ui = repo.ui
814 if options['hidden']:
818 if options['hidden']:
815 repo = repo.unfiltered()
819 repo = repo.unfiltered()
816 args.insert(0, repo)
820 args.insert(0, repo)
817 elif rpath:
821 elif rpath:
818 ui.warn(_("warning: --repository ignored\n"))
822 ui.warn(_("warning: --repository ignored\n"))
819
823
820 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
824 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
821 ui.log("command", '%s\n', msg)
825 ui.log("command", '%s\n', msg)
822 strcmdopt = pycompat.strkwargs(cmdoptions)
826 strcmdopt = pycompat.strkwargs(cmdoptions)
823 d = lambda: util.checksignature(func)(ui, *args, **strcmdopt)
827 d = lambda: util.checksignature(func)(ui, *args, **strcmdopt)
824 try:
828 try:
825 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
829 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
826 cmdpats, cmdoptions)
830 cmdpats, cmdoptions)
827 finally:
831 finally:
828 if repo and repo != req.repo:
832 if repo and repo != req.repo:
829 repo.close()
833 repo.close()
830
834
831 def _runcommand(ui, options, cmd, cmdfunc):
835 def _runcommand(ui, options, cmd, cmdfunc):
832 """Run a command function, possibly with profiling enabled."""
836 """Run a command function, possibly with profiling enabled."""
833 try:
837 try:
834 return cmdfunc()
838 return cmdfunc()
835 except error.SignatureError:
839 except error.SignatureError:
836 raise error.CommandError(cmd, _('invalid arguments'))
840 raise error.CommandError(cmd, _('invalid arguments'))
837
841
838 def _exceptionwarning(ui):
842 def _exceptionwarning(ui):
839 """Produce a warning message for the current active exception"""
843 """Produce a warning message for the current active exception"""
840
844
841 # For compatibility checking, we discard the portion of the hg
845 # For compatibility checking, we discard the portion of the hg
842 # version after the + on the assumption that if a "normal
846 # version after the + on the assumption that if a "normal
843 # user" is running a build with a + in it the packager
847 # user" is running a build with a + in it the packager
844 # probably built from fairly close to a tag and anyone with a
848 # probably built from fairly close to a tag and anyone with a
845 # 'make local' copy of hg (where the version number can be out
849 # 'make local' copy of hg (where the version number can be out
846 # of date) will be clueful enough to notice the implausible
850 # of date) will be clueful enough to notice the implausible
847 # version number and try updating.
851 # version number and try updating.
848 ct = util.versiontuple(n=2)
852 ct = util.versiontuple(n=2)
849 worst = None, ct, ''
853 worst = None, ct, ''
850 if ui.config('ui', 'supportcontact', None) is None:
854 if ui.config('ui', 'supportcontact', None) is None:
851 for name, mod in extensions.extensions():
855 for name, mod in extensions.extensions():
852 testedwith = getattr(mod, 'testedwith', '')
856 testedwith = getattr(mod, 'testedwith', '')
853 report = getattr(mod, 'buglink', _('the extension author.'))
857 report = getattr(mod, 'buglink', _('the extension author.'))
854 if not testedwith.strip():
858 if not testedwith.strip():
855 # We found an untested extension. It's likely the culprit.
859 # We found an untested extension. It's likely the culprit.
856 worst = name, 'unknown', report
860 worst = name, 'unknown', report
857 break
861 break
858
862
859 # Never blame on extensions bundled with Mercurial.
863 # Never blame on extensions bundled with Mercurial.
860 if extensions.ismoduleinternal(mod):
864 if extensions.ismoduleinternal(mod):
861 continue
865 continue
862
866
863 tested = [util.versiontuple(t, 2) for t in testedwith.split()]
867 tested = [util.versiontuple(t, 2) for t in testedwith.split()]
864 if ct in tested:
868 if ct in tested:
865 continue
869 continue
866
870
867 lower = [t for t in tested if t < ct]
871 lower = [t for t in tested if t < ct]
868 nearest = max(lower or tested)
872 nearest = max(lower or tested)
869 if worst[0] is None or nearest < worst[1]:
873 if worst[0] is None or nearest < worst[1]:
870 worst = name, nearest, report
874 worst = name, nearest, report
871 if worst[0] is not None:
875 if worst[0] is not None:
872 name, testedwith, report = worst
876 name, testedwith, report = worst
873 if not isinstance(testedwith, str):
877 if not isinstance(testedwith, str):
874 testedwith = '.'.join([str(c) for c in testedwith])
878 testedwith = '.'.join([str(c) for c in testedwith])
875 warning = (_('** Unknown exception encountered with '
879 warning = (_('** Unknown exception encountered with '
876 'possibly-broken third-party extension %s\n'
880 'possibly-broken third-party extension %s\n'
877 '** which supports versions %s of Mercurial.\n'
881 '** which supports versions %s of Mercurial.\n'
878 '** Please disable %s and try your action again.\n'
882 '** Please disable %s and try your action again.\n'
879 '** If that fixes the bug please report it to %s\n')
883 '** If that fixes the bug please report it to %s\n')
880 % (name, testedwith, name, report))
884 % (name, testedwith, name, report))
881 else:
885 else:
882 bugtracker = ui.config('ui', 'supportcontact', None)
886 bugtracker = ui.config('ui', 'supportcontact', None)
883 if bugtracker is None:
887 if bugtracker is None:
884 bugtracker = _("https://mercurial-scm.org/wiki/BugTracker")
888 bugtracker = _("https://mercurial-scm.org/wiki/BugTracker")
885 warning = (_("** unknown exception encountered, "
889 warning = (_("** unknown exception encountered, "
886 "please report by visiting\n** ") + bugtracker + '\n')
890 "please report by visiting\n** ") + bugtracker + '\n')
887 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
891 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
888 (_("** Mercurial Distributed SCM (version %s)\n") %
892 (_("** Mercurial Distributed SCM (version %s)\n") %
889 util.version()) +
893 util.version()) +
890 (_("** Extensions loaded: %s\n") %
894 (_("** Extensions loaded: %s\n") %
891 ", ".join([x[0] for x in extensions.extensions()])))
895 ", ".join([x[0] for x in extensions.extensions()])))
892 return warning
896 return warning
893
897
894 def handlecommandexception(ui):
898 def handlecommandexception(ui):
895 """Produce a warning message for broken commands
899 """Produce a warning message for broken commands
896
900
897 Called when handling an exception; the exception is reraised if
901 Called when handling an exception; the exception is reraised if
898 this function returns False, ignored otherwise.
902 this function returns False, ignored otherwise.
899 """
903 """
900 warning = _exceptionwarning(ui)
904 warning = _exceptionwarning(ui)
901 ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
905 ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
902 ui.warn(warning)
906 ui.warn(warning)
903 return False # re-raise the exception
907 return False # re-raise the exception
@@ -1,387 +1,388
1 $ cat <<EOF >> $HGRCPATH
1 $ cat <<EOF >> $HGRCPATH
2 > [extensions]
2 > [extensions]
3 > color =
3 > color =
4 > [color]
4 > [color]
5 > mode = ansi
5 > mode = ansi
6 > EOF
6 > EOF
7 Terminfo codes compatibility fix
7 Terminfo codes compatibility fix
8 $ echo "color.none=0" >> $HGRCPATH
8 $ echo "color.none=0" >> $HGRCPATH
9
9
10 $ hg init repo1
10 $ hg init repo1
11 $ cd repo1
11 $ cd repo1
12 $ mkdir a b a/1 b/1 b/2
12 $ mkdir a b a/1 b/1 b/2
13 $ touch in_root a/in_a b/in_b a/1/in_a_1 b/1/in_b_1 b/2/in_b_2
13 $ touch in_root a/in_a b/in_b a/1/in_a_1 b/1/in_b_1 b/2/in_b_2
14
14
15 hg status in repo root:
15 hg status in repo root:
16
16
17 $ hg status --color=always
17 $ hg status --color=always
18 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc)
18 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc)
19 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc)
19 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc)
20 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc)
20 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc)
21 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc)
21 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc)
22 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc)
22 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc)
23 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc)
23 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc)
24
24
25 $ hg status --color=debug
25 $ hg status --color=debug
26 [status.unknown|? ][status.unknown|a/1/in_a_1]
26 [status.unknown|? ][status.unknown|a/1/in_a_1]
27 [status.unknown|? ][status.unknown|a/in_a]
27 [status.unknown|? ][status.unknown|a/in_a]
28 [status.unknown|? ][status.unknown|b/1/in_b_1]
28 [status.unknown|? ][status.unknown|b/1/in_b_1]
29 [status.unknown|? ][status.unknown|b/2/in_b_2]
29 [status.unknown|? ][status.unknown|b/2/in_b_2]
30 [status.unknown|? ][status.unknown|b/in_b]
30 [status.unknown|? ][status.unknown|b/in_b]
31 [status.unknown|? ][status.unknown|in_root]
31 [status.unknown|? ][status.unknown|in_root]
32
32
33 hg status with template
33 hg status with template
34 $ hg status -T "{label('red', path)}\n" --color=debug
34 $ hg status -T "{label('red', path)}\n" --color=debug
35 [red|a/1/in_a_1]
35 [red|a/1/in_a_1]
36 [red|a/in_a]
36 [red|a/in_a]
37 [red|b/1/in_b_1]
37 [red|b/1/in_b_1]
38 [red|b/2/in_b_2]
38 [red|b/2/in_b_2]
39 [red|b/in_b]
39 [red|b/in_b]
40 [red|in_root]
40 [red|in_root]
41
41
42 hg status . in repo root:
42 hg status . in repo root:
43
43
44 $ hg status --color=always .
44 $ hg status --color=always .
45 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc)
45 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc)
46 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc)
46 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc)
47 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc)
47 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc)
48 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc)
48 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc)
49 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc)
49 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc)
50 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc)
50 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc)
51
51
52 $ hg status --color=always --cwd a
52 $ hg status --color=always --cwd a
53 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc)
53 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc)
54 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc)
54 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc)
55 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc)
55 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc)
56 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc)
56 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc)
57 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc)
57 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc)
58 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc)
58 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc)
59 $ hg status --color=always --cwd a .
59 $ hg status --color=always --cwd a .
60 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m1/in_a_1\x1b[0m (esc)
60 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m1/in_a_1\x1b[0m (esc)
61 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_a\x1b[0m (esc)
61 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_a\x1b[0m (esc)
62 $ hg status --color=always --cwd a ..
62 $ hg status --color=always --cwd a ..
63 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m1/in_a_1\x1b[0m (esc)
63 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m1/in_a_1\x1b[0m (esc)
64 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_a\x1b[0m (esc)
64 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_a\x1b[0m (esc)
65 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../b/1/in_b_1\x1b[0m (esc)
65 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../b/1/in_b_1\x1b[0m (esc)
66 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../b/2/in_b_2\x1b[0m (esc)
66 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../b/2/in_b_2\x1b[0m (esc)
67 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../b/in_b\x1b[0m (esc)
67 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../b/in_b\x1b[0m (esc)
68 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../in_root\x1b[0m (esc)
68 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../in_root\x1b[0m (esc)
69
69
70 $ hg status --color=always --cwd b
70 $ hg status --color=always --cwd b
71 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc)
71 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc)
72 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc)
72 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc)
73 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc)
73 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc)
74 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc)
74 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc)
75 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc)
75 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc)
76 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc)
76 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc)
77 $ hg status --color=always --cwd b .
77 $ hg status --color=always --cwd b .
78 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m1/in_b_1\x1b[0m (esc)
78 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m1/in_b_1\x1b[0m (esc)
79 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m2/in_b_2\x1b[0m (esc)
79 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m2/in_b_2\x1b[0m (esc)
80 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_b\x1b[0m (esc)
80 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_b\x1b[0m (esc)
81 $ hg status --color=always --cwd b ..
81 $ hg status --color=always --cwd b ..
82 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../a/1/in_a_1\x1b[0m (esc)
82 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../a/1/in_a_1\x1b[0m (esc)
83 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../a/in_a\x1b[0m (esc)
83 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../a/in_a\x1b[0m (esc)
84 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m1/in_b_1\x1b[0m (esc)
84 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m1/in_b_1\x1b[0m (esc)
85 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m2/in_b_2\x1b[0m (esc)
85 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m2/in_b_2\x1b[0m (esc)
86 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_b\x1b[0m (esc)
86 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_b\x1b[0m (esc)
87 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../in_root\x1b[0m (esc)
87 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../in_root\x1b[0m (esc)
88
88
89 $ hg status --color=always --cwd a/1
89 $ hg status --color=always --cwd a/1
90 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc)
90 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc)
91 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc)
91 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc)
92 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc)
92 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc)
93 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc)
93 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc)
94 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc)
94 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc)
95 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc)
95 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc)
96 $ hg status --color=always --cwd a/1 .
96 $ hg status --color=always --cwd a/1 .
97 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_a_1\x1b[0m (esc)
97 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_a_1\x1b[0m (esc)
98 $ hg status --color=always --cwd a/1 ..
98 $ hg status --color=always --cwd a/1 ..
99 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_a_1\x1b[0m (esc)
99 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_a_1\x1b[0m (esc)
100 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../in_a\x1b[0m (esc)
100 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../in_a\x1b[0m (esc)
101
101
102 $ hg status --color=always --cwd b/1
102 $ hg status --color=always --cwd b/1
103 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc)
103 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc)
104 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc)
104 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc)
105 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc)
105 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc)
106 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc)
106 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc)
107 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc)
107 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc)
108 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc)
108 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc)
109 $ hg status --color=always --cwd b/1 .
109 $ hg status --color=always --cwd b/1 .
110 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_b_1\x1b[0m (esc)
110 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_b_1\x1b[0m (esc)
111 $ hg status --color=always --cwd b/1 ..
111 $ hg status --color=always --cwd b/1 ..
112 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_b_1\x1b[0m (esc)
112 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_b_1\x1b[0m (esc)
113 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../2/in_b_2\x1b[0m (esc)
113 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../2/in_b_2\x1b[0m (esc)
114 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../in_b\x1b[0m (esc)
114 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../in_b\x1b[0m (esc)
115
115
116 $ hg status --color=always --cwd b/2
116 $ hg status --color=always --cwd b/2
117 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc)
117 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/1/in_a_1\x1b[0m (esc)
118 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc)
118 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4ma/in_a\x1b[0m (esc)
119 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc)
119 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/1/in_b_1\x1b[0m (esc)
120 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc)
120 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/2/in_b_2\x1b[0m (esc)
121 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc)
121 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4mb/in_b\x1b[0m (esc)
122 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc)
122 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_root\x1b[0m (esc)
123 $ hg status --color=always --cwd b/2 .
123 $ hg status --color=always --cwd b/2 .
124 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_b_2\x1b[0m (esc)
124 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_b_2\x1b[0m (esc)
125 $ hg status --color=always --cwd b/2 ..
125 $ hg status --color=always --cwd b/2 ..
126 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../1/in_b_1\x1b[0m (esc)
126 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../1/in_b_1\x1b[0m (esc)
127 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_b_2\x1b[0m (esc)
127 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4min_b_2\x1b[0m (esc)
128 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../in_b\x1b[0m (esc)
128 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4m../in_b\x1b[0m (esc)
129
129
130 Make sure --color=never works
130 Make sure --color=never works
131 $ hg status --color=never
131 $ hg status --color=never
132 ? a/1/in_a_1
132 ? a/1/in_a_1
133 ? a/in_a
133 ? a/in_a
134 ? b/1/in_b_1
134 ? b/1/in_b_1
135 ? b/2/in_b_2
135 ? b/2/in_b_2
136 ? b/in_b
136 ? b/in_b
137 ? in_root
137 ? in_root
138
138
139 Make sure ui.formatted=False works
139 Make sure ui.formatted=False works
140 $ hg status --config ui.formatted=False
140 $ hg status --config ui.formatted=False
141 ? a/1/in_a_1
141 ? a/1/in_a_1
142 ? a/in_a
142 ? a/in_a
143 ? b/1/in_b_1
143 ? b/1/in_b_1
144 ? b/2/in_b_2
144 ? b/2/in_b_2
145 ? b/in_b
145 ? b/in_b
146 ? in_root
146 ? in_root
147
147
148 $ cd ..
148 $ cd ..
149
149
150 $ hg init repo2
150 $ hg init repo2
151 $ cd repo2
151 $ cd repo2
152 $ touch modified removed deleted ignored
152 $ touch modified removed deleted ignored
153 $ echo "^ignored$" > .hgignore
153 $ echo "^ignored$" > .hgignore
154 $ hg ci -A -m 'initial checkin'
154 $ hg ci -A -m 'initial checkin'
155 adding .hgignore
155 adding .hgignore
156 adding deleted
156 adding deleted
157 adding modified
157 adding modified
158 adding removed
158 adding removed
159 $ hg log --color=debug
159 $ hg log --color=debug
160 [log.changeset changeset.draft|changeset: 0:389aef86a55e]
160 [log.changeset changeset.draft|changeset: 0:389aef86a55e]
161 [log.tag|tag: tip]
161 [log.tag|tag: tip]
162 [log.user|user: test]
162 [log.user|user: test]
163 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
163 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
164 [log.summary|summary: initial checkin]
164 [log.summary|summary: initial checkin]
165
165
166 $ hg log -Tcompact --color=debug
166 $ hg log -Tcompact --color=debug
167 [log.changeset changeset.draft|0][tip] [log.node|389aef86a55e] [log.date|1970-01-01 00:00 +0000] [log.user|test]
167 [log.changeset changeset.draft|0][tip] [log.node|389aef86a55e] [log.date|1970-01-01 00:00 +0000] [log.user|test]
168 [ui.note log.description|initial checkin]
168 [ui.note log.description|initial checkin]
169
169
170 Labels on empty strings should not be displayed, labels on custom
170 Labels on empty strings should not be displayed, labels on custom
171 templates should be.
171 templates should be.
172
172
173 $ hg log --color=debug -T '{label("my.label",author)}\n{label("skipped.label","")}'
173 $ hg log --color=debug -T '{label("my.label",author)}\n{label("skipped.label","")}'
174 [my.label|test]
174 [my.label|test]
175 $ touch modified added unknown ignored
175 $ touch modified added unknown ignored
176 $ hg add added
176 $ hg add added
177 $ hg remove removed
177 $ hg remove removed
178 $ rm deleted
178 $ rm deleted
179
179
180 hg status:
180 hg status:
181
181
182 $ hg status --color=always
182 $ hg status --color=always
183 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1madded\x1b[0m (esc)
183 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1madded\x1b[0m (esc)
184 \x1b[0;31;1mR \x1b[0m\x1b[0;31;1mremoved\x1b[0m (esc)
184 \x1b[0;31;1mR \x1b[0m\x1b[0;31;1mremoved\x1b[0m (esc)
185 \x1b[0;36;1;4m! \x1b[0m\x1b[0;36;1;4mdeleted\x1b[0m (esc)
185 \x1b[0;36;1;4m! \x1b[0m\x1b[0;36;1;4mdeleted\x1b[0m (esc)
186 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4munknown\x1b[0m (esc)
186 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4munknown\x1b[0m (esc)
187
187
188 hg status modified added removed deleted unknown never-existed ignored:
188 hg status modified added removed deleted unknown never-existed ignored:
189
189
190 $ hg status --color=always modified added removed deleted unknown never-existed ignored
190 $ hg status --color=always modified added removed deleted unknown never-existed ignored
191 never-existed: * (glob)
191 never-existed: * (glob)
192 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1madded\x1b[0m (esc)
192 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1madded\x1b[0m (esc)
193 \x1b[0;31;1mR \x1b[0m\x1b[0;31;1mremoved\x1b[0m (esc)
193 \x1b[0;31;1mR \x1b[0m\x1b[0;31;1mremoved\x1b[0m (esc)
194 \x1b[0;36;1;4m! \x1b[0m\x1b[0;36;1;4mdeleted\x1b[0m (esc)
194 \x1b[0;36;1;4m! \x1b[0m\x1b[0;36;1;4mdeleted\x1b[0m (esc)
195 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4munknown\x1b[0m (esc)
195 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4munknown\x1b[0m (esc)
196
196
197 $ hg copy modified copied
197 $ hg copy modified copied
198
198
199 hg status -C:
199 hg status -C:
200
200
201 $ hg status --color=always -C
201 $ hg status --color=always -C
202 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1madded\x1b[0m (esc)
202 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1madded\x1b[0m (esc)
203 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1mcopied\x1b[0m (esc)
203 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1mcopied\x1b[0m (esc)
204 \x1b[0;0m modified\x1b[0m (esc)
204 \x1b[0;0m modified\x1b[0m (esc)
205 \x1b[0;31;1mR \x1b[0m\x1b[0;31;1mremoved\x1b[0m (esc)
205 \x1b[0;31;1mR \x1b[0m\x1b[0;31;1mremoved\x1b[0m (esc)
206 \x1b[0;36;1;4m! \x1b[0m\x1b[0;36;1;4mdeleted\x1b[0m (esc)
206 \x1b[0;36;1;4m! \x1b[0m\x1b[0;36;1;4mdeleted\x1b[0m (esc)
207 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4munknown\x1b[0m (esc)
207 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4munknown\x1b[0m (esc)
208
208
209 hg status -A:
209 hg status -A:
210
210
211 $ hg status --color=always -A
211 $ hg status --color=always -A
212 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1madded\x1b[0m (esc)
212 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1madded\x1b[0m (esc)
213 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1mcopied\x1b[0m (esc)
213 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1mcopied\x1b[0m (esc)
214 \x1b[0;0m modified\x1b[0m (esc)
214 \x1b[0;0m modified\x1b[0m (esc)
215 \x1b[0;31;1mR \x1b[0m\x1b[0;31;1mremoved\x1b[0m (esc)
215 \x1b[0;31;1mR \x1b[0m\x1b[0;31;1mremoved\x1b[0m (esc)
216 \x1b[0;36;1;4m! \x1b[0m\x1b[0;36;1;4mdeleted\x1b[0m (esc)
216 \x1b[0;36;1;4m! \x1b[0m\x1b[0;36;1;4mdeleted\x1b[0m (esc)
217 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4munknown\x1b[0m (esc)
217 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4munknown\x1b[0m (esc)
218 \x1b[0;30;1mI \x1b[0m\x1b[0;30;1mignored\x1b[0m (esc)
218 \x1b[0;30;1mI \x1b[0m\x1b[0;30;1mignored\x1b[0m (esc)
219 \x1b[0;0mC \x1b[0m\x1b[0;0m.hgignore\x1b[0m (esc)
219 \x1b[0;0mC \x1b[0m\x1b[0;0m.hgignore\x1b[0m (esc)
220 \x1b[0;0mC \x1b[0m\x1b[0;0mmodified\x1b[0m (esc)
220 \x1b[0;0mC \x1b[0m\x1b[0;0mmodified\x1b[0m (esc)
221
221
222
222
223 hg status -A (with terminfo color):
223 hg status -A (with terminfo color):
224
224
225 #if tic
225 #if tic
226
226
227 $ mkdir "$TESTTMP/terminfo"
227 $ mkdir "$TESTTMP/terminfo"
228 $ TERMINFO="$TESTTMP/terminfo" tic "$TESTDIR/hgterm.ti"
228 $ TERMINFO="$TESTTMP/terminfo" tic "$TESTDIR/hgterm.ti"
229 $ TERM=hgterm TERMINFO="$TESTTMP/terminfo" hg status --config color.mode=terminfo --color=always -A
229 $ TERM=hgterm TERMINFO="$TESTTMP/terminfo" hg status --config color.mode=terminfo --color=always -A
230 \x1b[30m\x1b[32m\x1b[1mA \x1b[30m\x1b[30m\x1b[32m\x1b[1madded\x1b[30m (esc)
230 \x1b[30m\x1b[32m\x1b[1mA \x1b[30m\x1b[30m\x1b[32m\x1b[1madded\x1b[30m (esc)
231 \x1b[30m\x1b[32m\x1b[1mA \x1b[30m\x1b[30m\x1b[32m\x1b[1mcopied\x1b[30m (esc)
231 \x1b[30m\x1b[32m\x1b[1mA \x1b[30m\x1b[30m\x1b[32m\x1b[1mcopied\x1b[30m (esc)
232 \x1b[30m\x1b[30m modified\x1b[30m (esc)
232 \x1b[30m\x1b[30m modified\x1b[30m (esc)
233 \x1b[30m\x1b[31m\x1b[1mR \x1b[30m\x1b[30m\x1b[31m\x1b[1mremoved\x1b[30m (esc)
233 \x1b[30m\x1b[31m\x1b[1mR \x1b[30m\x1b[30m\x1b[31m\x1b[1mremoved\x1b[30m (esc)
234 \x1b[30m\x1b[36m\x1b[1m\x1b[4m! \x1b[30m\x1b[30m\x1b[36m\x1b[1m\x1b[4mdeleted\x1b[30m (esc)
234 \x1b[30m\x1b[36m\x1b[1m\x1b[4m! \x1b[30m\x1b[30m\x1b[36m\x1b[1m\x1b[4mdeleted\x1b[30m (esc)
235 \x1b[30m\x1b[35m\x1b[1m\x1b[4m? \x1b[30m\x1b[30m\x1b[35m\x1b[1m\x1b[4munknown\x1b[30m (esc)
235 \x1b[30m\x1b[35m\x1b[1m\x1b[4m? \x1b[30m\x1b[30m\x1b[35m\x1b[1m\x1b[4munknown\x1b[30m (esc)
236 \x1b[30m\x1b[30m\x1b[1mI \x1b[30m\x1b[30m\x1b[30m\x1b[1mignored\x1b[30m (esc)
236 \x1b[30m\x1b[30m\x1b[1mI \x1b[30m\x1b[30m\x1b[30m\x1b[1mignored\x1b[30m (esc)
237 \x1b[30m\x1b[30mC \x1b[30m\x1b[30m\x1b[30m.hgignore\x1b[30m (esc)
237 \x1b[30m\x1b[30mC \x1b[30m\x1b[30m\x1b[30m.hgignore\x1b[30m (esc)
238 \x1b[30m\x1b[30mC \x1b[30m\x1b[30m\x1b[30mmodified\x1b[30m (esc)
238 \x1b[30m\x1b[30mC \x1b[30m\x1b[30m\x1b[30mmodified\x1b[30m (esc)
239
239
240 The user can define effects with raw terminfo codes:
240 The user can define effects with raw terminfo codes:
241
241
242 $ cat <<EOF >> $HGRCPATH
242 $ cat <<EOF >> $HGRCPATH
243 > # Completely bogus code for dim
243 > # Completely bogus code for dim
244 > terminfo.dim = \E[88m
244 > terminfo.dim = \E[88m
245 > # We can override what's in the terminfo database, too
245 > # We can override what's in the terminfo database, too
246 > terminfo.bold = \E[2m
246 > terminfo.bold = \E[2m
247 > EOF
247 > EOF
248 $ TERM=hgterm TERMINFO="$TESTTMP/terminfo" hg status --config color.mode=terminfo --config color.status.clean=dim --color=always -A
248 $ TERM=hgterm TERMINFO="$TESTTMP/terminfo" hg status --config color.mode=terminfo --config color.status.clean=dim --color=always -A
249 \x1b[30m\x1b[32m\x1b[2mA \x1b[30m\x1b[30m\x1b[32m\x1b[2madded\x1b[30m (esc)
249 \x1b[30m\x1b[32m\x1b[2mA \x1b[30m\x1b[30m\x1b[32m\x1b[2madded\x1b[30m (esc)
250 \x1b[30m\x1b[32m\x1b[2mA \x1b[30m\x1b[30m\x1b[32m\x1b[2mcopied\x1b[30m (esc)
250 \x1b[30m\x1b[32m\x1b[2mA \x1b[30m\x1b[30m\x1b[32m\x1b[2mcopied\x1b[30m (esc)
251 \x1b[30m\x1b[30m modified\x1b[30m (esc)
251 \x1b[30m\x1b[30m modified\x1b[30m (esc)
252 \x1b[30m\x1b[31m\x1b[2mR \x1b[30m\x1b[30m\x1b[31m\x1b[2mremoved\x1b[30m (esc)
252 \x1b[30m\x1b[31m\x1b[2mR \x1b[30m\x1b[30m\x1b[31m\x1b[2mremoved\x1b[30m (esc)
253 \x1b[30m\x1b[36m\x1b[2m\x1b[4m! \x1b[30m\x1b[30m\x1b[36m\x1b[2m\x1b[4mdeleted\x1b[30m (esc)
253 \x1b[30m\x1b[36m\x1b[2m\x1b[4m! \x1b[30m\x1b[30m\x1b[36m\x1b[2m\x1b[4mdeleted\x1b[30m (esc)
254 \x1b[30m\x1b[35m\x1b[2m\x1b[4m? \x1b[30m\x1b[30m\x1b[35m\x1b[2m\x1b[4munknown\x1b[30m (esc)
254 \x1b[30m\x1b[35m\x1b[2m\x1b[4m? \x1b[30m\x1b[30m\x1b[35m\x1b[2m\x1b[4munknown\x1b[30m (esc)
255 \x1b[30m\x1b[30m\x1b[2mI \x1b[30m\x1b[30m\x1b[30m\x1b[2mignored\x1b[30m (esc)
255 \x1b[30m\x1b[30m\x1b[2mI \x1b[30m\x1b[30m\x1b[30m\x1b[2mignored\x1b[30m (esc)
256 \x1b[30m\x1b[88mC \x1b[30m\x1b[30m\x1b[88m.hgignore\x1b[30m (esc)
256 \x1b[30m\x1b[88mC \x1b[30m\x1b[30m\x1b[88m.hgignore\x1b[30m (esc)
257 \x1b[30m\x1b[88mC \x1b[30m\x1b[30m\x1b[88mmodified\x1b[30m (esc)
257 \x1b[30m\x1b[88mC \x1b[30m\x1b[30m\x1b[88mmodified\x1b[30m (esc)
258
258
259 #endif
259 #endif
260
260
261
261
262 $ echo "^ignoreddir$" > .hgignore
262 $ echo "^ignoreddir$" > .hgignore
263 $ mkdir ignoreddir
263 $ mkdir ignoreddir
264 $ touch ignoreddir/file
264 $ touch ignoreddir/file
265
265
266 hg status ignoreddir/file:
266 hg status ignoreddir/file:
267
267
268 $ hg status --color=always ignoreddir/file
268 $ hg status --color=always ignoreddir/file
269
269
270 hg status -i ignoreddir/file:
270 hg status -i ignoreddir/file:
271
271
272 $ hg status --color=always -i ignoreddir/file
272 $ hg status --color=always -i ignoreddir/file
273 \x1b[0;30;1mI \x1b[0m\x1b[0;30;1mignoreddir/file\x1b[0m (esc)
273 \x1b[0;30;1mI \x1b[0m\x1b[0;30;1mignoreddir/file\x1b[0m (esc)
274 $ cd ..
274 $ cd ..
275
275
276 check 'status -q' and some combinations
276 check 'status -q' and some combinations
277
277
278 $ hg init repo3
278 $ hg init repo3
279 $ cd repo3
279 $ cd repo3
280 $ touch modified removed deleted ignored
280 $ touch modified removed deleted ignored
281 $ echo "^ignored$" > .hgignore
281 $ echo "^ignored$" > .hgignore
282 $ hg commit -A -m 'initial checkin'
282 $ hg commit -A -m 'initial checkin'
283 adding .hgignore
283 adding .hgignore
284 adding deleted
284 adding deleted
285 adding modified
285 adding modified
286 adding removed
286 adding removed
287 $ touch added unknown ignored
287 $ touch added unknown ignored
288 $ hg add added
288 $ hg add added
289 $ echo "test" >> modified
289 $ echo "test" >> modified
290 $ hg remove removed
290 $ hg remove removed
291 $ rm deleted
291 $ rm deleted
292 $ hg copy modified copied
292 $ hg copy modified copied
293
293
294 test unknown color
294 test unknown color
295
295
296 $ hg --config color.status.modified=periwinkle status --color=always
296 $ hg --config color.status.modified=periwinkle status --color=always
297 ignoring unknown color/effect 'periwinkle' (configured in color.status.modified)
297 ignoring unknown color/effect 'periwinkle' (configured in color.status.modified)
298 ignoring unknown color/effect 'periwinkle' (configured in color.status.modified)
298 M modified
299 M modified
299 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1madded\x1b[0m (esc)
300 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1madded\x1b[0m (esc)
300 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1mcopied\x1b[0m (esc)
301 \x1b[0;32;1mA \x1b[0m\x1b[0;32;1mcopied\x1b[0m (esc)
301 \x1b[0;31;1mR \x1b[0m\x1b[0;31;1mremoved\x1b[0m (esc)
302 \x1b[0;31;1mR \x1b[0m\x1b[0;31;1mremoved\x1b[0m (esc)
302 \x1b[0;36;1;4m! \x1b[0m\x1b[0;36;1;4mdeleted\x1b[0m (esc)
303 \x1b[0;36;1;4m! \x1b[0m\x1b[0;36;1;4mdeleted\x1b[0m (esc)
303 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4munknown\x1b[0m (esc)
304 \x1b[0;35;1;4m? \x1b[0m\x1b[0;35;1;4munknown\x1b[0m (esc)
304
305
305 Run status with 2 different flags.
306 Run status with 2 different flags.
306 Check if result is the same or different.
307 Check if result is the same or different.
307 If result is not as expected, raise error
308 If result is not as expected, raise error
308
309
309 $ assert() {
310 $ assert() {
310 > hg status --color=always $1 > ../a
311 > hg status --color=always $1 > ../a
311 > hg status --color=always $2 > ../b
312 > hg status --color=always $2 > ../b
312 > if diff ../a ../b > /dev/null; then
313 > if diff ../a ../b > /dev/null; then
313 > out=0
314 > out=0
314 > else
315 > else
315 > out=1
316 > out=1
316 > fi
317 > fi
317 > if [ $3 -eq 0 ]; then
318 > if [ $3 -eq 0 ]; then
318 > df="same"
319 > df="same"
319 > else
320 > else
320 > df="different"
321 > df="different"
321 > fi
322 > fi
322 > if [ $out -ne $3 ]; then
323 > if [ $out -ne $3 ]; then
323 > echo "Error on $1 and $2, should be $df."
324 > echo "Error on $1 and $2, should be $df."
324 > fi
325 > fi
325 > }
326 > }
326
327
327 assert flag1 flag2 [0-same | 1-different]
328 assert flag1 flag2 [0-same | 1-different]
328
329
329 $ assert "-q" "-mard" 0
330 $ assert "-q" "-mard" 0
330 $ assert "-A" "-marduicC" 0
331 $ assert "-A" "-marduicC" 0
331 $ assert "-qA" "-mardcC" 0
332 $ assert "-qA" "-mardcC" 0
332 $ assert "-qAui" "-A" 0
333 $ assert "-qAui" "-A" 0
333 $ assert "-qAu" "-marducC" 0
334 $ assert "-qAu" "-marducC" 0
334 $ assert "-qAi" "-mardicC" 0
335 $ assert "-qAi" "-mardicC" 0
335 $ assert "-qu" "-u" 0
336 $ assert "-qu" "-u" 0
336 $ assert "-q" "-u" 1
337 $ assert "-q" "-u" 1
337 $ assert "-m" "-a" 1
338 $ assert "-m" "-a" 1
338 $ assert "-r" "-d" 1
339 $ assert "-r" "-d" 1
339 $ cd ..
340 $ cd ..
340
341
341 test 'resolve -l'
342 test 'resolve -l'
342
343
343 $ hg init repo4
344 $ hg init repo4
344 $ cd repo4
345 $ cd repo4
345 $ echo "file a" > a
346 $ echo "file a" > a
346 $ echo "file b" > b
347 $ echo "file b" > b
347 $ hg add a b
348 $ hg add a b
348 $ hg commit -m "initial"
349 $ hg commit -m "initial"
349 $ echo "file a change 1" > a
350 $ echo "file a change 1" > a
350 $ echo "file b change 1" > b
351 $ echo "file b change 1" > b
351 $ hg commit -m "head 1"
352 $ hg commit -m "head 1"
352 $ hg update 0
353 $ hg update 0
353 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
354 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
354 $ echo "file a change 2" > a
355 $ echo "file a change 2" > a
355 $ echo "file b change 2" > b
356 $ echo "file b change 2" > b
356 $ hg commit -m "head 2"
357 $ hg commit -m "head 2"
357 created new head
358 created new head
358 $ hg merge
359 $ hg merge
359 merging a
360 merging a
360 merging b
361 merging b
361 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
362 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
362 warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
363 warning: conflicts while merging b! (edit, then use 'hg resolve --mark')
363 0 files updated, 0 files merged, 0 files removed, 2 files unresolved
364 0 files updated, 0 files merged, 0 files removed, 2 files unresolved
364 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
365 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
365 [1]
366 [1]
366 $ hg resolve -m b
367 $ hg resolve -m b
367
368
368 hg resolve with one unresolved, one resolved:
369 hg resolve with one unresolved, one resolved:
369
370
370 $ hg resolve --color=always -l
371 $ hg resolve --color=always -l
371 \x1b[0;31;1mU \x1b[0m\x1b[0;31;1ma\x1b[0m (esc)
372 \x1b[0;31;1mU \x1b[0m\x1b[0;31;1ma\x1b[0m (esc)
372 \x1b[0;32;1mR \x1b[0m\x1b[0;32;1mb\x1b[0m (esc)
373 \x1b[0;32;1mR \x1b[0m\x1b[0;32;1mb\x1b[0m (esc)
373
374
374 color coding of error message with current availability of curses
375 color coding of error message with current availability of curses
375
376
376 $ hg unknowncommand > /dev/null
377 $ hg unknowncommand > /dev/null
377 hg: unknown command 'unknowncommand'
378 hg: unknown command 'unknowncommand'
378 [255]
379 [255]
379
380
380 color coding of error message without curses
381 color coding of error message without curses
381
382
382 $ echo 'raise ImportError' > curses.py
383 $ echo 'raise ImportError' > curses.py
383 $ PYTHONPATH=`pwd`:$PYTHONPATH hg unknowncommand > /dev/null
384 $ PYTHONPATH=`pwd`:$PYTHONPATH hg unknowncommand > /dev/null
384 hg: unknown command 'unknowncommand'
385 hg: unknown command 'unknowncommand'
385 [255]
386 [255]
386
387
387 $ cd ..
388 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now