##// END OF EJS Templates
merge with stable
Matt Mackall -
r14759:c64bd320 merge default
parent child Browse files
Show More
@@ -0,0 +1,27 b''
1 hgterm,
2 am, km, mir, msgr, xenl,
3 colors#8, cols#80, it#8, lines#24, pairs#64,
4 acsc=``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,
5 bel=^G, bold=\E[1m, clear=\E[H\E[2J, cr=\r,
6 csr=\E[%i%p1%d;%p2%dr, cub=\E[%p1%dD, cub1=\b,
7 cud=\E[%p1%dB, cud1=\n, cuf=\E[%p1%dC, cuf1=\E[C,
8 cup=\E[%i%p1%d;%p2%dH, cuu=\E[%p1%dA, cuu1=\E[A,
9 dch=\E[%p1%dP, dch1=\E[P, dl=\E[%p1%dM, dl1=\E[M,
10 ed=\E[J, el=\E[K, enacs=\E)0, home=\E[H, ht=\t,
11 hts=\EH, il=\E[%p1%dL, il1=\E[L, ind=\n,
12 is2=\E[m\E[?7h\E[4l\E>\E7\E[r\E[?1;3;4;6l\E8, kbs=\b,
13 kcub1=\EOD, kcud1=\EOB, kcuf1=\EOC, kcuu1=\EOA,
14 kdch1=\E[3~, kf1=\E[11~, kf10=\E[21~, kf11=\E[23~,
15 kf12=\E[24~, kf13=\E[25~, kf14=\E[26~, kf15=\E[28~,
16 kf16=\E[29~, kf17=\E[31~, kf18=\E[32~, kf19=\E[33~,
17 kf2=\E[12~, kf20=\E[34~, kf3=\E[13~, kf4=\E[14~,
18 kf5=\E[15~, kf6=\E[17~, kf7=\E[18~, kf8=\E[19~,
19 kf9=\E[20~, kfnd=\E[1~, kich1=\E[2~, kmous=\E[M,
20 knp=\E[6~, kpp=\E[5~, kslt=\E[4~, op=\E[m, rc=\E8,
21 rev=\E[7m, ri=\EM, rmacs=^O, rmcup=\E[2J\E[?47l\E8,
22 rmir=\E[4l, rmkx=\E[?1l\E>, rmso=\E[m, rmul=\E[m,
23 rs2=\E[m\E[?7h\E[4l\E>\E7\E[r\E[?1;3;4;6l\E8, sc=\E7,
24 setab=\E[4%p1%dm, setaf=\E[3%p1%dm, sgr0=\E[m,
25 smacs=^N, smcup=\E7\E[?47h, smir=\E[4h,
26 smkx=\E[?1h\E=, smso=\E[7m, smul=\E[4m, tbc=\E[3g,
27 u6=\E[%i%d;%dR, u7=\E[6n, u8=\E[?1;2c, u9=\E[c,
@@ -1,496 +1,499 b''
1 # color.py color output for the status and qseries commands
1 # color.py color output for the status and qseries 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 program is free software; you can redistribute it and/or modify it
5 # This program is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by the
6 # under the terms of the GNU General Public License as published by the
7 # Free Software Foundation; either version 2 of the License, or (at your
7 # Free Software Foundation; either version 2 of the License, or (at your
8 # option) any later version.
8 # option) any later version.
9 #
9 #
10 # This program is distributed in the hope that it will be useful, but
10 # This program is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
13 # Public License for more details.
13 # Public License for more details.
14 #
14 #
15 # You should have received a copy of the GNU General Public License along
15 # You should have received a copy of the GNU General Public License along
16 # with this program; if not, write to the Free Software Foundation, Inc.,
16 # with this program; if not, write to the Free Software Foundation, Inc.,
17 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18
18
19 '''colorize output from some commands
19 '''colorize output from some commands
20
20
21 This extension modifies the status and resolve commands to add color
21 This extension modifies the status and resolve commands to add color
22 to their output to reflect file status, the qseries command to add
22 to their output to reflect file status, the qseries command to add
23 color to reflect patch status (applied, unapplied, missing), and to
23 color to reflect patch status (applied, unapplied, missing), and to
24 diff-related commands to highlight additions, removals, diff headers,
24 diff-related commands to highlight additions, removals, diff headers,
25 and trailing whitespace.
25 and trailing whitespace.
26
26
27 Other effects in addition to color, like bold and underlined text, are
27 Other effects in addition to color, like bold and underlined text, are
28 also available. By default, the terminfo database is used to find the
28 also available. By default, the terminfo database is used to find the
29 terminal codes used to change color and effect. If terminfo is not
29 terminal codes used to change color and effect. If terminfo is not
30 available, then effects are rendered with the ECMA-48 SGR control
30 available, then effects are rendered with the ECMA-48 SGR control
31 function (aka ANSI escape codes).
31 function (aka ANSI escape codes).
32
32
33 Default effects may be overridden from your configuration file::
33 Default effects may be overridden from your configuration file::
34
34
35 [color]
35 [color]
36 status.modified = blue bold underline red_background
36 status.modified = blue bold underline red_background
37 status.added = green bold
37 status.added = green bold
38 status.removed = red bold blue_background
38 status.removed = red bold blue_background
39 status.deleted = cyan bold underline
39 status.deleted = cyan bold underline
40 status.unknown = magenta bold underline
40 status.unknown = magenta bold underline
41 status.ignored = black bold
41 status.ignored = black bold
42
42
43 # 'none' turns off all effects
43 # 'none' turns off all effects
44 status.clean = none
44 status.clean = none
45 status.copied = none
45 status.copied = none
46
46
47 qseries.applied = blue bold underline
47 qseries.applied = blue bold underline
48 qseries.unapplied = black bold
48 qseries.unapplied = black bold
49 qseries.missing = red bold
49 qseries.missing = red bold
50
50
51 diff.diffline = bold
51 diff.diffline = bold
52 diff.extended = cyan bold
52 diff.extended = cyan bold
53 diff.file_a = red bold
53 diff.file_a = red bold
54 diff.file_b = green bold
54 diff.file_b = green bold
55 diff.hunk = magenta
55 diff.hunk = magenta
56 diff.deleted = red
56 diff.deleted = red
57 diff.inserted = green
57 diff.inserted = green
58 diff.changed = white
58 diff.changed = white
59 diff.trailingwhitespace = bold red_background
59 diff.trailingwhitespace = bold red_background
60
60
61 resolve.unresolved = red bold
61 resolve.unresolved = red bold
62 resolve.resolved = green bold
62 resolve.resolved = green bold
63
63
64 bookmarks.current = green
64 bookmarks.current = green
65
65
66 branches.active = none
66 branches.active = none
67 branches.closed = black bold
67 branches.closed = black bold
68 branches.current = green
68 branches.current = green
69 branches.inactive = none
69 branches.inactive = none
70
70
71 The available effects in terminfo mode are 'blink', 'bold', 'dim',
71 The available effects in terminfo mode are 'blink', 'bold', 'dim',
72 'inverse', 'invisible', 'italic', 'standout', and 'underline'; in
72 'inverse', 'invisible', 'italic', 'standout', and 'underline'; in
73 ECMA-48 mode, the options are 'bold', 'inverse', 'italic', and
73 ECMA-48 mode, the options are 'bold', 'inverse', 'italic', and
74 'underline'. How each is rendered depends on the terminal emulator.
74 'underline'. How each is rendered depends on the terminal emulator.
75 Some may not be available for a given terminal type, and will be
75 Some may not be available for a given terminal type, and will be
76 silently ignored.
76 silently ignored.
77
77
78 Because there are only eight standard colors, this module allows you
78 Because there are only eight standard colors, this module allows you
79 to define color names for other color slots which might be available
79 to define color names for other color slots which might be available
80 for your terminal type, assuming terminfo mode. For instance::
80 for your terminal type, assuming terminfo mode. For instance::
81
81
82 color.brightblue = 12
82 color.brightblue = 12
83 color.pink = 207
83 color.pink = 207
84 color.orange = 202
84 color.orange = 202
85
85
86 to set 'brightblue' to color slot 12 (useful for 16 color terminals
86 to set 'brightblue' to color slot 12 (useful for 16 color terminals
87 that have brighter colors defined in the upper eight) and, 'pink' and
87 that have brighter colors defined in the upper eight) and, 'pink' and
88 'orange' to colors in 256-color xterm's default color cube. These
88 'orange' to colors in 256-color xterm's default color cube. These
89 defined colors may then be used as any of the pre-defined eight,
89 defined colors may then be used as any of the pre-defined eight,
90 including appending '_background' to set the background to that color.
90 including appending '_background' to set the background to that color.
91
91
92 The color extension will try to detect whether to use terminfo, ANSI
92 The color extension will try to detect whether to use terminfo, ANSI
93 codes or Win32 console APIs, unless it is made explicit; e.g.::
93 codes or Win32 console APIs, unless it is made explicit; e.g.::
94
94
95 [color]
95 [color]
96 mode = ansi
96 mode = ansi
97
97
98 Any value other than 'ansi', 'win32', 'terminfo', or 'auto' will
98 Any value other than 'ansi', 'win32', 'terminfo', or 'auto' will
99 disable color.
99 disable color.
100
100
101 '''
101 '''
102
102
103 import os
103 import os
104
104
105 from mercurial import commands, dispatch, extensions, ui as uimod, util
105 from mercurial import commands, dispatch, extensions, ui as uimod, util
106 from mercurial.i18n import _
106 from mercurial.i18n import _
107
107
108 # start and stop parameters for effects
108 # start and stop parameters for effects
109 _effects = {'none': 0, 'black': 30, 'red': 31, 'green': 32, 'yellow': 33,
109 _effects = {'none': 0, 'black': 30, 'red': 31, 'green': 32, 'yellow': 33,
110 'blue': 34, 'magenta': 35, 'cyan': 36, 'white': 37, 'bold': 1,
110 'blue': 34, 'magenta': 35, 'cyan': 36, 'white': 37, 'bold': 1,
111 'italic': 3, 'underline': 4, 'inverse': 7,
111 'italic': 3, 'underline': 4, 'inverse': 7,
112 'black_background': 40, 'red_background': 41,
112 'black_background': 40, 'red_background': 41,
113 'green_background': 42, 'yellow_background': 43,
113 'green_background': 42, 'yellow_background': 43,
114 'blue_background': 44, 'purple_background': 45,
114 'blue_background': 44, 'purple_background': 45,
115 'cyan_background': 46, 'white_background': 47}
115 'cyan_background': 46, 'white_background': 47}
116
116
117 def _terminfosetup(ui, mode):
117 def _terminfosetup(ui, mode):
118 '''Initialize terminfo data and the terminal if we're in terminfo mode.'''
118 '''Initialize terminfo data and the terminal if we're in terminfo mode.'''
119
119
120 global _terminfo_params
120 global _terminfo_params
121 # If we failed to load curses, we go ahead and return.
121 # If we failed to load curses, we go ahead and return.
122 if not _terminfo_params:
122 if not _terminfo_params:
123 return
123 return
124 # Otherwise, see what the config file says.
124 # Otherwise, see what the config file says.
125 if mode not in ('auto', 'terminfo'):
125 if mode not in ('auto', 'terminfo'):
126 return
126 return
127
127
128 _terminfo_params.update((key[6:], (False, int(val)))
128 _terminfo_params.update((key[6:], (False, int(val)))
129 for key, val in ui.configitems('color')
129 for key, val in ui.configitems('color')
130 if key.startswith('color.'))
130 if key.startswith('color.'))
131
131
132 try:
132 try:
133 curses.setupterm()
133 curses.setupterm()
134 except curses.error, e:
134 except curses.error, e:
135 _terminfo_params = {}
135 _terminfo_params = {}
136 return
136 return
137
137
138 for key, (b, e) in _terminfo_params.items():
138 for key, (b, e) in _terminfo_params.items():
139 if not b:
139 if not b:
140 continue
140 continue
141 if not curses.tigetstr(e):
141 if not curses.tigetstr(e):
142 # Most terminals don't support dim, invis, etc, so don't be
142 # Most terminals don't support dim, invis, etc, so don't be
143 # noisy and use ui.debug().
143 # noisy and use ui.debug().
144 ui.debug("no terminfo entry for %s\n" % e)
144 ui.debug("no terminfo entry for %s\n" % e)
145 del _terminfo_params[key]
145 del _terminfo_params[key]
146 if not curses.tigetstr('setaf') or not curses.tigetstr('setab'):
146 if not curses.tigetstr('setaf') or not curses.tigetstr('setab'):
147 ui.warn(_("no terminfo entry for setab/setaf: reverting to "
147 # Only warn about missing terminfo entries if we explicitly asked for
148 "ECMA-48 color\n"))
148 # terminfo mode.
149 if mode == "terminfo":
150 ui.warn(_("no terminfo entry for setab/setaf: reverting to "
151 "ECMA-48 color\n"))
149 _terminfo_params = {}
152 _terminfo_params = {}
150
153
151 def _modesetup(ui, opts):
154 def _modesetup(ui, opts):
152 global _terminfo_params
155 global _terminfo_params
153
156
154 coloropt = opts['color']
157 coloropt = opts['color']
155 auto = coloropt == 'auto'
158 auto = coloropt == 'auto'
156 always = not auto and util.parsebool(coloropt)
159 always = not auto and util.parsebool(coloropt)
157 if not always and not auto:
160 if not always and not auto:
158 return None
161 return None
159
162
160 formatted = always or (os.environ.get('TERM') != 'dumb' and ui.formatted())
163 formatted = always or (os.environ.get('TERM') != 'dumb' and ui.formatted())
161
164
162 mode = ui.config('color', 'mode', 'auto')
165 mode = ui.config('color', 'mode', 'auto')
163 realmode = mode
166 realmode = mode
164 if mode == 'auto':
167 if mode == 'auto':
165 if os.name == 'nt' and 'TERM' not in os.environ:
168 if os.name == 'nt' and 'TERM' not in os.environ:
166 # looks line a cmd.exe console, use win32 API or nothing
169 # looks line a cmd.exe console, use win32 API or nothing
167 realmode = 'win32'
170 realmode = 'win32'
168 elif not formatted:
171 elif not formatted:
169 realmode = 'ansi'
172 realmode = 'ansi'
170 else:
173 else:
171 realmode = 'terminfo'
174 realmode = 'terminfo'
172
175
173 if realmode == 'win32':
176 if realmode == 'win32':
174 if not w32effects and mode == 'win32':
177 if not w32effects and mode == 'win32':
175 # only warn if color.mode is explicitly set to win32
178 # only warn if color.mode is explicitly set to win32
176 ui.warn(_('warning: failed to set color mode to %s\n') % mode)
179 ui.warn(_('warning: failed to set color mode to %s\n') % mode)
177 return None
180 return None
178 _effects.update(w32effects)
181 _effects.update(w32effects)
179 elif realmode == 'ansi':
182 elif realmode == 'ansi':
180 _terminfo_params = {}
183 _terminfo_params = {}
181 elif realmode == 'terminfo':
184 elif realmode == 'terminfo':
182 _terminfosetup(ui, mode)
185 _terminfosetup(ui, mode)
183 if not _terminfo_params:
186 if not _terminfo_params:
184 if mode == 'terminfo':
187 if mode == 'terminfo':
185 ## FIXME Shouldn't we return None in this case too?
188 ## FIXME Shouldn't we return None in this case too?
186 # only warn if color.mode is explicitly set to win32
189 # only warn if color.mode is explicitly set to win32
187 ui.warn(_('warning: failed to set color mode to %s\n') % mode)
190 ui.warn(_('warning: failed to set color mode to %s\n') % mode)
188 realmode = 'ansi'
191 realmode = 'ansi'
189 else:
192 else:
190 return None
193 return None
191
194
192 if always or (auto and formatted):
195 if always or (auto and formatted):
193 return realmode
196 return realmode
194 return None
197 return None
195
198
196 try:
199 try:
197 import curses
200 import curses
198 # Mapping from effect name to terminfo attribute name or color number.
201 # Mapping from effect name to terminfo attribute name or color number.
199 # This will also force-load the curses module.
202 # This will also force-load the curses module.
200 _terminfo_params = {'none': (True, 'sgr0'),
203 _terminfo_params = {'none': (True, 'sgr0'),
201 'standout': (True, 'smso'),
204 'standout': (True, 'smso'),
202 'underline': (True, 'smul'),
205 'underline': (True, 'smul'),
203 'reverse': (True, 'rev'),
206 'reverse': (True, 'rev'),
204 'inverse': (True, 'rev'),
207 'inverse': (True, 'rev'),
205 'blink': (True, 'blink'),
208 'blink': (True, 'blink'),
206 'dim': (True, 'dim'),
209 'dim': (True, 'dim'),
207 'bold': (True, 'bold'),
210 'bold': (True, 'bold'),
208 'invisible': (True, 'invis'),
211 'invisible': (True, 'invis'),
209 'italic': (True, 'sitm'),
212 'italic': (True, 'sitm'),
210 'black': (False, curses.COLOR_BLACK),
213 'black': (False, curses.COLOR_BLACK),
211 'red': (False, curses.COLOR_RED),
214 'red': (False, curses.COLOR_RED),
212 'green': (False, curses.COLOR_GREEN),
215 'green': (False, curses.COLOR_GREEN),
213 'yellow': (False, curses.COLOR_YELLOW),
216 'yellow': (False, curses.COLOR_YELLOW),
214 'blue': (False, curses.COLOR_BLUE),
217 'blue': (False, curses.COLOR_BLUE),
215 'magenta': (False, curses.COLOR_MAGENTA),
218 'magenta': (False, curses.COLOR_MAGENTA),
216 'cyan': (False, curses.COLOR_CYAN),
219 'cyan': (False, curses.COLOR_CYAN),
217 'white': (False, curses.COLOR_WHITE)}
220 'white': (False, curses.COLOR_WHITE)}
218 except ImportError:
221 except ImportError:
219 _terminfo_params = False
222 _terminfo_params = False
220
223
221 _styles = {'grep.match': 'red bold',
224 _styles = {'grep.match': 'red bold',
222 'bookmarks.current': 'green',
225 'bookmarks.current': 'green',
223 'branches.active': 'none',
226 'branches.active': 'none',
224 'branches.closed': 'black bold',
227 'branches.closed': 'black bold',
225 'branches.current': 'green',
228 'branches.current': 'green',
226 'branches.inactive': 'none',
229 'branches.inactive': 'none',
227 'diff.changed': 'white',
230 'diff.changed': 'white',
228 'diff.deleted': 'red',
231 'diff.deleted': 'red',
229 'diff.diffline': 'bold',
232 'diff.diffline': 'bold',
230 'diff.extended': 'cyan bold',
233 'diff.extended': 'cyan bold',
231 'diff.file_a': 'red bold',
234 'diff.file_a': 'red bold',
232 'diff.file_b': 'green bold',
235 'diff.file_b': 'green bold',
233 'diff.hunk': 'magenta',
236 'diff.hunk': 'magenta',
234 'diff.inserted': 'green',
237 'diff.inserted': 'green',
235 'diff.trailingwhitespace': 'bold red_background',
238 'diff.trailingwhitespace': 'bold red_background',
236 'diffstat.deleted': 'red',
239 'diffstat.deleted': 'red',
237 'diffstat.inserted': 'green',
240 'diffstat.inserted': 'green',
238 'ui.prompt': 'yellow',
241 'ui.prompt': 'yellow',
239 'log.changeset': 'yellow',
242 'log.changeset': 'yellow',
240 'resolve.resolved': 'green bold',
243 'resolve.resolved': 'green bold',
241 'resolve.unresolved': 'red bold',
244 'resolve.unresolved': 'red bold',
242 'status.added': 'green bold',
245 'status.added': 'green bold',
243 'status.clean': 'none',
246 'status.clean': 'none',
244 'status.copied': 'none',
247 'status.copied': 'none',
245 'status.deleted': 'cyan bold underline',
248 'status.deleted': 'cyan bold underline',
246 'status.ignored': 'black bold',
249 'status.ignored': 'black bold',
247 'status.modified': 'blue bold',
250 'status.modified': 'blue bold',
248 'status.removed': 'red bold',
251 'status.removed': 'red bold',
249 'status.unknown': 'magenta bold underline'}
252 'status.unknown': 'magenta bold underline'}
250
253
251
254
252 def _effect_str(effect):
255 def _effect_str(effect):
253 '''Helper function for render_effects().'''
256 '''Helper function for render_effects().'''
254
257
255 bg = False
258 bg = False
256 if effect.endswith('_background'):
259 if effect.endswith('_background'):
257 bg = True
260 bg = True
258 effect = effect[:-11]
261 effect = effect[:-11]
259 attr, val = _terminfo_params[effect]
262 attr, val = _terminfo_params[effect]
260 if attr:
263 if attr:
261 return curses.tigetstr(val)
264 return curses.tigetstr(val)
262 elif bg:
265 elif bg:
263 return curses.tparm(curses.tigetstr('setab'), val)
266 return curses.tparm(curses.tigetstr('setab'), val)
264 else:
267 else:
265 return curses.tparm(curses.tigetstr('setaf'), val)
268 return curses.tparm(curses.tigetstr('setaf'), val)
266
269
267 def render_effects(text, effects):
270 def render_effects(text, effects):
268 'Wrap text in commands to turn on each effect.'
271 'Wrap text in commands to turn on each effect.'
269 if not text:
272 if not text:
270 return text
273 return text
271 if not _terminfo_params:
274 if not _terminfo_params:
272 start = [str(_effects[e]) for e in ['none'] + effects.split()]
275 start = [str(_effects[e]) for e in ['none'] + effects.split()]
273 start = '\033[' + ';'.join(start) + 'm'
276 start = '\033[' + ';'.join(start) + 'm'
274 stop = '\033[' + str(_effects['none']) + 'm'
277 stop = '\033[' + str(_effects['none']) + 'm'
275 else:
278 else:
276 start = ''.join(_effect_str(effect)
279 start = ''.join(_effect_str(effect)
277 for effect in ['none'] + effects.split())
280 for effect in ['none'] + effects.split())
278 stop = _effect_str('none')
281 stop = _effect_str('none')
279 return ''.join([start, text, stop])
282 return ''.join([start, text, stop])
280
283
281 def extstyles():
284 def extstyles():
282 for name, ext in extensions.extensions():
285 for name, ext in extensions.extensions():
283 _styles.update(getattr(ext, 'colortable', {}))
286 _styles.update(getattr(ext, 'colortable', {}))
284
287
285 def configstyles(ui):
288 def configstyles(ui):
286 for status, cfgeffects in ui.configitems('color'):
289 for status, cfgeffects in ui.configitems('color'):
287 if '.' not in status or status.startswith('color.'):
290 if '.' not in status or status.startswith('color.'):
288 continue
291 continue
289 cfgeffects = ui.configlist('color', status)
292 cfgeffects = ui.configlist('color', status)
290 if cfgeffects:
293 if cfgeffects:
291 good = []
294 good = []
292 for e in cfgeffects:
295 for e in cfgeffects:
293 if not _terminfo_params and e in _effects:
296 if not _terminfo_params and e in _effects:
294 good.append(e)
297 good.append(e)
295 elif e in _terminfo_params or e[:-11] in _terminfo_params:
298 elif e in _terminfo_params or e[:-11] in _terminfo_params:
296 good.append(e)
299 good.append(e)
297 else:
300 else:
298 ui.warn(_("ignoring unknown color/effect %r "
301 ui.warn(_("ignoring unknown color/effect %r "
299 "(configured in color.%s)\n")
302 "(configured in color.%s)\n")
300 % (e, status))
303 % (e, status))
301 _styles[status] = ' '.join(good)
304 _styles[status] = ' '.join(good)
302
305
303 class colorui(uimod.ui):
306 class colorui(uimod.ui):
304 def popbuffer(self, labeled=False):
307 def popbuffer(self, labeled=False):
305 if labeled:
308 if labeled:
306 return ''.join(self.label(a, label) for a, label
309 return ''.join(self.label(a, label) for a, label
307 in self._buffers.pop())
310 in self._buffers.pop())
308 return ''.join(a for a, label in self._buffers.pop())
311 return ''.join(a for a, label in self._buffers.pop())
309
312
310 _colormode = 'ansi'
313 _colormode = 'ansi'
311 def write(self, *args, **opts):
314 def write(self, *args, **opts):
312 label = opts.get('label', '')
315 label = opts.get('label', '')
313 if self._buffers:
316 if self._buffers:
314 self._buffers[-1].extend([(str(a), label) for a in args])
317 self._buffers[-1].extend([(str(a), label) for a in args])
315 elif self._colormode == 'win32':
318 elif self._colormode == 'win32':
316 for a in args:
319 for a in args:
317 win32print(a, super(colorui, self).write, **opts)
320 win32print(a, super(colorui, self).write, **opts)
318 else:
321 else:
319 return super(colorui, self).write(
322 return super(colorui, self).write(
320 *[self.label(str(a), label) for a in args], **opts)
323 *[self.label(str(a), label) for a in args], **opts)
321
324
322 def write_err(self, *args, **opts):
325 def write_err(self, *args, **opts):
323 label = opts.get('label', '')
326 label = opts.get('label', '')
324 if self._colormode == 'win32':
327 if self._colormode == 'win32':
325 for a in args:
328 for a in args:
326 win32print(a, super(colorui, self).write_err, **opts)
329 win32print(a, super(colorui, self).write_err, **opts)
327 else:
330 else:
328 return super(colorui, self).write_err(
331 return super(colorui, self).write_err(
329 *[self.label(str(a), label) for a in args], **opts)
332 *[self.label(str(a), label) for a in args], **opts)
330
333
331 def label(self, msg, label):
334 def label(self, msg, label):
332 effects = []
335 effects = []
333 for l in label.split():
336 for l in label.split():
334 s = _styles.get(l, '')
337 s = _styles.get(l, '')
335 if s:
338 if s:
336 effects.append(s)
339 effects.append(s)
337 effects = ' '.join(effects)
340 effects = ' '.join(effects)
338 if effects:
341 if effects:
339 return '\n'.join([render_effects(s, effects)
342 return '\n'.join([render_effects(s, effects)
340 for s in msg.split('\n')])
343 for s in msg.split('\n')])
341 return msg
344 return msg
342
345
343
346
344 def uisetup(ui):
347 def uisetup(ui):
345 global _terminfo_params
348 global _terminfo_params
346 if ui.plain():
349 if ui.plain():
347 return
350 return
348 def colorcmd(orig, ui_, opts, cmd, cmdfunc):
351 def colorcmd(orig, ui_, opts, cmd, cmdfunc):
349 mode = _modesetup(ui_, opts)
352 mode = _modesetup(ui_, opts)
350 if mode:
353 if mode:
351 colorui._colormode = mode
354 colorui._colormode = mode
352 if not issubclass(ui_.__class__, colorui):
355 if not issubclass(ui_.__class__, colorui):
353 colorui.__bases__ = (ui_.__class__,)
356 colorui.__bases__ = (ui_.__class__,)
354 ui_.__class__ = colorui
357 ui_.__class__ = colorui
355 extstyles()
358 extstyles()
356 configstyles(ui_)
359 configstyles(ui_)
357 return orig(ui_, opts, cmd, cmdfunc)
360 return orig(ui_, opts, cmd, cmdfunc)
358 extensions.wrapfunction(dispatch, '_runcommand', colorcmd)
361 extensions.wrapfunction(dispatch, '_runcommand', colorcmd)
359
362
360 def extsetup(ui):
363 def extsetup(ui):
361 commands.globalopts.append(
364 commands.globalopts.append(
362 ('', 'color', 'auto',
365 ('', 'color', 'auto',
363 # i18n: 'always', 'auto', and 'never' are keywords and should
366 # i18n: 'always', 'auto', and 'never' are keywords and should
364 # not be translated
367 # not be translated
365 _("when to colorize (boolean, always, auto, or never)"),
368 _("when to colorize (boolean, always, auto, or never)"),
366 _('TYPE')))
369 _('TYPE')))
367
370
368 if os.name != 'nt':
371 if os.name != 'nt':
369 w32effects = None
372 w32effects = None
370 else:
373 else:
371 import re, ctypes
374 import re, ctypes
372
375
373 _kernel32 = ctypes.windll.kernel32
376 _kernel32 = ctypes.windll.kernel32
374
377
375 _WORD = ctypes.c_ushort
378 _WORD = ctypes.c_ushort
376
379
377 _INVALID_HANDLE_VALUE = -1
380 _INVALID_HANDLE_VALUE = -1
378
381
379 class _COORD(ctypes.Structure):
382 class _COORD(ctypes.Structure):
380 _fields_ = [('X', ctypes.c_short),
383 _fields_ = [('X', ctypes.c_short),
381 ('Y', ctypes.c_short)]
384 ('Y', ctypes.c_short)]
382
385
383 class _SMALL_RECT(ctypes.Structure):
386 class _SMALL_RECT(ctypes.Structure):
384 _fields_ = [('Left', ctypes.c_short),
387 _fields_ = [('Left', ctypes.c_short),
385 ('Top', ctypes.c_short),
388 ('Top', ctypes.c_short),
386 ('Right', ctypes.c_short),
389 ('Right', ctypes.c_short),
387 ('Bottom', ctypes.c_short)]
390 ('Bottom', ctypes.c_short)]
388
391
389 class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
392 class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
390 _fields_ = [('dwSize', _COORD),
393 _fields_ = [('dwSize', _COORD),
391 ('dwCursorPosition', _COORD),
394 ('dwCursorPosition', _COORD),
392 ('wAttributes', _WORD),
395 ('wAttributes', _WORD),
393 ('srWindow', _SMALL_RECT),
396 ('srWindow', _SMALL_RECT),
394 ('dwMaximumWindowSize', _COORD)]
397 ('dwMaximumWindowSize', _COORD)]
395
398
396 _STD_OUTPUT_HANDLE = 0xfffffff5L # (DWORD)-11
399 _STD_OUTPUT_HANDLE = 0xfffffff5L # (DWORD)-11
397 _STD_ERROR_HANDLE = 0xfffffff4L # (DWORD)-12
400 _STD_ERROR_HANDLE = 0xfffffff4L # (DWORD)-12
398
401
399 _FOREGROUND_BLUE = 0x0001
402 _FOREGROUND_BLUE = 0x0001
400 _FOREGROUND_GREEN = 0x0002
403 _FOREGROUND_GREEN = 0x0002
401 _FOREGROUND_RED = 0x0004
404 _FOREGROUND_RED = 0x0004
402 _FOREGROUND_INTENSITY = 0x0008
405 _FOREGROUND_INTENSITY = 0x0008
403
406
404 _BACKGROUND_BLUE = 0x0010
407 _BACKGROUND_BLUE = 0x0010
405 _BACKGROUND_GREEN = 0x0020
408 _BACKGROUND_GREEN = 0x0020
406 _BACKGROUND_RED = 0x0040
409 _BACKGROUND_RED = 0x0040
407 _BACKGROUND_INTENSITY = 0x0080
410 _BACKGROUND_INTENSITY = 0x0080
408
411
409 _COMMON_LVB_REVERSE_VIDEO = 0x4000
412 _COMMON_LVB_REVERSE_VIDEO = 0x4000
410 _COMMON_LVB_UNDERSCORE = 0x8000
413 _COMMON_LVB_UNDERSCORE = 0x8000
411
414
412 # http://msdn.microsoft.com/en-us/library/ms682088%28VS.85%29.aspx
415 # http://msdn.microsoft.com/en-us/library/ms682088%28VS.85%29.aspx
413 w32effects = {
416 w32effects = {
414 'none': -1,
417 'none': -1,
415 'black': 0,
418 'black': 0,
416 'red': _FOREGROUND_RED,
419 'red': _FOREGROUND_RED,
417 'green': _FOREGROUND_GREEN,
420 'green': _FOREGROUND_GREEN,
418 'yellow': _FOREGROUND_RED | _FOREGROUND_GREEN,
421 'yellow': _FOREGROUND_RED | _FOREGROUND_GREEN,
419 'blue': _FOREGROUND_BLUE,
422 'blue': _FOREGROUND_BLUE,
420 'magenta': _FOREGROUND_BLUE | _FOREGROUND_RED,
423 'magenta': _FOREGROUND_BLUE | _FOREGROUND_RED,
421 'cyan': _FOREGROUND_BLUE | _FOREGROUND_GREEN,
424 'cyan': _FOREGROUND_BLUE | _FOREGROUND_GREEN,
422 'white': _FOREGROUND_RED | _FOREGROUND_GREEN | _FOREGROUND_BLUE,
425 'white': _FOREGROUND_RED | _FOREGROUND_GREEN | _FOREGROUND_BLUE,
423 'bold': _FOREGROUND_INTENSITY,
426 'bold': _FOREGROUND_INTENSITY,
424 'black_background': 0x100, # unused value > 0x0f
427 'black_background': 0x100, # unused value > 0x0f
425 'red_background': _BACKGROUND_RED,
428 'red_background': _BACKGROUND_RED,
426 'green_background': _BACKGROUND_GREEN,
429 'green_background': _BACKGROUND_GREEN,
427 'yellow_background': _BACKGROUND_RED | _BACKGROUND_GREEN,
430 'yellow_background': _BACKGROUND_RED | _BACKGROUND_GREEN,
428 'blue_background': _BACKGROUND_BLUE,
431 'blue_background': _BACKGROUND_BLUE,
429 'purple_background': _BACKGROUND_BLUE | _BACKGROUND_RED,
432 'purple_background': _BACKGROUND_BLUE | _BACKGROUND_RED,
430 'cyan_background': _BACKGROUND_BLUE | _BACKGROUND_GREEN,
433 'cyan_background': _BACKGROUND_BLUE | _BACKGROUND_GREEN,
431 'white_background': (_BACKGROUND_RED | _BACKGROUND_GREEN |
434 'white_background': (_BACKGROUND_RED | _BACKGROUND_GREEN |
432 _BACKGROUND_BLUE),
435 _BACKGROUND_BLUE),
433 'bold_background': _BACKGROUND_INTENSITY,
436 'bold_background': _BACKGROUND_INTENSITY,
434 'underline': _COMMON_LVB_UNDERSCORE, # double-byte charsets only
437 'underline': _COMMON_LVB_UNDERSCORE, # double-byte charsets only
435 'inverse': _COMMON_LVB_REVERSE_VIDEO, # double-byte charsets only
438 'inverse': _COMMON_LVB_REVERSE_VIDEO, # double-byte charsets only
436 }
439 }
437
440
438 passthrough = set([_FOREGROUND_INTENSITY,
441 passthrough = set([_FOREGROUND_INTENSITY,
439 _BACKGROUND_INTENSITY,
442 _BACKGROUND_INTENSITY,
440 _COMMON_LVB_UNDERSCORE,
443 _COMMON_LVB_UNDERSCORE,
441 _COMMON_LVB_REVERSE_VIDEO])
444 _COMMON_LVB_REVERSE_VIDEO])
442
445
443 stdout = _kernel32.GetStdHandle(
446 stdout = _kernel32.GetStdHandle(
444 _STD_OUTPUT_HANDLE) # don't close the handle returned
447 _STD_OUTPUT_HANDLE) # don't close the handle returned
445 if stdout is None or stdout == _INVALID_HANDLE_VALUE:
448 if stdout is None or stdout == _INVALID_HANDLE_VALUE:
446 w32effects = None
449 w32effects = None
447 else:
450 else:
448 csbi = _CONSOLE_SCREEN_BUFFER_INFO()
451 csbi = _CONSOLE_SCREEN_BUFFER_INFO()
449 if not _kernel32.GetConsoleScreenBufferInfo(
452 if not _kernel32.GetConsoleScreenBufferInfo(
450 stdout, ctypes.byref(csbi)):
453 stdout, ctypes.byref(csbi)):
451 # stdout may not support GetConsoleScreenBufferInfo()
454 # stdout may not support GetConsoleScreenBufferInfo()
452 # when called from subprocess or redirected
455 # when called from subprocess or redirected
453 w32effects = None
456 w32effects = None
454 else:
457 else:
455 origattr = csbi.wAttributes
458 origattr = csbi.wAttributes
456 ansire = re.compile('\033\[([^m]*)m([^\033]*)(.*)',
459 ansire = re.compile('\033\[([^m]*)m([^\033]*)(.*)',
457 re.MULTILINE | re.DOTALL)
460 re.MULTILINE | re.DOTALL)
458
461
459 def win32print(text, orig, **opts):
462 def win32print(text, orig, **opts):
460 label = opts.get('label', '')
463 label = opts.get('label', '')
461 attr = origattr
464 attr = origattr
462
465
463 def mapcolor(val, attr):
466 def mapcolor(val, attr):
464 if val == -1:
467 if val == -1:
465 return origattr
468 return origattr
466 elif val in passthrough:
469 elif val in passthrough:
467 return attr | val
470 return attr | val
468 elif val > 0x0f:
471 elif val > 0x0f:
469 return (val & 0x70) | (attr & 0x8f)
472 return (val & 0x70) | (attr & 0x8f)
470 else:
473 else:
471 return (val & 0x07) | (attr & 0xf8)
474 return (val & 0x07) | (attr & 0xf8)
472
475
473 # determine console attributes based on labels
476 # determine console attributes based on labels
474 for l in label.split():
477 for l in label.split():
475 style = _styles.get(l, '')
478 style = _styles.get(l, '')
476 for effect in style.split():
479 for effect in style.split():
477 attr = mapcolor(w32effects[effect], attr)
480 attr = mapcolor(w32effects[effect], attr)
478
481
479 # hack to ensure regexp finds data
482 # hack to ensure regexp finds data
480 if not text.startswith('\033['):
483 if not text.startswith('\033['):
481 text = '\033[m' + text
484 text = '\033[m' + text
482
485
483 # Look for ANSI-like codes embedded in text
486 # Look for ANSI-like codes embedded in text
484 m = re.match(ansire, text)
487 m = re.match(ansire, text)
485
488
486 try:
489 try:
487 while m:
490 while m:
488 for sattr in m.group(1).split(';'):
491 for sattr in m.group(1).split(';'):
489 if sattr:
492 if sattr:
490 attr = mapcolor(int(sattr), attr)
493 attr = mapcolor(int(sattr), attr)
491 _kernel32.SetConsoleTextAttribute(stdout, attr)
494 _kernel32.SetConsoleTextAttribute(stdout, attr)
492 orig(m.group(2), **opts)
495 orig(m.group(2), **opts)
493 m = re.match(ansire, m.group(3))
496 m = re.match(ansire, m.group(3))
494 finally:
497 finally:
495 # Explicity reset original attributes
498 # Explicity reset original attributes
496 _kernel32.SetConsoleTextAttribute(stdout, origattr)
499 _kernel32.SetConsoleTextAttribute(stdout, origattr)
@@ -1,5158 +1,5163 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing 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 node import hex, bin, nullid, nullrev, short
8 from node import hex, bin, nullid, nullrev, short
9 from lock import release
9 from lock import release
10 from i18n import _, gettext
10 from i18n import _, gettext
11 import os, re, difflib, time, tempfile, errno
11 import os, re, difflib, time, tempfile, errno
12 import hg, scmutil, util, revlog, extensions, copies, error, bookmarks
12 import hg, scmutil, util, revlog, extensions, copies, error, bookmarks
13 import patch, help, url, encoding, templatekw, discovery
13 import patch, help, url, encoding, templatekw, discovery
14 import archival, changegroup, cmdutil, hbisect
14 import archival, changegroup, cmdutil, hbisect
15 import sshserver, hgweb, hgweb.server, commandserver
15 import sshserver, hgweb, hgweb.server, commandserver
16 import merge as mergemod
16 import merge as mergemod
17 import minirst, revset, fileset
17 import minirst, revset, fileset
18 import dagparser, context, simplemerge
18 import dagparser, context, simplemerge
19 import random, setdiscovery, treediscovery, dagutil
19 import random, setdiscovery, treediscovery, dagutil
20
20
21 table = {}
21 table = {}
22
22
23 command = cmdutil.command(table)
23 command = cmdutil.command(table)
24
24
25 # common command options
25 # common command options
26
26
27 globalopts = [
27 globalopts = [
28 ('R', 'repository', '',
28 ('R', 'repository', '',
29 _('repository root directory or name of overlay bundle file'),
29 _('repository root directory or name of overlay bundle file'),
30 _('REPO')),
30 _('REPO')),
31 ('', 'cwd', '',
31 ('', 'cwd', '',
32 _('change working directory'), _('DIR')),
32 _('change working directory'), _('DIR')),
33 ('y', 'noninteractive', None,
33 ('y', 'noninteractive', None,
34 _('do not prompt, assume \'yes\' for any required answers')),
34 _('do not prompt, assume \'yes\' for any required answers')),
35 ('q', 'quiet', None, _('suppress output')),
35 ('q', 'quiet', None, _('suppress output')),
36 ('v', 'verbose', None, _('enable additional output')),
36 ('v', 'verbose', None, _('enable additional output')),
37 ('', 'config', [],
37 ('', 'config', [],
38 _('set/override config option (use \'section.name=value\')'),
38 _('set/override config option (use \'section.name=value\')'),
39 _('CONFIG')),
39 _('CONFIG')),
40 ('', 'debug', None, _('enable debugging output')),
40 ('', 'debug', None, _('enable debugging output')),
41 ('', 'debugger', None, _('start debugger')),
41 ('', 'debugger', None, _('start debugger')),
42 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
42 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
43 _('ENCODE')),
43 _('ENCODE')),
44 ('', 'encodingmode', encoding.encodingmode,
44 ('', 'encodingmode', encoding.encodingmode,
45 _('set the charset encoding mode'), _('MODE')),
45 _('set the charset encoding mode'), _('MODE')),
46 ('', 'traceback', None, _('always print a traceback on exception')),
46 ('', 'traceback', None, _('always print a traceback on exception')),
47 ('', 'time', None, _('time how long the command takes')),
47 ('', 'time', None, _('time how long the command takes')),
48 ('', 'profile', None, _('print command execution profile')),
48 ('', 'profile', None, _('print command execution profile')),
49 ('', 'version', None, _('output version information and exit')),
49 ('', 'version', None, _('output version information and exit')),
50 ('h', 'help', None, _('display help and exit')),
50 ('h', 'help', None, _('display help and exit')),
51 ]
51 ]
52
52
53 dryrunopts = [('n', 'dry-run', None,
53 dryrunopts = [('n', 'dry-run', None,
54 _('do not perform actions, just print output'))]
54 _('do not perform actions, just print output'))]
55
55
56 remoteopts = [
56 remoteopts = [
57 ('e', 'ssh', '',
57 ('e', 'ssh', '',
58 _('specify ssh command to use'), _('CMD')),
58 _('specify ssh command to use'), _('CMD')),
59 ('', 'remotecmd', '',
59 ('', 'remotecmd', '',
60 _('specify hg command to run on the remote side'), _('CMD')),
60 _('specify hg command to run on the remote side'), _('CMD')),
61 ('', 'insecure', None,
61 ('', 'insecure', None,
62 _('do not verify server certificate (ignoring web.cacerts config)')),
62 _('do not verify server certificate (ignoring web.cacerts config)')),
63 ]
63 ]
64
64
65 walkopts = [
65 walkopts = [
66 ('I', 'include', [],
66 ('I', 'include', [],
67 _('include names matching the given patterns'), _('PATTERN')),
67 _('include names matching the given patterns'), _('PATTERN')),
68 ('X', 'exclude', [],
68 ('X', 'exclude', [],
69 _('exclude names matching the given patterns'), _('PATTERN')),
69 _('exclude names matching the given patterns'), _('PATTERN')),
70 ]
70 ]
71
71
72 commitopts = [
72 commitopts = [
73 ('m', 'message', '',
73 ('m', 'message', '',
74 _('use text as commit message'), _('TEXT')),
74 _('use text as commit message'), _('TEXT')),
75 ('l', 'logfile', '',
75 ('l', 'logfile', '',
76 _('read commit message from file'), _('FILE')),
76 _('read commit message from file'), _('FILE')),
77 ]
77 ]
78
78
79 commitopts2 = [
79 commitopts2 = [
80 ('d', 'date', '',
80 ('d', 'date', '',
81 _('record the specified date as commit date'), _('DATE')),
81 _('record the specified date as commit date'), _('DATE')),
82 ('u', 'user', '',
82 ('u', 'user', '',
83 _('record the specified user as committer'), _('USER')),
83 _('record the specified user as committer'), _('USER')),
84 ]
84 ]
85
85
86 templateopts = [
86 templateopts = [
87 ('', 'style', '',
87 ('', 'style', '',
88 _('display using template map file'), _('STYLE')),
88 _('display using template map file'), _('STYLE')),
89 ('', 'template', '',
89 ('', 'template', '',
90 _('display with template'), _('TEMPLATE')),
90 _('display with template'), _('TEMPLATE')),
91 ]
91 ]
92
92
93 logopts = [
93 logopts = [
94 ('p', 'patch', None, _('show patch')),
94 ('p', 'patch', None, _('show patch')),
95 ('g', 'git', None, _('use git extended diff format')),
95 ('g', 'git', None, _('use git extended diff format')),
96 ('l', 'limit', '',
96 ('l', 'limit', '',
97 _('limit number of changes displayed'), _('NUM')),
97 _('limit number of changes displayed'), _('NUM')),
98 ('M', 'no-merges', None, _('do not show merges')),
98 ('M', 'no-merges', None, _('do not show merges')),
99 ('', 'stat', None, _('output diffstat-style summary of changes')),
99 ('', 'stat', None, _('output diffstat-style summary of changes')),
100 ] + templateopts
100 ] + templateopts
101
101
102 diffopts = [
102 diffopts = [
103 ('a', 'text', None, _('treat all files as text')),
103 ('a', 'text', None, _('treat all files as text')),
104 ('g', 'git', None, _('use git extended diff format')),
104 ('g', 'git', None, _('use git extended diff format')),
105 ('', 'nodates', None, _('omit dates from diff headers'))
105 ('', 'nodates', None, _('omit dates from diff headers'))
106 ]
106 ]
107
107
108 diffopts2 = [
108 diffopts2 = [
109 ('p', 'show-function', None, _('show which function each change is in')),
109 ('p', 'show-function', None, _('show which function each change is in')),
110 ('', 'reverse', None, _('produce a diff that undoes the changes')),
110 ('', 'reverse', None, _('produce a diff that undoes the changes')),
111 ('w', 'ignore-all-space', None,
111 ('w', 'ignore-all-space', None,
112 _('ignore white space when comparing lines')),
112 _('ignore white space when comparing lines')),
113 ('b', 'ignore-space-change', None,
113 ('b', 'ignore-space-change', None,
114 _('ignore changes in the amount of white space')),
114 _('ignore changes in the amount of white space')),
115 ('B', 'ignore-blank-lines', None,
115 ('B', 'ignore-blank-lines', None,
116 _('ignore changes whose lines are all blank')),
116 _('ignore changes whose lines are all blank')),
117 ('U', 'unified', '',
117 ('U', 'unified', '',
118 _('number of lines of context to show'), _('NUM')),
118 _('number of lines of context to show'), _('NUM')),
119 ('', 'stat', None, _('output diffstat-style summary of changes')),
119 ('', 'stat', None, _('output diffstat-style summary of changes')),
120 ]
120 ]
121
121
122 similarityopts = [
122 similarityopts = [
123 ('s', 'similarity', '',
123 ('s', 'similarity', '',
124 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
124 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
125 ]
125 ]
126
126
127 subrepoopts = [
127 subrepoopts = [
128 ('S', 'subrepos', None,
128 ('S', 'subrepos', None,
129 _('recurse into subrepositories'))
129 _('recurse into subrepositories'))
130 ]
130 ]
131
131
132 # Commands start here, listed alphabetically
132 # Commands start here, listed alphabetically
133
133
134 @command('^add',
134 @command('^add',
135 walkopts + subrepoopts + dryrunopts,
135 walkopts + subrepoopts + dryrunopts,
136 _('[OPTION]... [FILE]...'))
136 _('[OPTION]... [FILE]...'))
137 def add(ui, repo, *pats, **opts):
137 def add(ui, repo, *pats, **opts):
138 """add the specified files on the next commit
138 """add the specified files on the next commit
139
139
140 Schedule files to be version controlled and added to the
140 Schedule files to be version controlled and added to the
141 repository.
141 repository.
142
142
143 The files will be added to the repository at the next commit. To
143 The files will be added to the repository at the next commit. To
144 undo an add before that, see :hg:`forget`.
144 undo an add before that, see :hg:`forget`.
145
145
146 If no names are given, add all files to the repository.
146 If no names are given, add all files to the repository.
147
147
148 .. container:: verbose
148 .. container:: verbose
149
149
150 An example showing how new (unknown) files are added
150 An example showing how new (unknown) files are added
151 automatically by :hg:`add`::
151 automatically by :hg:`add`::
152
152
153 $ ls
153 $ ls
154 foo.c
154 foo.c
155 $ hg status
155 $ hg status
156 ? foo.c
156 ? foo.c
157 $ hg add
157 $ hg add
158 adding foo.c
158 adding foo.c
159 $ hg status
159 $ hg status
160 A foo.c
160 A foo.c
161
161
162 Returns 0 if all files are successfully added.
162 Returns 0 if all files are successfully added.
163 """
163 """
164
164
165 m = scmutil.match(repo[None], pats, opts)
165 m = scmutil.match(repo[None], pats, opts)
166 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
166 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
167 opts.get('subrepos'), prefix="")
167 opts.get('subrepos'), prefix="")
168 return rejected and 1 or 0
168 return rejected and 1 or 0
169
169
170 @command('addremove',
170 @command('addremove',
171 similarityopts + walkopts + dryrunopts,
171 similarityopts + walkopts + dryrunopts,
172 _('[OPTION]... [FILE]...'))
172 _('[OPTION]... [FILE]...'))
173 def addremove(ui, repo, *pats, **opts):
173 def addremove(ui, repo, *pats, **opts):
174 """add all new files, delete all missing files
174 """add all new files, delete all missing files
175
175
176 Add all new files and remove all missing files from the
176 Add all new files and remove all missing files from the
177 repository.
177 repository.
178
178
179 New files are ignored if they match any of the patterns in
179 New files are ignored if they match any of the patterns in
180 ``.hgignore``. As with add, these changes take effect at the next
180 ``.hgignore``. As with add, these changes take effect at the next
181 commit.
181 commit.
182
182
183 Use the -s/--similarity option to detect renamed files. With a
183 Use the -s/--similarity option to detect renamed files. With a
184 parameter greater than 0, this compares every removed file with
184 parameter greater than 0, this compares every removed file with
185 every added file and records those similar enough as renames. This
185 every added file and records those similar enough as renames. This
186 option takes a percentage between 0 (disabled) and 100 (files must
186 option takes a percentage between 0 (disabled) and 100 (files must
187 be identical) as its parameter. Detecting renamed files this way
187 be identical) as its parameter. Detecting renamed files this way
188 can be expensive. After using this option, :hg:`status -C` can be
188 can be expensive. After using this option, :hg:`status -C` can be
189 used to check which files were identified as moved or renamed.
189 used to check which files were identified as moved or renamed.
190
190
191 Returns 0 if all files are successfully added.
191 Returns 0 if all files are successfully added.
192 """
192 """
193 try:
193 try:
194 sim = float(opts.get('similarity') or 100)
194 sim = float(opts.get('similarity') or 100)
195 except ValueError:
195 except ValueError:
196 raise util.Abort(_('similarity must be a number'))
196 raise util.Abort(_('similarity must be a number'))
197 if sim < 0 or sim > 100:
197 if sim < 0 or sim > 100:
198 raise util.Abort(_('similarity must be between 0 and 100'))
198 raise util.Abort(_('similarity must be between 0 and 100'))
199 return scmutil.addremove(repo, pats, opts, similarity=sim / 100.0)
199 return scmutil.addremove(repo, pats, opts, similarity=sim / 100.0)
200
200
201 @command('^annotate|blame',
201 @command('^annotate|blame',
202 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
202 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
203 ('', 'follow', None,
203 ('', 'follow', None,
204 _('follow copies/renames and list the filename (DEPRECATED)')),
204 _('follow copies/renames and list the filename (DEPRECATED)')),
205 ('', 'no-follow', None, _("don't follow copies and renames")),
205 ('', 'no-follow', None, _("don't follow copies and renames")),
206 ('a', 'text', None, _('treat all files as text')),
206 ('a', 'text', None, _('treat all files as text')),
207 ('u', 'user', None, _('list the author (long with -v)')),
207 ('u', 'user', None, _('list the author (long with -v)')),
208 ('f', 'file', None, _('list the filename')),
208 ('f', 'file', None, _('list the filename')),
209 ('d', 'date', None, _('list the date (short with -q)')),
209 ('d', 'date', None, _('list the date (short with -q)')),
210 ('n', 'number', None, _('list the revision number (default)')),
210 ('n', 'number', None, _('list the revision number (default)')),
211 ('c', 'changeset', None, _('list the changeset')),
211 ('c', 'changeset', None, _('list the changeset')),
212 ('l', 'line-number', None, _('show line number at the first appearance'))
212 ('l', 'line-number', None, _('show line number at the first appearance'))
213 ] + walkopts,
213 ] + walkopts,
214 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'))
214 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'))
215 def annotate(ui, repo, *pats, **opts):
215 def annotate(ui, repo, *pats, **opts):
216 """show changeset information by line for each file
216 """show changeset information by line for each file
217
217
218 List changes in files, showing the revision id responsible for
218 List changes in files, showing the revision id responsible for
219 each line
219 each line
220
220
221 This command is useful for discovering when a change was made and
221 This command is useful for discovering when a change was made and
222 by whom.
222 by whom.
223
223
224 Without the -a/--text option, annotate will avoid processing files
224 Without the -a/--text option, annotate will avoid processing files
225 it detects as binary. With -a, annotate will annotate the file
225 it detects as binary. With -a, annotate will annotate the file
226 anyway, although the results will probably be neither useful
226 anyway, although the results will probably be neither useful
227 nor desirable.
227 nor desirable.
228
228
229 Returns 0 on success.
229 Returns 0 on success.
230 """
230 """
231 if opts.get('follow'):
231 if opts.get('follow'):
232 # --follow is deprecated and now just an alias for -f/--file
232 # --follow is deprecated and now just an alias for -f/--file
233 # to mimic the behavior of Mercurial before version 1.5
233 # to mimic the behavior of Mercurial before version 1.5
234 opts['file'] = True
234 opts['file'] = True
235
235
236 datefunc = ui.quiet and util.shortdate or util.datestr
236 datefunc = ui.quiet and util.shortdate or util.datestr
237 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
237 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
238
238
239 if not pats:
239 if not pats:
240 raise util.Abort(_('at least one filename or pattern is required'))
240 raise util.Abort(_('at least one filename or pattern is required'))
241
241
242 opmap = [('user', ' ', lambda x: ui.shortuser(x[0].user())),
242 opmap = [('user', ' ', lambda x: ui.shortuser(x[0].user())),
243 ('number', ' ', lambda x: str(x[0].rev())),
243 ('number', ' ', lambda x: str(x[0].rev())),
244 ('changeset', ' ', lambda x: short(x[0].node())),
244 ('changeset', ' ', lambda x: short(x[0].node())),
245 ('date', ' ', getdate),
245 ('date', ' ', getdate),
246 ('file', ' ', lambda x: x[0].path()),
246 ('file', ' ', lambda x: x[0].path()),
247 ('line_number', ':', lambda x: str(x[1])),
247 ('line_number', ':', lambda x: str(x[1])),
248 ]
248 ]
249
249
250 if (not opts.get('user') and not opts.get('changeset')
250 if (not opts.get('user') and not opts.get('changeset')
251 and not opts.get('date') and not opts.get('file')):
251 and not opts.get('date') and not opts.get('file')):
252 opts['number'] = True
252 opts['number'] = True
253
253
254 linenumber = opts.get('line_number') is not None
254 linenumber = opts.get('line_number') is not None
255 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
255 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
256 raise util.Abort(_('at least one of -n/-c is required for -l'))
256 raise util.Abort(_('at least one of -n/-c is required for -l'))
257
257
258 funcmap = [(func, sep) for op, sep, func in opmap if opts.get(op)]
258 funcmap = [(func, sep) for op, sep, func in opmap if opts.get(op)]
259 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
259 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
260
260
261 def bad(x, y):
261 def bad(x, y):
262 raise util.Abort("%s: %s" % (x, y))
262 raise util.Abort("%s: %s" % (x, y))
263
263
264 ctx = scmutil.revsingle(repo, opts.get('rev'))
264 ctx = scmutil.revsingle(repo, opts.get('rev'))
265 m = scmutil.match(ctx, pats, opts)
265 m = scmutil.match(ctx, pats, opts)
266 m.bad = bad
266 m.bad = bad
267 follow = not opts.get('no_follow')
267 follow = not opts.get('no_follow')
268 for abs in ctx.walk(m):
268 for abs in ctx.walk(m):
269 fctx = ctx[abs]
269 fctx = ctx[abs]
270 if not opts.get('text') and util.binary(fctx.data()):
270 if not opts.get('text') and util.binary(fctx.data()):
271 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
271 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
272 continue
272 continue
273
273
274 lines = fctx.annotate(follow=follow, linenumber=linenumber)
274 lines = fctx.annotate(follow=follow, linenumber=linenumber)
275 pieces = []
275 pieces = []
276
276
277 for f, sep in funcmap:
277 for f, sep in funcmap:
278 l = [f(n) for n, dummy in lines]
278 l = [f(n) for n, dummy in lines]
279 if l:
279 if l:
280 sized = [(x, encoding.colwidth(x)) for x in l]
280 sized = [(x, encoding.colwidth(x)) for x in l]
281 ml = max([w for x, w in sized])
281 ml = max([w for x, w in sized])
282 pieces.append(["%s%s%s" % (sep, ' ' * (ml - w), x)
282 pieces.append(["%s%s%s" % (sep, ' ' * (ml - w), x)
283 for x, w in sized])
283 for x, w in sized])
284
284
285 if pieces:
285 if pieces:
286 for p, l in zip(zip(*pieces), lines):
286 for p, l in zip(zip(*pieces), lines):
287 ui.write("%s: %s" % ("".join(p), l[1]))
287 ui.write("%s: %s" % ("".join(p), l[1]))
288
288
289 @command('archive',
289 @command('archive',
290 [('', 'no-decode', None, _('do not pass files through decoders')),
290 [('', 'no-decode', None, _('do not pass files through decoders')),
291 ('p', 'prefix', '', _('directory prefix for files in archive'),
291 ('p', 'prefix', '', _('directory prefix for files in archive'),
292 _('PREFIX')),
292 _('PREFIX')),
293 ('r', 'rev', '', _('revision to distribute'), _('REV')),
293 ('r', 'rev', '', _('revision to distribute'), _('REV')),
294 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
294 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
295 ] + subrepoopts + walkopts,
295 ] + subrepoopts + walkopts,
296 _('[OPTION]... DEST'))
296 _('[OPTION]... DEST'))
297 def archive(ui, repo, dest, **opts):
297 def archive(ui, repo, dest, **opts):
298 '''create an unversioned archive of a repository revision
298 '''create an unversioned archive of a repository revision
299
299
300 By default, the revision used is the parent of the working
300 By default, the revision used is the parent of the working
301 directory; use -r/--rev to specify a different revision.
301 directory; use -r/--rev to specify a different revision.
302
302
303 The archive type is automatically detected based on file
303 The archive type is automatically detected based on file
304 extension (or override using -t/--type).
304 extension (or override using -t/--type).
305
305
306 Valid types are:
306 Valid types are:
307
307
308 :``files``: a directory full of files (default)
308 :``files``: a directory full of files (default)
309 :``tar``: tar archive, uncompressed
309 :``tar``: tar archive, uncompressed
310 :``tbz2``: tar archive, compressed using bzip2
310 :``tbz2``: tar archive, compressed using bzip2
311 :``tgz``: tar archive, compressed using gzip
311 :``tgz``: tar archive, compressed using gzip
312 :``uzip``: zip archive, uncompressed
312 :``uzip``: zip archive, uncompressed
313 :``zip``: zip archive, compressed using deflate
313 :``zip``: zip archive, compressed using deflate
314
314
315 The exact name of the destination archive or directory is given
315 The exact name of the destination archive or directory is given
316 using a format string; see :hg:`help export` for details.
316 using a format string; see :hg:`help export` for details.
317
317
318 Each member added to an archive file has a directory prefix
318 Each member added to an archive file has a directory prefix
319 prepended. Use -p/--prefix to specify a format string for the
319 prepended. Use -p/--prefix to specify a format string for the
320 prefix. The default is the basename of the archive, with suffixes
320 prefix. The default is the basename of the archive, with suffixes
321 removed.
321 removed.
322
322
323 Returns 0 on success.
323 Returns 0 on success.
324 '''
324 '''
325
325
326 ctx = scmutil.revsingle(repo, opts.get('rev'))
326 ctx = scmutil.revsingle(repo, opts.get('rev'))
327 if not ctx:
327 if not ctx:
328 raise util.Abort(_('no working directory: please specify a revision'))
328 raise util.Abort(_('no working directory: please specify a revision'))
329 node = ctx.node()
329 node = ctx.node()
330 dest = cmdutil.makefilename(repo, dest, node)
330 dest = cmdutil.makefilename(repo, dest, node)
331 if os.path.realpath(dest) == repo.root:
331 if os.path.realpath(dest) == repo.root:
332 raise util.Abort(_('repository root cannot be destination'))
332 raise util.Abort(_('repository root cannot be destination'))
333
333
334 kind = opts.get('type') or archival.guesskind(dest) or 'files'
334 kind = opts.get('type') or archival.guesskind(dest) or 'files'
335 prefix = opts.get('prefix')
335 prefix = opts.get('prefix')
336
336
337 if dest == '-':
337 if dest == '-':
338 if kind == 'files':
338 if kind == 'files':
339 raise util.Abort(_('cannot archive plain files to stdout'))
339 raise util.Abort(_('cannot archive plain files to stdout'))
340 dest = cmdutil.makefileobj(repo, dest)
340 dest = cmdutil.makefileobj(repo, dest)
341 if not prefix:
341 if not prefix:
342 prefix = os.path.basename(repo.root) + '-%h'
342 prefix = os.path.basename(repo.root) + '-%h'
343
343
344 prefix = cmdutil.makefilename(repo, prefix, node)
344 prefix = cmdutil.makefilename(repo, prefix, node)
345 matchfn = scmutil.match(ctx, [], opts)
345 matchfn = scmutil.match(ctx, [], opts)
346 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
346 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
347 matchfn, prefix, subrepos=opts.get('subrepos'))
347 matchfn, prefix, subrepos=opts.get('subrepos'))
348
348
349 @command('backout',
349 @command('backout',
350 [('', 'merge', None, _('merge with old dirstate parent after backout')),
350 [('', 'merge', None, _('merge with old dirstate parent after backout')),
351 ('', 'parent', '', _('parent to choose when backing out merge'), _('REV')),
351 ('', 'parent', '', _('parent to choose when backing out merge'), _('REV')),
352 ('t', 'tool', '', _('specify merge tool')),
352 ('t', 'tool', '', _('specify merge tool')),
353 ('r', 'rev', '', _('revision to backout'), _('REV')),
353 ('r', 'rev', '', _('revision to backout'), _('REV')),
354 ] + walkopts + commitopts + commitopts2,
354 ] + walkopts + commitopts + commitopts2,
355 _('[OPTION]... [-r] REV'))
355 _('[OPTION]... [-r] REV'))
356 def backout(ui, repo, node=None, rev=None, **opts):
356 def backout(ui, repo, node=None, rev=None, **opts):
357 '''reverse effect of earlier changeset
357 '''reverse effect of earlier changeset
358
358
359 Prepare a new changeset with the effect of REV undone in the
359 Prepare a new changeset with the effect of REV undone in the
360 current working directory.
360 current working directory.
361
361
362 If REV is the parent of the working directory, then this new changeset
362 If REV is the parent of the working directory, then this new changeset
363 is committed automatically. Otherwise, hg needs to merge the
363 is committed automatically. Otherwise, hg needs to merge the
364 changes and the merged result is left uncommitted.
364 changes and the merged result is left uncommitted.
365
365
366 By default, the pending changeset will have one parent,
366 By default, the pending changeset will have one parent,
367 maintaining a linear history. With --merge, the pending changeset
367 maintaining a linear history. With --merge, the pending changeset
368 will instead have two parents: the old parent of the working
368 will instead have two parents: the old parent of the working
369 directory and a new child of REV that simply undoes REV.
369 directory and a new child of REV that simply undoes REV.
370
370
371 Before version 1.7, the behavior without --merge was equivalent to
371 Before version 1.7, the behavior without --merge was equivalent to
372 specifying --merge followed by :hg:`update --clean .` to cancel
372 specifying --merge followed by :hg:`update --clean .` to cancel
373 the merge and leave the child of REV as a head to be merged
373 the merge and leave the child of REV as a head to be merged
374 separately.
374 separately.
375
375
376 See :hg:`help dates` for a list of formats valid for -d/--date.
376 See :hg:`help dates` for a list of formats valid for -d/--date.
377
377
378 Returns 0 on success.
378 Returns 0 on success.
379 '''
379 '''
380 if rev and node:
380 if rev and node:
381 raise util.Abort(_("please specify just one revision"))
381 raise util.Abort(_("please specify just one revision"))
382
382
383 if not rev:
383 if not rev:
384 rev = node
384 rev = node
385
385
386 if not rev:
386 if not rev:
387 raise util.Abort(_("please specify a revision to backout"))
387 raise util.Abort(_("please specify a revision to backout"))
388
388
389 date = opts.get('date')
389 date = opts.get('date')
390 if date:
390 if date:
391 opts['date'] = util.parsedate(date)
391 opts['date'] = util.parsedate(date)
392
392
393 cmdutil.bailifchanged(repo)
393 cmdutil.bailifchanged(repo)
394 node = scmutil.revsingle(repo, rev).node()
394 node = scmutil.revsingle(repo, rev).node()
395
395
396 op1, op2 = repo.dirstate.parents()
396 op1, op2 = repo.dirstate.parents()
397 a = repo.changelog.ancestor(op1, node)
397 a = repo.changelog.ancestor(op1, node)
398 if a != node:
398 if a != node:
399 raise util.Abort(_('cannot backout change on a different branch'))
399 raise util.Abort(_('cannot backout change on a different branch'))
400
400
401 p1, p2 = repo.changelog.parents(node)
401 p1, p2 = repo.changelog.parents(node)
402 if p1 == nullid:
402 if p1 == nullid:
403 raise util.Abort(_('cannot backout a change with no parents'))
403 raise util.Abort(_('cannot backout a change with no parents'))
404 if p2 != nullid:
404 if p2 != nullid:
405 if not opts.get('parent'):
405 if not opts.get('parent'):
406 raise util.Abort(_('cannot backout a merge changeset without '
406 raise util.Abort(_('cannot backout a merge changeset without '
407 '--parent'))
407 '--parent'))
408 p = repo.lookup(opts['parent'])
408 p = repo.lookup(opts['parent'])
409 if p not in (p1, p2):
409 if p not in (p1, p2):
410 raise util.Abort(_('%s is not a parent of %s') %
410 raise util.Abort(_('%s is not a parent of %s') %
411 (short(p), short(node)))
411 (short(p), short(node)))
412 parent = p
412 parent = p
413 else:
413 else:
414 if opts.get('parent'):
414 if opts.get('parent'):
415 raise util.Abort(_('cannot use --parent on non-merge changeset'))
415 raise util.Abort(_('cannot use --parent on non-merge changeset'))
416 parent = p1
416 parent = p1
417
417
418 # the backout should appear on the same branch
418 # the backout should appear on the same branch
419 branch = repo.dirstate.branch()
419 branch = repo.dirstate.branch()
420 hg.clean(repo, node, show_stats=False)
420 hg.clean(repo, node, show_stats=False)
421 repo.dirstate.setbranch(branch)
421 repo.dirstate.setbranch(branch)
422 revert_opts = opts.copy()
422 revert_opts = opts.copy()
423 revert_opts['date'] = None
423 revert_opts['date'] = None
424 revert_opts['all'] = True
424 revert_opts['all'] = True
425 revert_opts['rev'] = hex(parent)
425 revert_opts['rev'] = hex(parent)
426 revert_opts['no_backup'] = None
426 revert_opts['no_backup'] = None
427 revert(ui, repo, **revert_opts)
427 revert(ui, repo, **revert_opts)
428 if not opts.get('merge') and op1 != node:
428 if not opts.get('merge') and op1 != node:
429 try:
429 try:
430 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
430 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
431 return hg.update(repo, op1)
431 return hg.update(repo, op1)
432 finally:
432 finally:
433 ui.setconfig('ui', 'forcemerge', '')
433 ui.setconfig('ui', 'forcemerge', '')
434
434
435 commit_opts = opts.copy()
435 commit_opts = opts.copy()
436 commit_opts['addremove'] = False
436 commit_opts['addremove'] = False
437 if not commit_opts['message'] and not commit_opts['logfile']:
437 if not commit_opts['message'] and not commit_opts['logfile']:
438 # we don't translate commit messages
438 # we don't translate commit messages
439 commit_opts['message'] = "Backed out changeset %s" % short(node)
439 commit_opts['message'] = "Backed out changeset %s" % short(node)
440 commit_opts['force_editor'] = True
440 commit_opts['force_editor'] = True
441 commit(ui, repo, **commit_opts)
441 commit(ui, repo, **commit_opts)
442 def nice(node):
442 def nice(node):
443 return '%d:%s' % (repo.changelog.rev(node), short(node))
443 return '%d:%s' % (repo.changelog.rev(node), short(node))
444 ui.status(_('changeset %s backs out changeset %s\n') %
444 ui.status(_('changeset %s backs out changeset %s\n') %
445 (nice(repo.changelog.tip()), nice(node)))
445 (nice(repo.changelog.tip()), nice(node)))
446 if opts.get('merge') and op1 != node:
446 if opts.get('merge') and op1 != node:
447 hg.clean(repo, op1, show_stats=False)
447 hg.clean(repo, op1, show_stats=False)
448 ui.status(_('merging with changeset %s\n')
448 ui.status(_('merging with changeset %s\n')
449 % nice(repo.changelog.tip()))
449 % nice(repo.changelog.tip()))
450 try:
450 try:
451 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
451 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
452 return hg.merge(repo, hex(repo.changelog.tip()))
452 return hg.merge(repo, hex(repo.changelog.tip()))
453 finally:
453 finally:
454 ui.setconfig('ui', 'forcemerge', '')
454 ui.setconfig('ui', 'forcemerge', '')
455 return 0
455 return 0
456
456
457 @command('bisect',
457 @command('bisect',
458 [('r', 'reset', False, _('reset bisect state')),
458 [('r', 'reset', False, _('reset bisect state')),
459 ('g', 'good', False, _('mark changeset good')),
459 ('g', 'good', False, _('mark changeset good')),
460 ('b', 'bad', False, _('mark changeset bad')),
460 ('b', 'bad', False, _('mark changeset bad')),
461 ('s', 'skip', False, _('skip testing changeset')),
461 ('s', 'skip', False, _('skip testing changeset')),
462 ('e', 'extend', False, _('extend the bisect range')),
462 ('e', 'extend', False, _('extend the bisect range')),
463 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
463 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
464 ('U', 'noupdate', False, _('do not update to target'))],
464 ('U', 'noupdate', False, _('do not update to target'))],
465 _("[-gbsr] [-U] [-c CMD] [REV]"))
465 _("[-gbsr] [-U] [-c CMD] [REV]"))
466 def bisect(ui, repo, rev=None, extra=None, command=None,
466 def bisect(ui, repo, rev=None, extra=None, command=None,
467 reset=None, good=None, bad=None, skip=None, extend=None,
467 reset=None, good=None, bad=None, skip=None, extend=None,
468 noupdate=None):
468 noupdate=None):
469 """subdivision search of changesets
469 """subdivision search of changesets
470
470
471 This command helps to find changesets which introduce problems. To
471 This command helps to find changesets which introduce problems. To
472 use, mark the earliest changeset you know exhibits the problem as
472 use, mark the earliest changeset you know exhibits the problem as
473 bad, then mark the latest changeset which is free from the problem
473 bad, then mark the latest changeset which is free from the problem
474 as good. Bisect will update your working directory to a revision
474 as good. Bisect will update your working directory to a revision
475 for testing (unless the -U/--noupdate option is specified). Once
475 for testing (unless the -U/--noupdate option is specified). Once
476 you have performed tests, mark the working directory as good or
476 you have performed tests, mark the working directory as good or
477 bad, and bisect will either update to another candidate changeset
477 bad, and bisect will either update to another candidate changeset
478 or announce that it has found the bad revision.
478 or announce that it has found the bad revision.
479
479
480 As a shortcut, you can also use the revision argument to mark a
480 As a shortcut, you can also use the revision argument to mark a
481 revision as good or bad without checking it out first.
481 revision as good or bad without checking it out first.
482
482
483 If you supply a command, it will be used for automatic bisection.
483 If you supply a command, it will be used for automatic bisection.
484 Its exit status will be used to mark revisions as good or bad:
484 Its exit status will be used to mark revisions as good or bad:
485 status 0 means good, 125 means to skip the revision, 127
485 status 0 means good, 125 means to skip the revision, 127
486 (command not found) will abort the bisection, and any other
486 (command not found) will abort the bisection, and any other
487 non-zero exit status means the revision is bad.
487 non-zero exit status means the revision is bad.
488
488
489 Returns 0 on success.
489 Returns 0 on success.
490 """
490 """
491 def extendbisectrange(nodes, good):
491 def extendbisectrange(nodes, good):
492 # bisect is incomplete when it ends on a merge node and
492 # bisect is incomplete when it ends on a merge node and
493 # one of the parent was not checked.
493 # one of the parent was not checked.
494 parents = repo[nodes[0]].parents()
494 parents = repo[nodes[0]].parents()
495 if len(parents) > 1:
495 if len(parents) > 1:
496 side = good and state['bad'] or state['good']
496 side = good and state['bad'] or state['good']
497 num = len(set(i.node() for i in parents) & set(side))
497 num = len(set(i.node() for i in parents) & set(side))
498 if num == 1:
498 if num == 1:
499 return parents[0].ancestor(parents[1])
499 return parents[0].ancestor(parents[1])
500 return None
500 return None
501
501
502 def print_result(nodes, good):
502 def print_result(nodes, good):
503 displayer = cmdutil.show_changeset(ui, repo, {})
503 displayer = cmdutil.show_changeset(ui, repo, {})
504 if len(nodes) == 1:
504 if len(nodes) == 1:
505 # narrowed it down to a single revision
505 # narrowed it down to a single revision
506 if good:
506 if good:
507 ui.write(_("The first good revision is:\n"))
507 ui.write(_("The first good revision is:\n"))
508 else:
508 else:
509 ui.write(_("The first bad revision is:\n"))
509 ui.write(_("The first bad revision is:\n"))
510 displayer.show(repo[nodes[0]])
510 displayer.show(repo[nodes[0]])
511 extendnode = extendbisectrange(nodes, good)
511 extendnode = extendbisectrange(nodes, good)
512 if extendnode is not None:
512 if extendnode is not None:
513 ui.write(_('Not all ancestors of this changeset have been'
513 ui.write(_('Not all ancestors of this changeset have been'
514 ' checked.\nUse bisect --extend to continue the '
514 ' checked.\nUse bisect --extend to continue the '
515 'bisection from\nthe common ancestor, %s.\n')
515 'bisection from\nthe common ancestor, %s.\n')
516 % extendnode)
516 % extendnode)
517 else:
517 else:
518 # multiple possible revisions
518 # multiple possible revisions
519 if good:
519 if good:
520 ui.write(_("Due to skipped revisions, the first "
520 ui.write(_("Due to skipped revisions, the first "
521 "good revision could be any of:\n"))
521 "good revision could be any of:\n"))
522 else:
522 else:
523 ui.write(_("Due to skipped revisions, the first "
523 ui.write(_("Due to skipped revisions, the first "
524 "bad revision could be any of:\n"))
524 "bad revision could be any of:\n"))
525 for n in nodes:
525 for n in nodes:
526 displayer.show(repo[n])
526 displayer.show(repo[n])
527 displayer.close()
527 displayer.close()
528
528
529 def check_state(state, interactive=True):
529 def check_state(state, interactive=True):
530 if not state['good'] or not state['bad']:
530 if not state['good'] or not state['bad']:
531 if (good or bad or skip or reset) and interactive:
531 if (good or bad or skip or reset) and interactive:
532 return
532 return
533 if not state['good']:
533 if not state['good']:
534 raise util.Abort(_('cannot bisect (no known good revisions)'))
534 raise util.Abort(_('cannot bisect (no known good revisions)'))
535 else:
535 else:
536 raise util.Abort(_('cannot bisect (no known bad revisions)'))
536 raise util.Abort(_('cannot bisect (no known bad revisions)'))
537 return True
537 return True
538
538
539 # backward compatibility
539 # backward compatibility
540 if rev in "good bad reset init".split():
540 if rev in "good bad reset init".split():
541 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
541 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
542 cmd, rev, extra = rev, extra, None
542 cmd, rev, extra = rev, extra, None
543 if cmd == "good":
543 if cmd == "good":
544 good = True
544 good = True
545 elif cmd == "bad":
545 elif cmd == "bad":
546 bad = True
546 bad = True
547 else:
547 else:
548 reset = True
548 reset = True
549 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
549 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
550 raise util.Abort(_('incompatible arguments'))
550 raise util.Abort(_('incompatible arguments'))
551
551
552 if reset:
552 if reset:
553 p = repo.join("bisect.state")
553 p = repo.join("bisect.state")
554 if os.path.exists(p):
554 if os.path.exists(p):
555 os.unlink(p)
555 os.unlink(p)
556 return
556 return
557
557
558 state = hbisect.load_state(repo)
558 state = hbisect.load_state(repo)
559
559
560 if command:
560 if command:
561 changesets = 1
561 changesets = 1
562 try:
562 try:
563 while changesets:
563 while changesets:
564 # update state
564 # update state
565 status = util.system(command, out=ui.fout)
565 status = util.system(command, out=ui.fout)
566 if status == 125:
566 if status == 125:
567 transition = "skip"
567 transition = "skip"
568 elif status == 0:
568 elif status == 0:
569 transition = "good"
569 transition = "good"
570 # status < 0 means process was killed
570 # status < 0 means process was killed
571 elif status == 127:
571 elif status == 127:
572 raise util.Abort(_("failed to execute %s") % command)
572 raise util.Abort(_("failed to execute %s") % command)
573 elif status < 0:
573 elif status < 0:
574 raise util.Abort(_("%s killed") % command)
574 raise util.Abort(_("%s killed") % command)
575 else:
575 else:
576 transition = "bad"
576 transition = "bad"
577 ctx = scmutil.revsingle(repo, rev)
577 ctx = scmutil.revsingle(repo, rev)
578 rev = None # clear for future iterations
578 rev = None # clear for future iterations
579 state[transition].append(ctx.node())
579 state[transition].append(ctx.node())
580 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
580 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
581 check_state(state, interactive=False)
581 check_state(state, interactive=False)
582 # bisect
582 # bisect
583 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
583 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
584 # update to next check
584 # update to next check
585 cmdutil.bailifchanged(repo)
585 cmdutil.bailifchanged(repo)
586 hg.clean(repo, nodes[0], show_stats=False)
586 hg.clean(repo, nodes[0], show_stats=False)
587 finally:
587 finally:
588 hbisect.save_state(repo, state)
588 hbisect.save_state(repo, state)
589 print_result(nodes, good)
589 print_result(nodes, good)
590 return
590 return
591
591
592 # update state
592 # update state
593
593
594 if rev:
594 if rev:
595 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
595 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
596 else:
596 else:
597 nodes = [repo.lookup('.')]
597 nodes = [repo.lookup('.')]
598
598
599 if good or bad or skip:
599 if good or bad or skip:
600 if good:
600 if good:
601 state['good'] += nodes
601 state['good'] += nodes
602 elif bad:
602 elif bad:
603 state['bad'] += nodes
603 state['bad'] += nodes
604 elif skip:
604 elif skip:
605 state['skip'] += nodes
605 state['skip'] += nodes
606 hbisect.save_state(repo, state)
606 hbisect.save_state(repo, state)
607
607
608 if not check_state(state):
608 if not check_state(state):
609 return
609 return
610
610
611 # actually bisect
611 # actually bisect
612 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
612 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
613 if extend:
613 if extend:
614 if not changesets:
614 if not changesets:
615 extendnode = extendbisectrange(nodes, good)
615 extendnode = extendbisectrange(nodes, good)
616 if extendnode is not None:
616 if extendnode is not None:
617 ui.write(_("Extending search to changeset %d:%s\n"
617 ui.write(_("Extending search to changeset %d:%s\n"
618 % (extendnode.rev(), extendnode)))
618 % (extendnode.rev(), extendnode)))
619 if noupdate:
619 if noupdate:
620 return
620 return
621 cmdutil.bailifchanged(repo)
621 cmdutil.bailifchanged(repo)
622 return hg.clean(repo, extendnode.node())
622 return hg.clean(repo, extendnode.node())
623 raise util.Abort(_("nothing to extend"))
623 raise util.Abort(_("nothing to extend"))
624
624
625 if changesets == 0:
625 if changesets == 0:
626 print_result(nodes, good)
626 print_result(nodes, good)
627 else:
627 else:
628 assert len(nodes) == 1 # only a single node can be tested next
628 assert len(nodes) == 1 # only a single node can be tested next
629 node = nodes[0]
629 node = nodes[0]
630 # compute the approximate number of remaining tests
630 # compute the approximate number of remaining tests
631 tests, size = 0, 2
631 tests, size = 0, 2
632 while size <= changesets:
632 while size <= changesets:
633 tests, size = tests + 1, size * 2
633 tests, size = tests + 1, size * 2
634 rev = repo.changelog.rev(node)
634 rev = repo.changelog.rev(node)
635 ui.write(_("Testing changeset %d:%s "
635 ui.write(_("Testing changeset %d:%s "
636 "(%d changesets remaining, ~%d tests)\n")
636 "(%d changesets remaining, ~%d tests)\n")
637 % (rev, short(node), changesets, tests))
637 % (rev, short(node), changesets, tests))
638 if not noupdate:
638 if not noupdate:
639 cmdutil.bailifchanged(repo)
639 cmdutil.bailifchanged(repo)
640 return hg.clean(repo, node)
640 return hg.clean(repo, node)
641
641
642 @command('bookmarks',
642 @command('bookmarks',
643 [('f', 'force', False, _('force')),
643 [('f', 'force', False, _('force')),
644 ('r', 'rev', '', _('revision'), _('REV')),
644 ('r', 'rev', '', _('revision'), _('REV')),
645 ('d', 'delete', False, _('delete a given bookmark')),
645 ('d', 'delete', False, _('delete a given bookmark')),
646 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
646 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
647 ('i', 'inactive', False, _('do not mark a new bookmark active'))],
647 ('i', 'inactive', False, _('do not mark a new bookmark active'))],
648 _('hg bookmarks [-f] [-d] [-i] [-m NAME] [-r REV] [NAME]'))
648 _('hg bookmarks [-f] [-d] [-i] [-m NAME] [-r REV] [NAME]'))
649 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False,
649 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False,
650 rename=None, inactive=False):
650 rename=None, inactive=False):
651 '''track a line of development with movable markers
651 '''track a line of development with movable markers
652
652
653 Bookmarks are pointers to certain commits that move when
653 Bookmarks are pointers to certain commits that move when
654 committing. Bookmarks are local. They can be renamed, copied and
654 committing. Bookmarks are local. They can be renamed, copied and
655 deleted. It is possible to use bookmark names in :hg:`merge` and
655 deleted. It is possible to use bookmark names in :hg:`merge` and
656 :hg:`update` to merge and update respectively to a given bookmark.
656 :hg:`update` to merge and update respectively to a given bookmark.
657
657
658 You can use :hg:`bookmark NAME` to set a bookmark on the working
658 You can use :hg:`bookmark NAME` to set a bookmark on the working
659 directory's parent revision with the given name. If you specify
659 directory's parent revision with the given name. If you specify
660 a revision using -r REV (where REV may be an existing bookmark),
660 a revision using -r REV (where REV may be an existing bookmark),
661 the bookmark is assigned to that revision.
661 the bookmark is assigned to that revision.
662
662
663 Bookmarks can be pushed and pulled between repositories (see :hg:`help
663 Bookmarks can be pushed and pulled between repositories (see :hg:`help
664 push` and :hg:`help pull`). This requires both the local and remote
664 push` and :hg:`help pull`). This requires both the local and remote
665 repositories to support bookmarks. For versions prior to 1.8, this means
665 repositories to support bookmarks. For versions prior to 1.8, this means
666 the bookmarks extension must be enabled.
666 the bookmarks extension must be enabled.
667 '''
667 '''
668 hexfn = ui.debugflag and hex or short
668 hexfn = ui.debugflag and hex or short
669 marks = repo._bookmarks
669 marks = repo._bookmarks
670 cur = repo.changectx('.').node()
670 cur = repo.changectx('.').node()
671
671
672 if rename:
672 if rename:
673 if rename not in marks:
673 if rename not in marks:
674 raise util.Abort(_("bookmark '%s' does not exist") % rename)
674 raise util.Abort(_("bookmark '%s' does not exist") % rename)
675 if mark in marks and not force:
675 if mark in marks and not force:
676 raise util.Abort(_("bookmark '%s' already exists "
676 raise util.Abort(_("bookmark '%s' already exists "
677 "(use -f to force)") % mark)
677 "(use -f to force)") % mark)
678 if mark is None:
678 if mark is None:
679 raise util.Abort(_("new bookmark name required"))
679 raise util.Abort(_("new bookmark name required"))
680 marks[mark] = marks[rename]
680 marks[mark] = marks[rename]
681 if repo._bookmarkcurrent == rename and not inactive:
681 if repo._bookmarkcurrent == rename and not inactive:
682 bookmarks.setcurrent(repo, mark)
682 bookmarks.setcurrent(repo, mark)
683 del marks[rename]
683 del marks[rename]
684 bookmarks.write(repo)
684 bookmarks.write(repo)
685 return
685 return
686
686
687 if delete:
687 if delete:
688 if mark is None:
688 if mark is None:
689 raise util.Abort(_("bookmark name required"))
689 raise util.Abort(_("bookmark name required"))
690 if mark not in marks:
690 if mark not in marks:
691 raise util.Abort(_("bookmark '%s' does not exist") % mark)
691 raise util.Abort(_("bookmark '%s' does not exist") % mark)
692 if mark == repo._bookmarkcurrent:
692 if mark == repo._bookmarkcurrent:
693 bookmarks.setcurrent(repo, None)
693 bookmarks.setcurrent(repo, None)
694 del marks[mark]
694 del marks[mark]
695 bookmarks.write(repo)
695 bookmarks.write(repo)
696 return
696 return
697
697
698 if mark is not None:
698 if mark is not None:
699 if "\n" in mark:
699 if "\n" in mark:
700 raise util.Abort(_("bookmark name cannot contain newlines"))
700 raise util.Abort(_("bookmark name cannot contain newlines"))
701 mark = mark.strip()
701 mark = mark.strip()
702 if not mark:
702 if not mark:
703 raise util.Abort(_("bookmark names cannot consist entirely of "
703 raise util.Abort(_("bookmark names cannot consist entirely of "
704 "whitespace"))
704 "whitespace"))
705 if inactive and mark == repo._bookmarkcurrent:
705 if inactive and mark == repo._bookmarkcurrent:
706 bookmarks.setcurrent(repo, None)
706 bookmarks.setcurrent(repo, None)
707 return
707 return
708 if mark in marks and not force:
708 if mark in marks and not force:
709 raise util.Abort(_("bookmark '%s' already exists "
709 raise util.Abort(_("bookmark '%s' already exists "
710 "(use -f to force)") % mark)
710 "(use -f to force)") % mark)
711 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
711 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
712 and not force):
712 and not force):
713 raise util.Abort(
713 raise util.Abort(
714 _("a bookmark cannot have the name of an existing branch"))
714 _("a bookmark cannot have the name of an existing branch"))
715 if rev:
715 if rev:
716 marks[mark] = repo.lookup(rev)
716 marks[mark] = repo.lookup(rev)
717 else:
717 else:
718 marks[mark] = repo.changectx('.').node()
718 marks[mark] = repo.changectx('.').node()
719 if not inactive and repo.changectx('.').node() == marks[mark]:
719 if not inactive and repo.changectx('.').node() == marks[mark]:
720 bookmarks.setcurrent(repo, mark)
720 bookmarks.setcurrent(repo, mark)
721 bookmarks.write(repo)
721 bookmarks.write(repo)
722 return
722 return
723
723
724 if mark is None:
724 if mark is None:
725 if rev:
725 if rev:
726 raise util.Abort(_("bookmark name required"))
726 raise util.Abort(_("bookmark name required"))
727 if len(marks) == 0:
727 if len(marks) == 0:
728 ui.status(_("no bookmarks set\n"))
728 ui.status(_("no bookmarks set\n"))
729 else:
729 else:
730 for bmark, n in sorted(marks.iteritems()):
730 for bmark, n in sorted(marks.iteritems()):
731 current = repo._bookmarkcurrent
731 current = repo._bookmarkcurrent
732 if bmark == current and n == cur:
732 if bmark == current and n == cur:
733 prefix, label = '*', 'bookmarks.current'
733 prefix, label = '*', 'bookmarks.current'
734 else:
734 else:
735 prefix, label = ' ', ''
735 prefix, label = ' ', ''
736
736
737 if ui.quiet:
737 if ui.quiet:
738 ui.write("%s\n" % bmark, label=label)
738 ui.write("%s\n" % bmark, label=label)
739 else:
739 else:
740 ui.write(" %s %-25s %d:%s\n" % (
740 ui.write(" %s %-25s %d:%s\n" % (
741 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
741 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
742 label=label)
742 label=label)
743 return
743 return
744
744
745 @command('branch',
745 @command('branch',
746 [('f', 'force', None,
746 [('f', 'force', None,
747 _('set branch name even if it shadows an existing branch')),
747 _('set branch name even if it shadows an existing branch')),
748 ('C', 'clean', None, _('reset branch name to parent branch name'))],
748 ('C', 'clean', None, _('reset branch name to parent branch name'))],
749 _('[-fC] [NAME]'))
749 _('[-fC] [NAME]'))
750 def branch(ui, repo, label=None, **opts):
750 def branch(ui, repo, label=None, **opts):
751 """set or show the current branch name
751 """set or show the current branch name
752
752
753 With no argument, show the current branch name. With one argument,
753 With no argument, show the current branch name. With one argument,
754 set the working directory branch name (the branch will not exist
754 set the working directory branch name (the branch will not exist
755 in the repository until the next commit). Standard practice
755 in the repository until the next commit). Standard practice
756 recommends that primary development take place on the 'default'
756 recommends that primary development take place on the 'default'
757 branch.
757 branch.
758
758
759 Unless -f/--force is specified, branch will not let you set a
759 Unless -f/--force is specified, branch will not let you set a
760 branch name that already exists, even if it's inactive.
760 branch name that already exists, even if it's inactive.
761
761
762 Use -C/--clean to reset the working directory branch to that of
762 Use -C/--clean to reset the working directory branch to that of
763 the parent of the working directory, negating a previous branch
763 the parent of the working directory, negating a previous branch
764 change.
764 change.
765
765
766 Use the command :hg:`update` to switch to an existing branch. Use
766 Use the command :hg:`update` to switch to an existing branch. Use
767 :hg:`commit --close-branch` to mark this branch as closed.
767 :hg:`commit --close-branch` to mark this branch as closed.
768
768
769 .. note::
769 .. note::
770
770
771 Branch names are permanent. Use :hg:`bookmark` to create a
771 Branch names are permanent. Use :hg:`bookmark` to create a
772 light-weight bookmark instead. See :hg:`help glossary` for more
772 light-weight bookmark instead. See :hg:`help glossary` for more
773 information about named branches and bookmarks.
773 information about named branches and bookmarks.
774
774
775 Returns 0 on success.
775 Returns 0 on success.
776 """
776 """
777
777
778 if opts.get('clean'):
778 if opts.get('clean'):
779 label = repo[None].p1().branch()
779 label = repo[None].p1().branch()
780 repo.dirstate.setbranch(label)
780 repo.dirstate.setbranch(label)
781 ui.status(_('reset working directory to branch %s\n') % label)
781 ui.status(_('reset working directory to branch %s\n') % label)
782 elif label:
782 elif label:
783 if not opts.get('force') and label in repo.branchtags():
783 if not opts.get('force') and label in repo.branchtags():
784 if label not in [p.branch() for p in repo.parents()]:
784 if label not in [p.branch() for p in repo.parents()]:
785 raise util.Abort(_('a branch of the same name already exists'),
785 raise util.Abort(_('a branch of the same name already exists'),
786 # i18n: "it" refers to an existing branch
786 # i18n: "it" refers to an existing branch
787 hint=_("use 'hg update' to switch to it"))
787 hint=_("use 'hg update' to switch to it"))
788 repo.dirstate.setbranch(label)
788 repo.dirstate.setbranch(label)
789 ui.status(_('marked working directory as branch %s\n') % label)
789 ui.status(_('marked working directory as branch %s\n') % label)
790 else:
790 else:
791 ui.write("%s\n" % repo.dirstate.branch())
791 ui.write("%s\n" % repo.dirstate.branch())
792
792
793 @command('branches',
793 @command('branches',
794 [('a', 'active', False, _('show only branches that have unmerged heads')),
794 [('a', 'active', False, _('show only branches that have unmerged heads')),
795 ('c', 'closed', False, _('show normal and closed branches'))],
795 ('c', 'closed', False, _('show normal and closed branches'))],
796 _('[-ac]'))
796 _('[-ac]'))
797 def branches(ui, repo, active=False, closed=False):
797 def branches(ui, repo, active=False, closed=False):
798 """list repository named branches
798 """list repository named branches
799
799
800 List the repository's named branches, indicating which ones are
800 List the repository's named branches, indicating which ones are
801 inactive. If -c/--closed is specified, also list branches which have
801 inactive. If -c/--closed is specified, also list branches which have
802 been marked closed (see :hg:`commit --close-branch`).
802 been marked closed (see :hg:`commit --close-branch`).
803
803
804 If -a/--active is specified, only show active branches. A branch
804 If -a/--active is specified, only show active branches. A branch
805 is considered active if it contains repository heads.
805 is considered active if it contains repository heads.
806
806
807 Use the command :hg:`update` to switch to an existing branch.
807 Use the command :hg:`update` to switch to an existing branch.
808
808
809 Returns 0.
809 Returns 0.
810 """
810 """
811
811
812 hexfunc = ui.debugflag and hex or short
812 hexfunc = ui.debugflag and hex or short
813 activebranches = [repo[n].branch() for n in repo.heads()]
813 activebranches = [repo[n].branch() for n in repo.heads()]
814 def testactive(tag, node):
814 def testactive(tag, node):
815 realhead = tag in activebranches
815 realhead = tag in activebranches
816 open = node in repo.branchheads(tag, closed=False)
816 open = node in repo.branchheads(tag, closed=False)
817 return realhead and open
817 return realhead and open
818 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
818 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
819 for tag, node in repo.branchtags().items()],
819 for tag, node in repo.branchtags().items()],
820 reverse=True)
820 reverse=True)
821
821
822 for isactive, node, tag in branches:
822 for isactive, node, tag in branches:
823 if (not active) or isactive:
823 if (not active) or isactive:
824 if ui.quiet:
824 if ui.quiet:
825 ui.write("%s\n" % tag)
825 ui.write("%s\n" % tag)
826 else:
826 else:
827 hn = repo.lookup(node)
827 hn = repo.lookup(node)
828 if isactive:
828 if isactive:
829 label = 'branches.active'
829 label = 'branches.active'
830 notice = ''
830 notice = ''
831 elif hn not in repo.branchheads(tag, closed=False):
831 elif hn not in repo.branchheads(tag, closed=False):
832 if not closed:
832 if not closed:
833 continue
833 continue
834 label = 'branches.closed'
834 label = 'branches.closed'
835 notice = _(' (closed)')
835 notice = _(' (closed)')
836 else:
836 else:
837 label = 'branches.inactive'
837 label = 'branches.inactive'
838 notice = _(' (inactive)')
838 notice = _(' (inactive)')
839 if tag == repo.dirstate.branch():
839 if tag == repo.dirstate.branch():
840 label = 'branches.current'
840 label = 'branches.current'
841 rev = str(node).rjust(31 - encoding.colwidth(tag))
841 rev = str(node).rjust(31 - encoding.colwidth(tag))
842 rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
842 rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
843 tag = ui.label(tag, label)
843 tag = ui.label(tag, label)
844 ui.write("%s %s%s\n" % (tag, rev, notice))
844 ui.write("%s %s%s\n" % (tag, rev, notice))
845
845
846 @command('bundle',
846 @command('bundle',
847 [('f', 'force', None, _('run even when the destination is unrelated')),
847 [('f', 'force', None, _('run even when the destination is unrelated')),
848 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
848 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
849 _('REV')),
849 _('REV')),
850 ('b', 'branch', [], _('a specific branch you would like to bundle'),
850 ('b', 'branch', [], _('a specific branch you would like to bundle'),
851 _('BRANCH')),
851 _('BRANCH')),
852 ('', 'base', [],
852 ('', 'base', [],
853 _('a base changeset assumed to be available at the destination'),
853 _('a base changeset assumed to be available at the destination'),
854 _('REV')),
854 _('REV')),
855 ('a', 'all', None, _('bundle all changesets in the repository')),
855 ('a', 'all', None, _('bundle all changesets in the repository')),
856 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
856 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
857 ] + remoteopts,
857 ] + remoteopts,
858 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
858 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
859 def bundle(ui, repo, fname, dest=None, **opts):
859 def bundle(ui, repo, fname, dest=None, **opts):
860 """create a changegroup file
860 """create a changegroup file
861
861
862 Generate a compressed changegroup file collecting changesets not
862 Generate a compressed changegroup file collecting changesets not
863 known to be in another repository.
863 known to be in another repository.
864
864
865 If you omit the destination repository, then hg assumes the
865 If you omit the destination repository, then hg assumes the
866 destination will have all the nodes you specify with --base
866 destination will have all the nodes you specify with --base
867 parameters. To create a bundle containing all changesets, use
867 parameters. To create a bundle containing all changesets, use
868 -a/--all (or --base null).
868 -a/--all (or --base null).
869
869
870 You can change compression method with the -t/--type option.
870 You can change compression method with the -t/--type option.
871 The available compression methods are: none, bzip2, and
871 The available compression methods are: none, bzip2, and
872 gzip (by default, bundles are compressed using bzip2).
872 gzip (by default, bundles are compressed using bzip2).
873
873
874 The bundle file can then be transferred using conventional means
874 The bundle file can then be transferred using conventional means
875 and applied to another repository with the unbundle or pull
875 and applied to another repository with the unbundle or pull
876 command. This is useful when direct push and pull are not
876 command. This is useful when direct push and pull are not
877 available or when exporting an entire repository is undesirable.
877 available or when exporting an entire repository is undesirable.
878
878
879 Applying bundles preserves all changeset contents including
879 Applying bundles preserves all changeset contents including
880 permissions, copy/rename information, and revision history.
880 permissions, copy/rename information, and revision history.
881
881
882 Returns 0 on success, 1 if no changes found.
882 Returns 0 on success, 1 if no changes found.
883 """
883 """
884 revs = None
884 revs = None
885 if 'rev' in opts:
885 if 'rev' in opts:
886 revs = scmutil.revrange(repo, opts['rev'])
886 revs = scmutil.revrange(repo, opts['rev'])
887
887
888 if opts.get('all'):
888 if opts.get('all'):
889 base = ['null']
889 base = ['null']
890 else:
890 else:
891 base = scmutil.revrange(repo, opts.get('base'))
891 base = scmutil.revrange(repo, opts.get('base'))
892 if base:
892 if base:
893 if dest:
893 if dest:
894 raise util.Abort(_("--base is incompatible with specifying "
894 raise util.Abort(_("--base is incompatible with specifying "
895 "a destination"))
895 "a destination"))
896 common = [repo.lookup(rev) for rev in base]
896 common = [repo.lookup(rev) for rev in base]
897 heads = revs and map(repo.lookup, revs) or revs
897 heads = revs and map(repo.lookup, revs) or revs
898 else:
898 else:
899 dest = ui.expandpath(dest or 'default-push', dest or 'default')
899 dest = ui.expandpath(dest or 'default-push', dest or 'default')
900 dest, branches = hg.parseurl(dest, opts.get('branch'))
900 dest, branches = hg.parseurl(dest, opts.get('branch'))
901 other = hg.peer(repo, opts, dest)
901 other = hg.peer(repo, opts, dest)
902 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
902 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
903 heads = revs and map(repo.lookup, revs) or revs
903 heads = revs and map(repo.lookup, revs) or revs
904 common, outheads = discovery.findcommonoutgoing(repo, other,
904 common, outheads = discovery.findcommonoutgoing(repo, other,
905 onlyheads=heads,
905 onlyheads=heads,
906 force=opts.get('force'))
906 force=opts.get('force'))
907
907
908 cg = repo.getbundle('bundle', common=common, heads=heads)
908 cg = repo.getbundle('bundle', common=common, heads=heads)
909 if not cg:
909 if not cg:
910 ui.status(_("no changes found\n"))
910 ui.status(_("no changes found\n"))
911 return 1
911 return 1
912
912
913 bundletype = opts.get('type', 'bzip2').lower()
913 bundletype = opts.get('type', 'bzip2').lower()
914 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
914 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
915 bundletype = btypes.get(bundletype)
915 bundletype = btypes.get(bundletype)
916 if bundletype not in changegroup.bundletypes:
916 if bundletype not in changegroup.bundletypes:
917 raise util.Abort(_('unknown bundle type specified with --type'))
917 raise util.Abort(_('unknown bundle type specified with --type'))
918
918
919 changegroup.writebundle(cg, fname, bundletype)
919 changegroup.writebundle(cg, fname, bundletype)
920
920
921 @command('cat',
921 @command('cat',
922 [('o', 'output', '',
922 [('o', 'output', '',
923 _('print output to file with formatted name'), _('FORMAT')),
923 _('print output to file with formatted name'), _('FORMAT')),
924 ('r', 'rev', '', _('print the given revision'), _('REV')),
924 ('r', 'rev', '', _('print the given revision'), _('REV')),
925 ('', 'decode', None, _('apply any matching decode filter')),
925 ('', 'decode', None, _('apply any matching decode filter')),
926 ] + walkopts,
926 ] + walkopts,
927 _('[OPTION]... FILE...'))
927 _('[OPTION]... FILE...'))
928 def cat(ui, repo, file1, *pats, **opts):
928 def cat(ui, repo, file1, *pats, **opts):
929 """output the current or given revision of files
929 """output the current or given revision of files
930
930
931 Print the specified files as they were at the given revision. If
931 Print the specified files as they were at the given revision. If
932 no revision is given, the parent of the working directory is used,
932 no revision is given, the parent of the working directory is used,
933 or tip if no revision is checked out.
933 or tip if no revision is checked out.
934
934
935 Output may be to a file, in which case the name of the file is
935 Output may be to a file, in which case the name of the file is
936 given using a format string. The formatting rules are the same as
936 given using a format string. The formatting rules are the same as
937 for the export command, with the following additions:
937 for the export command, with the following additions:
938
938
939 :``%s``: basename of file being printed
939 :``%s``: basename of file being printed
940 :``%d``: dirname of file being printed, or '.' if in repository root
940 :``%d``: dirname of file being printed, or '.' if in repository root
941 :``%p``: root-relative path name of file being printed
941 :``%p``: root-relative path name of file being printed
942
942
943 Returns 0 on success.
943 Returns 0 on success.
944 """
944 """
945 ctx = scmutil.revsingle(repo, opts.get('rev'))
945 ctx = scmutil.revsingle(repo, opts.get('rev'))
946 err = 1
946 err = 1
947 m = scmutil.match(ctx, (file1,) + pats, opts)
947 m = scmutil.match(ctx, (file1,) + pats, opts)
948 for abs in ctx.walk(m):
948 for abs in ctx.walk(m):
949 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
949 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
950 pathname=abs)
950 pathname=abs)
951 data = ctx[abs].data()
951 data = ctx[abs].data()
952 if opts.get('decode'):
952 if opts.get('decode'):
953 data = repo.wwritedata(abs, data)
953 data = repo.wwritedata(abs, data)
954 fp.write(data)
954 fp.write(data)
955 fp.close()
955 fp.close()
956 err = 0
956 err = 0
957 return err
957 return err
958
958
959 @command('^clone',
959 @command('^clone',
960 [('U', 'noupdate', None,
960 [('U', 'noupdate', None,
961 _('the clone will include an empty working copy (only a repository)')),
961 _('the clone will include an empty working copy (only a repository)')),
962 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
962 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
963 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
963 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
964 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
964 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
965 ('', 'pull', None, _('use pull protocol to copy metadata')),
965 ('', 'pull', None, _('use pull protocol to copy metadata')),
966 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
966 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
967 ] + remoteopts,
967 ] + remoteopts,
968 _('[OPTION]... SOURCE [DEST]'))
968 _('[OPTION]... SOURCE [DEST]'))
969 def clone(ui, source, dest=None, **opts):
969 def clone(ui, source, dest=None, **opts):
970 """make a copy of an existing repository
970 """make a copy of an existing repository
971
971
972 Create a copy of an existing repository in a new directory.
972 Create a copy of an existing repository in a new directory.
973
973
974 If no destination directory name is specified, it defaults to the
974 If no destination directory name is specified, it defaults to the
975 basename of the source.
975 basename of the source.
976
976
977 The location of the source is added to the new repository's
977 The location of the source is added to the new repository's
978 ``.hg/hgrc`` file, as the default to be used for future pulls.
978 ``.hg/hgrc`` file, as the default to be used for future pulls.
979
979
980 See :hg:`help urls` for valid source format details.
980 See :hg:`help urls` for valid source format details.
981
981
982 It is possible to specify an ``ssh://`` URL as the destination, but no
982 It is possible to specify an ``ssh://`` URL as the destination, but no
983 ``.hg/hgrc`` and working directory will be created on the remote side.
983 ``.hg/hgrc`` and working directory will be created on the remote side.
984 Please see :hg:`help urls` for important details about ``ssh://`` URLs.
984 Please see :hg:`help urls` for important details about ``ssh://`` URLs.
985
985
986 A set of changesets (tags, or branch names) to pull may be specified
986 A set of changesets (tags, or branch names) to pull may be specified
987 by listing each changeset (tag, or branch name) with -r/--rev.
987 by listing each changeset (tag, or branch name) with -r/--rev.
988 If -r/--rev is used, the cloned repository will contain only a subset
988 If -r/--rev is used, the cloned repository will contain only a subset
989 of the changesets of the source repository. Only the set of changesets
989 of the changesets of the source repository. Only the set of changesets
990 defined by all -r/--rev options (including all their ancestors)
990 defined by all -r/--rev options (including all their ancestors)
991 will be pulled into the destination repository.
991 will be pulled into the destination repository.
992 No subsequent changesets (including subsequent tags) will be present
992 No subsequent changesets (including subsequent tags) will be present
993 in the destination.
993 in the destination.
994
994
995 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
995 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
996 local source repositories.
996 local source repositories.
997
997
998 For efficiency, hardlinks are used for cloning whenever the source
998 For efficiency, hardlinks are used for cloning whenever the source
999 and destination are on the same filesystem (note this applies only
999 and destination are on the same filesystem (note this applies only
1000 to the repository data, not to the working directory). Some
1000 to the repository data, not to the working directory). Some
1001 filesystems, such as AFS, implement hardlinking incorrectly, but
1001 filesystems, such as AFS, implement hardlinking incorrectly, but
1002 do not report errors. In these cases, use the --pull option to
1002 do not report errors. In these cases, use the --pull option to
1003 avoid hardlinking.
1003 avoid hardlinking.
1004
1004
1005 In some cases, you can clone repositories and the working directory
1005 In some cases, you can clone repositories and the working directory
1006 using full hardlinks with ::
1006 using full hardlinks with ::
1007
1007
1008 $ cp -al REPO REPOCLONE
1008 $ cp -al REPO REPOCLONE
1009
1009
1010 This is the fastest way to clone, but it is not always safe. The
1010 This is the fastest way to clone, but it is not always safe. The
1011 operation is not atomic (making sure REPO is not modified during
1011 operation is not atomic (making sure REPO is not modified during
1012 the operation is up to you) and you have to make sure your editor
1012 the operation is up to you) and you have to make sure your editor
1013 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
1013 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
1014 this is not compatible with certain extensions that place their
1014 this is not compatible with certain extensions that place their
1015 metadata under the .hg directory, such as mq.
1015 metadata under the .hg directory, such as mq.
1016
1016
1017 Mercurial will update the working directory to the first applicable
1017 Mercurial will update the working directory to the first applicable
1018 revision from this list:
1018 revision from this list:
1019
1019
1020 a) null if -U or the source repository has no changesets
1020 a) null if -U or the source repository has no changesets
1021 b) if -u . and the source repository is local, the first parent of
1021 b) if -u . and the source repository is local, the first parent of
1022 the source repository's working directory
1022 the source repository's working directory
1023 c) the changeset specified with -u (if a branch name, this means the
1023 c) the changeset specified with -u (if a branch name, this means the
1024 latest head of that branch)
1024 latest head of that branch)
1025 d) the changeset specified with -r
1025 d) the changeset specified with -r
1026 e) the tipmost head specified with -b
1026 e) the tipmost head specified with -b
1027 f) the tipmost head specified with the url#branch source syntax
1027 f) the tipmost head specified with the url#branch source syntax
1028 g) the tipmost head of the default branch
1028 g) the tipmost head of the default branch
1029 h) tip
1029 h) tip
1030
1030
1031 Returns 0 on success.
1031 Returns 0 on success.
1032 """
1032 """
1033 if opts.get('noupdate') and opts.get('updaterev'):
1033 if opts.get('noupdate') and opts.get('updaterev'):
1034 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1034 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1035
1035
1036 r = hg.clone(ui, opts, source, dest,
1036 r = hg.clone(ui, opts, source, dest,
1037 pull=opts.get('pull'),
1037 pull=opts.get('pull'),
1038 stream=opts.get('uncompressed'),
1038 stream=opts.get('uncompressed'),
1039 rev=opts.get('rev'),
1039 rev=opts.get('rev'),
1040 update=opts.get('updaterev') or not opts.get('noupdate'),
1040 update=opts.get('updaterev') or not opts.get('noupdate'),
1041 branch=opts.get('branch'))
1041 branch=opts.get('branch'))
1042
1042
1043 return r is None
1043 return r is None
1044
1044
1045 @command('^commit|ci',
1045 @command('^commit|ci',
1046 [('A', 'addremove', None,
1046 [('A', 'addremove', None,
1047 _('mark new/missing files as added/removed before committing')),
1047 _('mark new/missing files as added/removed before committing')),
1048 ('', 'close-branch', None,
1048 ('', 'close-branch', None,
1049 _('mark a branch as closed, hiding it from the branch list')),
1049 _('mark a branch as closed, hiding it from the branch list')),
1050 ] + walkopts + commitopts + commitopts2,
1050 ] + walkopts + commitopts + commitopts2,
1051 _('[OPTION]... [FILE]...'))
1051 _('[OPTION]... [FILE]...'))
1052 def commit(ui, repo, *pats, **opts):
1052 def commit(ui, repo, *pats, **opts):
1053 """commit the specified files or all outstanding changes
1053 """commit the specified files or all outstanding changes
1054
1054
1055 Commit changes to the given files into the repository. Unlike a
1055 Commit changes to the given files into the repository. Unlike a
1056 centralized SCM, this operation is a local operation. See
1056 centralized SCM, this operation is a local operation. See
1057 :hg:`push` for a way to actively distribute your changes.
1057 :hg:`push` for a way to actively distribute your changes.
1058
1058
1059 If a list of files is omitted, all changes reported by :hg:`status`
1059 If a list of files is omitted, all changes reported by :hg:`status`
1060 will be committed.
1060 will be committed.
1061
1061
1062 If you are committing the result of a merge, do not provide any
1062 If you are committing the result of a merge, do not provide any
1063 filenames or -I/-X filters.
1063 filenames or -I/-X filters.
1064
1064
1065 If no commit message is specified, Mercurial starts your
1065 If no commit message is specified, Mercurial starts your
1066 configured editor where you can enter a message. In case your
1066 configured editor where you can enter a message. In case your
1067 commit fails, you will find a backup of your message in
1067 commit fails, you will find a backup of your message in
1068 ``.hg/last-message.txt``.
1068 ``.hg/last-message.txt``.
1069
1069
1070 See :hg:`help dates` for a list of formats valid for -d/--date.
1070 See :hg:`help dates` for a list of formats valid for -d/--date.
1071
1071
1072 Returns 0 on success, 1 if nothing changed.
1072 Returns 0 on success, 1 if nothing changed.
1073 """
1073 """
1074 extra = {}
1074 extra = {}
1075 if opts.get('close_branch'):
1075 if opts.get('close_branch'):
1076 if repo['.'].node() not in repo.branchheads():
1076 if repo['.'].node() not in repo.branchheads():
1077 # The topo heads set is included in the branch heads set of the
1077 # The topo heads set is included in the branch heads set of the
1078 # current branch, so it's sufficient to test branchheads
1078 # current branch, so it's sufficient to test branchheads
1079 raise util.Abort(_('can only close branch heads'))
1079 raise util.Abort(_('can only close branch heads'))
1080 extra['close'] = 1
1080 extra['close'] = 1
1081 e = cmdutil.commiteditor
1081 e = cmdutil.commiteditor
1082 if opts.get('force_editor'):
1082 if opts.get('force_editor'):
1083 e = cmdutil.commitforceeditor
1083 e = cmdutil.commitforceeditor
1084
1084
1085 def commitfunc(ui, repo, message, match, opts):
1085 def commitfunc(ui, repo, message, match, opts):
1086 return repo.commit(message, opts.get('user'), opts.get('date'), match,
1086 return repo.commit(message, opts.get('user'), opts.get('date'), match,
1087 editor=e, extra=extra)
1087 editor=e, extra=extra)
1088
1088
1089 branch = repo[None].branch()
1089 branch = repo[None].branch()
1090 bheads = repo.branchheads(branch)
1090 bheads = repo.branchheads(branch)
1091
1091
1092 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1092 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1093 if not node:
1093 if not node:
1094 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1094 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1095 if stat[3]:
1095 if stat[3]:
1096 ui.status(_("nothing changed (%d missing files, see 'hg status')\n")
1096 ui.status(_("nothing changed (%d missing files, see 'hg status')\n")
1097 % len(stat[3]))
1097 % len(stat[3]))
1098 else:
1098 else:
1099 ui.status(_("nothing changed\n"))
1099 ui.status(_("nothing changed\n"))
1100 return 1
1100 return 1
1101
1101
1102 ctx = repo[node]
1102 ctx = repo[node]
1103 parents = ctx.parents()
1103 parents = ctx.parents()
1104
1104
1105 if bheads and not [x for x in parents
1105 if bheads and not [x for x in parents
1106 if x.node() in bheads and x.branch() == branch]:
1106 if x.node() in bheads and x.branch() == branch]:
1107 ui.status(_('created new head\n'))
1107 ui.status(_('created new head\n'))
1108 # The message is not printed for initial roots. For the other
1108 # The message is not printed for initial roots. For the other
1109 # changesets, it is printed in the following situations:
1109 # changesets, it is printed in the following situations:
1110 #
1110 #
1111 # Par column: for the 2 parents with ...
1111 # Par column: for the 2 parents with ...
1112 # N: null or no parent
1112 # N: null or no parent
1113 # B: parent is on another named branch
1113 # B: parent is on another named branch
1114 # C: parent is a regular non head changeset
1114 # C: parent is a regular non head changeset
1115 # H: parent was a branch head of the current branch
1115 # H: parent was a branch head of the current branch
1116 # Msg column: whether we print "created new head" message
1116 # Msg column: whether we print "created new head" message
1117 # In the following, it is assumed that there already exists some
1117 # In the following, it is assumed that there already exists some
1118 # initial branch heads of the current branch, otherwise nothing is
1118 # initial branch heads of the current branch, otherwise nothing is
1119 # printed anyway.
1119 # printed anyway.
1120 #
1120 #
1121 # Par Msg Comment
1121 # Par Msg Comment
1122 # NN y additional topo root
1122 # NN y additional topo root
1123 #
1123 #
1124 # BN y additional branch root
1124 # BN y additional branch root
1125 # CN y additional topo head
1125 # CN y additional topo head
1126 # HN n usual case
1126 # HN n usual case
1127 #
1127 #
1128 # BB y weird additional branch root
1128 # BB y weird additional branch root
1129 # CB y branch merge
1129 # CB y branch merge
1130 # HB n merge with named branch
1130 # HB n merge with named branch
1131 #
1131 #
1132 # CC y additional head from merge
1132 # CC y additional head from merge
1133 # CH n merge with a head
1133 # CH n merge with a head
1134 #
1134 #
1135 # HH n head merge: head count decreases
1135 # HH n head merge: head count decreases
1136
1136
1137 if not opts.get('close_branch'):
1137 if not opts.get('close_branch'):
1138 for r in parents:
1138 for r in parents:
1139 if r.extra().get('close') and r.branch() == branch:
1139 if r.extra().get('close') and r.branch() == branch:
1140 ui.status(_('reopening closed branch head %d\n') % r)
1140 ui.status(_('reopening closed branch head %d\n') % r)
1141
1141
1142 if ui.debugflag:
1142 if ui.debugflag:
1143 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
1143 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
1144 elif ui.verbose:
1144 elif ui.verbose:
1145 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
1145 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
1146
1146
1147 @command('copy|cp',
1147 @command('copy|cp',
1148 [('A', 'after', None, _('record a copy that has already occurred')),
1148 [('A', 'after', None, _('record a copy that has already occurred')),
1149 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1149 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1150 ] + walkopts + dryrunopts,
1150 ] + walkopts + dryrunopts,
1151 _('[OPTION]... [SOURCE]... DEST'))
1151 _('[OPTION]... [SOURCE]... DEST'))
1152 def copy(ui, repo, *pats, **opts):
1152 def copy(ui, repo, *pats, **opts):
1153 """mark files as copied for the next commit
1153 """mark files as copied for the next commit
1154
1154
1155 Mark dest as having copies of source files. If dest is a
1155 Mark dest as having copies of source files. If dest is a
1156 directory, copies are put in that directory. If dest is a file,
1156 directory, copies are put in that directory. If dest is a file,
1157 the source must be a single file.
1157 the source must be a single file.
1158
1158
1159 By default, this command copies the contents of files as they
1159 By default, this command copies the contents of files as they
1160 exist in the working directory. If invoked with -A/--after, the
1160 exist in the working directory. If invoked with -A/--after, the
1161 operation is recorded, but no copying is performed.
1161 operation is recorded, but no copying is performed.
1162
1162
1163 This command takes effect with the next commit. To undo a copy
1163 This command takes effect with the next commit. To undo a copy
1164 before that, see :hg:`revert`.
1164 before that, see :hg:`revert`.
1165
1165
1166 Returns 0 on success, 1 if errors are encountered.
1166 Returns 0 on success, 1 if errors are encountered.
1167 """
1167 """
1168 wlock = repo.wlock(False)
1168 wlock = repo.wlock(False)
1169 try:
1169 try:
1170 return cmdutil.copy(ui, repo, pats, opts)
1170 return cmdutil.copy(ui, repo, pats, opts)
1171 finally:
1171 finally:
1172 wlock.release()
1172 wlock.release()
1173
1173
1174 @command('debugancestor', [], _('[INDEX] REV1 REV2'))
1174 @command('debugancestor', [], _('[INDEX] REV1 REV2'))
1175 def debugancestor(ui, repo, *args):
1175 def debugancestor(ui, repo, *args):
1176 """find the ancestor revision of two revisions in a given index"""
1176 """find the ancestor revision of two revisions in a given index"""
1177 if len(args) == 3:
1177 if len(args) == 3:
1178 index, rev1, rev2 = args
1178 index, rev1, rev2 = args
1179 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1179 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1180 lookup = r.lookup
1180 lookup = r.lookup
1181 elif len(args) == 2:
1181 elif len(args) == 2:
1182 if not repo:
1182 if not repo:
1183 raise util.Abort(_("there is no Mercurial repository here "
1183 raise util.Abort(_("there is no Mercurial repository here "
1184 "(.hg not found)"))
1184 "(.hg not found)"))
1185 rev1, rev2 = args
1185 rev1, rev2 = args
1186 r = repo.changelog
1186 r = repo.changelog
1187 lookup = repo.lookup
1187 lookup = repo.lookup
1188 else:
1188 else:
1189 raise util.Abort(_('either two or three arguments required'))
1189 raise util.Abort(_('either two or three arguments required'))
1190 a = r.ancestor(lookup(rev1), lookup(rev2))
1190 a = r.ancestor(lookup(rev1), lookup(rev2))
1191 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1191 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1192
1192
1193 @command('debugbuilddag',
1193 @command('debugbuilddag',
1194 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1194 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1195 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1195 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1196 ('n', 'new-file', None, _('add new file at each rev'))],
1196 ('n', 'new-file', None, _('add new file at each rev'))],
1197 _('[OPTION]... [TEXT]'))
1197 _('[OPTION]... [TEXT]'))
1198 def debugbuilddag(ui, repo, text=None,
1198 def debugbuilddag(ui, repo, text=None,
1199 mergeable_file=False,
1199 mergeable_file=False,
1200 overwritten_file=False,
1200 overwritten_file=False,
1201 new_file=False):
1201 new_file=False):
1202 """builds a repo with a given DAG from scratch in the current empty repo
1202 """builds a repo with a given DAG from scratch in the current empty repo
1203
1203
1204 The description of the DAG is read from stdin if not given on the
1204 The description of the DAG is read from stdin if not given on the
1205 command line.
1205 command line.
1206
1206
1207 Elements:
1207 Elements:
1208
1208
1209 - "+n" is a linear run of n nodes based on the current default parent
1209 - "+n" is a linear run of n nodes based on the current default parent
1210 - "." is a single node based on the current default parent
1210 - "." is a single node based on the current default parent
1211 - "$" resets the default parent to null (implied at the start);
1211 - "$" resets the default parent to null (implied at the start);
1212 otherwise the default parent is always the last node created
1212 otherwise the default parent is always the last node created
1213 - "<p" sets the default parent to the backref p
1213 - "<p" sets the default parent to the backref p
1214 - "*p" is a fork at parent p, which is a backref
1214 - "*p" is a fork at parent p, which is a backref
1215 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1215 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1216 - "/p2" is a merge of the preceding node and p2
1216 - "/p2" is a merge of the preceding node and p2
1217 - ":tag" defines a local tag for the preceding node
1217 - ":tag" defines a local tag for the preceding node
1218 - "@branch" sets the named branch for subsequent nodes
1218 - "@branch" sets the named branch for subsequent nodes
1219 - "#...\\n" is a comment up to the end of the line
1219 - "#...\\n" is a comment up to the end of the line
1220
1220
1221 Whitespace between the above elements is ignored.
1221 Whitespace between the above elements is ignored.
1222
1222
1223 A backref is either
1223 A backref is either
1224
1224
1225 - a number n, which references the node curr-n, where curr is the current
1225 - a number n, which references the node curr-n, where curr is the current
1226 node, or
1226 node, or
1227 - the name of a local tag you placed earlier using ":tag", or
1227 - the name of a local tag you placed earlier using ":tag", or
1228 - empty to denote the default parent.
1228 - empty to denote the default parent.
1229
1229
1230 All string valued-elements are either strictly alphanumeric, or must
1230 All string valued-elements are either strictly alphanumeric, or must
1231 be enclosed in double quotes ("..."), with "\\" as escape character.
1231 be enclosed in double quotes ("..."), with "\\" as escape character.
1232 """
1232 """
1233
1233
1234 if text is None:
1234 if text is None:
1235 ui.status(_("reading DAG from stdin\n"))
1235 ui.status(_("reading DAG from stdin\n"))
1236 text = ui.fin.read()
1236 text = ui.fin.read()
1237
1237
1238 cl = repo.changelog
1238 cl = repo.changelog
1239 if len(cl) > 0:
1239 if len(cl) > 0:
1240 raise util.Abort(_('repository is not empty'))
1240 raise util.Abort(_('repository is not empty'))
1241
1241
1242 # determine number of revs in DAG
1242 # determine number of revs in DAG
1243 total = 0
1243 total = 0
1244 for type, data in dagparser.parsedag(text):
1244 for type, data in dagparser.parsedag(text):
1245 if type == 'n':
1245 if type == 'n':
1246 total += 1
1246 total += 1
1247
1247
1248 if mergeable_file:
1248 if mergeable_file:
1249 linesperrev = 2
1249 linesperrev = 2
1250 # make a file with k lines per rev
1250 # make a file with k lines per rev
1251 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1251 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1252 initialmergedlines.append("")
1252 initialmergedlines.append("")
1253
1253
1254 tags = []
1254 tags = []
1255
1255
1256 tr = repo.transaction("builddag")
1256 tr = repo.transaction("builddag")
1257 try:
1257 try:
1258
1258
1259 at = -1
1259 at = -1
1260 atbranch = 'default'
1260 atbranch = 'default'
1261 nodeids = []
1261 nodeids = []
1262 ui.progress(_('building'), 0, unit=_('revisions'), total=total)
1262 ui.progress(_('building'), 0, unit=_('revisions'), total=total)
1263 for type, data in dagparser.parsedag(text):
1263 for type, data in dagparser.parsedag(text):
1264 if type == 'n':
1264 if type == 'n':
1265 ui.note('node %s\n' % str(data))
1265 ui.note('node %s\n' % str(data))
1266 id, ps = data
1266 id, ps = data
1267
1267
1268 files = []
1268 files = []
1269 fctxs = {}
1269 fctxs = {}
1270
1270
1271 p2 = None
1271 p2 = None
1272 if mergeable_file:
1272 if mergeable_file:
1273 fn = "mf"
1273 fn = "mf"
1274 p1 = repo[ps[0]]
1274 p1 = repo[ps[0]]
1275 if len(ps) > 1:
1275 if len(ps) > 1:
1276 p2 = repo[ps[1]]
1276 p2 = repo[ps[1]]
1277 pa = p1.ancestor(p2)
1277 pa = p1.ancestor(p2)
1278 base, local, other = [x[fn].data() for x in pa, p1, p2]
1278 base, local, other = [x[fn].data() for x in pa, p1, p2]
1279 m3 = simplemerge.Merge3Text(base, local, other)
1279 m3 = simplemerge.Merge3Text(base, local, other)
1280 ml = [l.strip() for l in m3.merge_lines()]
1280 ml = [l.strip() for l in m3.merge_lines()]
1281 ml.append("")
1281 ml.append("")
1282 elif at > 0:
1282 elif at > 0:
1283 ml = p1[fn].data().split("\n")
1283 ml = p1[fn].data().split("\n")
1284 else:
1284 else:
1285 ml = initialmergedlines
1285 ml = initialmergedlines
1286 ml[id * linesperrev] += " r%i" % id
1286 ml[id * linesperrev] += " r%i" % id
1287 mergedtext = "\n".join(ml)
1287 mergedtext = "\n".join(ml)
1288 files.append(fn)
1288 files.append(fn)
1289 fctxs[fn] = context.memfilectx(fn, mergedtext)
1289 fctxs[fn] = context.memfilectx(fn, mergedtext)
1290
1290
1291 if overwritten_file:
1291 if overwritten_file:
1292 fn = "of"
1292 fn = "of"
1293 files.append(fn)
1293 files.append(fn)
1294 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1294 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1295
1295
1296 if new_file:
1296 if new_file:
1297 fn = "nf%i" % id
1297 fn = "nf%i" % id
1298 files.append(fn)
1298 files.append(fn)
1299 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1299 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1300 if len(ps) > 1:
1300 if len(ps) > 1:
1301 if not p2:
1301 if not p2:
1302 p2 = repo[ps[1]]
1302 p2 = repo[ps[1]]
1303 for fn in p2:
1303 for fn in p2:
1304 if fn.startswith("nf"):
1304 if fn.startswith("nf"):
1305 files.append(fn)
1305 files.append(fn)
1306 fctxs[fn] = p2[fn]
1306 fctxs[fn] = p2[fn]
1307
1307
1308 def fctxfn(repo, cx, path):
1308 def fctxfn(repo, cx, path):
1309 return fctxs.get(path)
1309 return fctxs.get(path)
1310
1310
1311 if len(ps) == 0 or ps[0] < 0:
1311 if len(ps) == 0 or ps[0] < 0:
1312 pars = [None, None]
1312 pars = [None, None]
1313 elif len(ps) == 1:
1313 elif len(ps) == 1:
1314 pars = [nodeids[ps[0]], None]
1314 pars = [nodeids[ps[0]], None]
1315 else:
1315 else:
1316 pars = [nodeids[p] for p in ps]
1316 pars = [nodeids[p] for p in ps]
1317 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1317 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1318 date=(id, 0),
1318 date=(id, 0),
1319 user="debugbuilddag",
1319 user="debugbuilddag",
1320 extra={'branch': atbranch})
1320 extra={'branch': atbranch})
1321 nodeid = repo.commitctx(cx)
1321 nodeid = repo.commitctx(cx)
1322 nodeids.append(nodeid)
1322 nodeids.append(nodeid)
1323 at = id
1323 at = id
1324 elif type == 'l':
1324 elif type == 'l':
1325 id, name = data
1325 id, name = data
1326 ui.note('tag %s\n' % name)
1326 ui.note('tag %s\n' % name)
1327 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1327 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1328 elif type == 'a':
1328 elif type == 'a':
1329 ui.note('branch %s\n' % data)
1329 ui.note('branch %s\n' % data)
1330 atbranch = data
1330 atbranch = data
1331 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1331 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1332 tr.close()
1332 tr.close()
1333 finally:
1333 finally:
1334 ui.progress(_('building'), None)
1334 ui.progress(_('building'), None)
1335 tr.release()
1335 tr.release()
1336
1336
1337 if tags:
1337 if tags:
1338 repo.opener.write("localtags", "".join(tags))
1338 repo.opener.write("localtags", "".join(tags))
1339
1339
1340 @command('debugbundle', [('a', 'all', None, _('show all details'))], _('FILE'))
1340 @command('debugbundle', [('a', 'all', None, _('show all details'))], _('FILE'))
1341 def debugbundle(ui, bundlepath, all=None, **opts):
1341 def debugbundle(ui, bundlepath, all=None, **opts):
1342 """lists the contents of a bundle"""
1342 """lists the contents of a bundle"""
1343 f = url.open(ui, bundlepath)
1343 f = url.open(ui, bundlepath)
1344 try:
1344 try:
1345 gen = changegroup.readbundle(f, bundlepath)
1345 gen = changegroup.readbundle(f, bundlepath)
1346 if all:
1346 if all:
1347 ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
1347 ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
1348
1348
1349 def showchunks(named):
1349 def showchunks(named):
1350 ui.write("\n%s\n" % named)
1350 ui.write("\n%s\n" % named)
1351 chain = None
1351 chain = None
1352 while True:
1352 while True:
1353 chunkdata = gen.deltachunk(chain)
1353 chunkdata = gen.deltachunk(chain)
1354 if not chunkdata:
1354 if not chunkdata:
1355 break
1355 break
1356 node = chunkdata['node']
1356 node = chunkdata['node']
1357 p1 = chunkdata['p1']
1357 p1 = chunkdata['p1']
1358 p2 = chunkdata['p2']
1358 p2 = chunkdata['p2']
1359 cs = chunkdata['cs']
1359 cs = chunkdata['cs']
1360 deltabase = chunkdata['deltabase']
1360 deltabase = chunkdata['deltabase']
1361 delta = chunkdata['delta']
1361 delta = chunkdata['delta']
1362 ui.write("%s %s %s %s %s %s\n" %
1362 ui.write("%s %s %s %s %s %s\n" %
1363 (hex(node), hex(p1), hex(p2),
1363 (hex(node), hex(p1), hex(p2),
1364 hex(cs), hex(deltabase), len(delta)))
1364 hex(cs), hex(deltabase), len(delta)))
1365 chain = node
1365 chain = node
1366
1366
1367 chunkdata = gen.changelogheader()
1367 chunkdata = gen.changelogheader()
1368 showchunks("changelog")
1368 showchunks("changelog")
1369 chunkdata = gen.manifestheader()
1369 chunkdata = gen.manifestheader()
1370 showchunks("manifest")
1370 showchunks("manifest")
1371 while True:
1371 while True:
1372 chunkdata = gen.filelogheader()
1372 chunkdata = gen.filelogheader()
1373 if not chunkdata:
1373 if not chunkdata:
1374 break
1374 break
1375 fname = chunkdata['filename']
1375 fname = chunkdata['filename']
1376 showchunks(fname)
1376 showchunks(fname)
1377 else:
1377 else:
1378 chunkdata = gen.changelogheader()
1378 chunkdata = gen.changelogheader()
1379 chain = None
1379 chain = None
1380 while True:
1380 while True:
1381 chunkdata = gen.deltachunk(chain)
1381 chunkdata = gen.deltachunk(chain)
1382 if not chunkdata:
1382 if not chunkdata:
1383 break
1383 break
1384 node = chunkdata['node']
1384 node = chunkdata['node']
1385 ui.write("%s\n" % hex(node))
1385 ui.write("%s\n" % hex(node))
1386 chain = node
1386 chain = node
1387 finally:
1387 finally:
1388 f.close()
1388 f.close()
1389
1389
1390 @command('debugcheckstate', [], '')
1390 @command('debugcheckstate', [], '')
1391 def debugcheckstate(ui, repo):
1391 def debugcheckstate(ui, repo):
1392 """validate the correctness of the current dirstate"""
1392 """validate the correctness of the current dirstate"""
1393 parent1, parent2 = repo.dirstate.parents()
1393 parent1, parent2 = repo.dirstate.parents()
1394 m1 = repo[parent1].manifest()
1394 m1 = repo[parent1].manifest()
1395 m2 = repo[parent2].manifest()
1395 m2 = repo[parent2].manifest()
1396 errors = 0
1396 errors = 0
1397 for f in repo.dirstate:
1397 for f in repo.dirstate:
1398 state = repo.dirstate[f]
1398 state = repo.dirstate[f]
1399 if state in "nr" and f not in m1:
1399 if state in "nr" and f not in m1:
1400 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1400 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1401 errors += 1
1401 errors += 1
1402 if state in "a" and f in m1:
1402 if state in "a" and f in m1:
1403 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1403 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1404 errors += 1
1404 errors += 1
1405 if state in "m" and f not in m1 and f not in m2:
1405 if state in "m" and f not in m1 and f not in m2:
1406 ui.warn(_("%s in state %s, but not in either manifest\n") %
1406 ui.warn(_("%s in state %s, but not in either manifest\n") %
1407 (f, state))
1407 (f, state))
1408 errors += 1
1408 errors += 1
1409 for f in m1:
1409 for f in m1:
1410 state = repo.dirstate[f]
1410 state = repo.dirstate[f]
1411 if state not in "nrm":
1411 if state not in "nrm":
1412 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1412 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1413 errors += 1
1413 errors += 1
1414 if errors:
1414 if errors:
1415 error = _(".hg/dirstate inconsistent with current parent's manifest")
1415 error = _(".hg/dirstate inconsistent with current parent's manifest")
1416 raise util.Abort(error)
1416 raise util.Abort(error)
1417
1417
1418 @command('debugcommands', [], _('[COMMAND]'))
1418 @command('debugcommands', [], _('[COMMAND]'))
1419 def debugcommands(ui, cmd='', *args):
1419 def debugcommands(ui, cmd='', *args):
1420 """list all available commands and options"""
1420 """list all available commands and options"""
1421 for cmd, vals in sorted(table.iteritems()):
1421 for cmd, vals in sorted(table.iteritems()):
1422 cmd = cmd.split('|')[0].strip('^')
1422 cmd = cmd.split('|')[0].strip('^')
1423 opts = ', '.join([i[1] for i in vals[1]])
1423 opts = ', '.join([i[1] for i in vals[1]])
1424 ui.write('%s: %s\n' % (cmd, opts))
1424 ui.write('%s: %s\n' % (cmd, opts))
1425
1425
1426 @command('debugcomplete',
1426 @command('debugcomplete',
1427 [('o', 'options', None, _('show the command options'))],
1427 [('o', 'options', None, _('show the command options'))],
1428 _('[-o] CMD'))
1428 _('[-o] CMD'))
1429 def debugcomplete(ui, cmd='', **opts):
1429 def debugcomplete(ui, cmd='', **opts):
1430 """returns the completion list associated with the given command"""
1430 """returns the completion list associated with the given command"""
1431
1431
1432 if opts.get('options'):
1432 if opts.get('options'):
1433 options = []
1433 options = []
1434 otables = [globalopts]
1434 otables = [globalopts]
1435 if cmd:
1435 if cmd:
1436 aliases, entry = cmdutil.findcmd(cmd, table, False)
1436 aliases, entry = cmdutil.findcmd(cmd, table, False)
1437 otables.append(entry[1])
1437 otables.append(entry[1])
1438 for t in otables:
1438 for t in otables:
1439 for o in t:
1439 for o in t:
1440 if "(DEPRECATED)" in o[3]:
1440 if "(DEPRECATED)" in o[3]:
1441 continue
1441 continue
1442 if o[0]:
1442 if o[0]:
1443 options.append('-%s' % o[0])
1443 options.append('-%s' % o[0])
1444 options.append('--%s' % o[1])
1444 options.append('--%s' % o[1])
1445 ui.write("%s\n" % "\n".join(options))
1445 ui.write("%s\n" % "\n".join(options))
1446 return
1446 return
1447
1447
1448 cmdlist = cmdutil.findpossible(cmd, table)
1448 cmdlist = cmdutil.findpossible(cmd, table)
1449 if ui.verbose:
1449 if ui.verbose:
1450 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1450 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1451 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1451 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1452
1452
1453 @command('debugdag',
1453 @command('debugdag',
1454 [('t', 'tags', None, _('use tags as labels')),
1454 [('t', 'tags', None, _('use tags as labels')),
1455 ('b', 'branches', None, _('annotate with branch names')),
1455 ('b', 'branches', None, _('annotate with branch names')),
1456 ('', 'dots', None, _('use dots for runs')),
1456 ('', 'dots', None, _('use dots for runs')),
1457 ('s', 'spaces', None, _('separate elements by spaces'))],
1457 ('s', 'spaces', None, _('separate elements by spaces'))],
1458 _('[OPTION]... [FILE [REV]...]'))
1458 _('[OPTION]... [FILE [REV]...]'))
1459 def debugdag(ui, repo, file_=None, *revs, **opts):
1459 def debugdag(ui, repo, file_=None, *revs, **opts):
1460 """format the changelog or an index DAG as a concise textual description
1460 """format the changelog or an index DAG as a concise textual description
1461
1461
1462 If you pass a revlog index, the revlog's DAG is emitted. If you list
1462 If you pass a revlog index, the revlog's DAG is emitted. If you list
1463 revision numbers, they get labelled in the output as rN.
1463 revision numbers, they get labelled in the output as rN.
1464
1464
1465 Otherwise, the changelog DAG of the current repo is emitted.
1465 Otherwise, the changelog DAG of the current repo is emitted.
1466 """
1466 """
1467 spaces = opts.get('spaces')
1467 spaces = opts.get('spaces')
1468 dots = opts.get('dots')
1468 dots = opts.get('dots')
1469 if file_:
1469 if file_:
1470 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1470 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1471 revs = set((int(r) for r in revs))
1471 revs = set((int(r) for r in revs))
1472 def events():
1472 def events():
1473 for r in rlog:
1473 for r in rlog:
1474 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1474 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1475 if r in revs:
1475 if r in revs:
1476 yield 'l', (r, "r%i" % r)
1476 yield 'l', (r, "r%i" % r)
1477 elif repo:
1477 elif repo:
1478 cl = repo.changelog
1478 cl = repo.changelog
1479 tags = opts.get('tags')
1479 tags = opts.get('tags')
1480 branches = opts.get('branches')
1480 branches = opts.get('branches')
1481 if tags:
1481 if tags:
1482 labels = {}
1482 labels = {}
1483 for l, n in repo.tags().items():
1483 for l, n in repo.tags().items():
1484 labels.setdefault(cl.rev(n), []).append(l)
1484 labels.setdefault(cl.rev(n), []).append(l)
1485 def events():
1485 def events():
1486 b = "default"
1486 b = "default"
1487 for r in cl:
1487 for r in cl:
1488 if branches:
1488 if branches:
1489 newb = cl.read(cl.node(r))[5]['branch']
1489 newb = cl.read(cl.node(r))[5]['branch']
1490 if newb != b:
1490 if newb != b:
1491 yield 'a', newb
1491 yield 'a', newb
1492 b = newb
1492 b = newb
1493 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1493 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1494 if tags:
1494 if tags:
1495 ls = labels.get(r)
1495 ls = labels.get(r)
1496 if ls:
1496 if ls:
1497 for l in ls:
1497 for l in ls:
1498 yield 'l', (r, l)
1498 yield 'l', (r, l)
1499 else:
1499 else:
1500 raise util.Abort(_('need repo for changelog dag'))
1500 raise util.Abort(_('need repo for changelog dag'))
1501
1501
1502 for line in dagparser.dagtextlines(events(),
1502 for line in dagparser.dagtextlines(events(),
1503 addspaces=spaces,
1503 addspaces=spaces,
1504 wraplabels=True,
1504 wraplabels=True,
1505 wrapannotations=True,
1505 wrapannotations=True,
1506 wrapnonlinear=dots,
1506 wrapnonlinear=dots,
1507 usedots=dots,
1507 usedots=dots,
1508 maxlinewidth=70):
1508 maxlinewidth=70):
1509 ui.write(line)
1509 ui.write(line)
1510 ui.write("\n")
1510 ui.write("\n")
1511
1511
1512 @command('debugdata',
1512 @command('debugdata',
1513 [('c', 'changelog', False, _('open changelog')),
1513 [('c', 'changelog', False, _('open changelog')),
1514 ('m', 'manifest', False, _('open manifest'))],
1514 ('m', 'manifest', False, _('open manifest'))],
1515 _('-c|-m|FILE REV'))
1515 _('-c|-m|FILE REV'))
1516 def debugdata(ui, repo, file_, rev = None, **opts):
1516 def debugdata(ui, repo, file_, rev = None, **opts):
1517 """dump the contents of a data file revision"""
1517 """dump the contents of a data file revision"""
1518 if opts.get('changelog') or opts.get('manifest'):
1518 if opts.get('changelog') or opts.get('manifest'):
1519 file_, rev = None, file_
1519 file_, rev = None, file_
1520 elif rev is None:
1520 elif rev is None:
1521 raise error.CommandError('debugdata', _('invalid arguments'))
1521 raise error.CommandError('debugdata', _('invalid arguments'))
1522 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
1522 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
1523 try:
1523 try:
1524 ui.write(r.revision(r.lookup(rev)))
1524 ui.write(r.revision(r.lookup(rev)))
1525 except KeyError:
1525 except KeyError:
1526 raise util.Abort(_('invalid revision identifier %s') % rev)
1526 raise util.Abort(_('invalid revision identifier %s') % rev)
1527
1527
1528 @command('debugdate',
1528 @command('debugdate',
1529 [('e', 'extended', None, _('try extended date formats'))],
1529 [('e', 'extended', None, _('try extended date formats'))],
1530 _('[-e] DATE [RANGE]'))
1530 _('[-e] DATE [RANGE]'))
1531 def debugdate(ui, date, range=None, **opts):
1531 def debugdate(ui, date, range=None, **opts):
1532 """parse and display a date"""
1532 """parse and display a date"""
1533 if opts["extended"]:
1533 if opts["extended"]:
1534 d = util.parsedate(date, util.extendeddateformats)
1534 d = util.parsedate(date, util.extendeddateformats)
1535 else:
1535 else:
1536 d = util.parsedate(date)
1536 d = util.parsedate(date)
1537 ui.write("internal: %s %s\n" % d)
1537 ui.write("internal: %s %s\n" % d)
1538 ui.write("standard: %s\n" % util.datestr(d))
1538 ui.write("standard: %s\n" % util.datestr(d))
1539 if range:
1539 if range:
1540 m = util.matchdate(range)
1540 m = util.matchdate(range)
1541 ui.write("match: %s\n" % m(d[0]))
1541 ui.write("match: %s\n" % m(d[0]))
1542
1542
1543 @command('debugdiscovery',
1543 @command('debugdiscovery',
1544 [('', 'old', None, _('use old-style discovery')),
1544 [('', 'old', None, _('use old-style discovery')),
1545 ('', 'nonheads', None,
1545 ('', 'nonheads', None,
1546 _('use old-style discovery with non-heads included')),
1546 _('use old-style discovery with non-heads included')),
1547 ] + remoteopts,
1547 ] + remoteopts,
1548 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
1548 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
1549 def debugdiscovery(ui, repo, remoteurl="default", **opts):
1549 def debugdiscovery(ui, repo, remoteurl="default", **opts):
1550 """runs the changeset discovery protocol in isolation"""
1550 """runs the changeset discovery protocol in isolation"""
1551 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl), opts.get('branch'))
1551 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl), opts.get('branch'))
1552 remote = hg.peer(repo, opts, remoteurl)
1552 remote = hg.peer(repo, opts, remoteurl)
1553 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
1553 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
1554
1554
1555 # make sure tests are repeatable
1555 # make sure tests are repeatable
1556 random.seed(12323)
1556 random.seed(12323)
1557
1557
1558 def doit(localheads, remoteheads):
1558 def doit(localheads, remoteheads):
1559 if opts.get('old'):
1559 if opts.get('old'):
1560 if localheads:
1560 if localheads:
1561 raise util.Abort('cannot use localheads with old style discovery')
1561 raise util.Abort('cannot use localheads with old style discovery')
1562 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
1562 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
1563 force=True)
1563 force=True)
1564 common = set(common)
1564 common = set(common)
1565 if not opts.get('nonheads'):
1565 if not opts.get('nonheads'):
1566 ui.write("unpruned common: %s\n" % " ".join([short(n)
1566 ui.write("unpruned common: %s\n" % " ".join([short(n)
1567 for n in common]))
1567 for n in common]))
1568 dag = dagutil.revlogdag(repo.changelog)
1568 dag = dagutil.revlogdag(repo.changelog)
1569 all = dag.ancestorset(dag.internalizeall(common))
1569 all = dag.ancestorset(dag.internalizeall(common))
1570 common = dag.externalizeall(dag.headsetofconnecteds(all))
1570 common = dag.externalizeall(dag.headsetofconnecteds(all))
1571 else:
1571 else:
1572 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
1572 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
1573 common = set(common)
1573 common = set(common)
1574 rheads = set(hds)
1574 rheads = set(hds)
1575 lheads = set(repo.heads())
1575 lheads = set(repo.heads())
1576 ui.write("common heads: %s\n" % " ".join([short(n) for n in common]))
1576 ui.write("common heads: %s\n" % " ".join([short(n) for n in common]))
1577 if lheads <= common:
1577 if lheads <= common:
1578 ui.write("local is subset\n")
1578 ui.write("local is subset\n")
1579 elif rheads <= common:
1579 elif rheads <= common:
1580 ui.write("remote is subset\n")
1580 ui.write("remote is subset\n")
1581
1581
1582 serverlogs = opts.get('serverlog')
1582 serverlogs = opts.get('serverlog')
1583 if serverlogs:
1583 if serverlogs:
1584 for filename in serverlogs:
1584 for filename in serverlogs:
1585 logfile = open(filename, 'r')
1585 logfile = open(filename, 'r')
1586 try:
1586 try:
1587 line = logfile.readline()
1587 line = logfile.readline()
1588 while line:
1588 while line:
1589 parts = line.strip().split(';')
1589 parts = line.strip().split(';')
1590 op = parts[1]
1590 op = parts[1]
1591 if op == 'cg':
1591 if op == 'cg':
1592 pass
1592 pass
1593 elif op == 'cgss':
1593 elif op == 'cgss':
1594 doit(parts[2].split(' '), parts[3].split(' '))
1594 doit(parts[2].split(' '), parts[3].split(' '))
1595 elif op == 'unb':
1595 elif op == 'unb':
1596 doit(parts[3].split(' '), parts[2].split(' '))
1596 doit(parts[3].split(' '), parts[2].split(' '))
1597 line = logfile.readline()
1597 line = logfile.readline()
1598 finally:
1598 finally:
1599 logfile.close()
1599 logfile.close()
1600
1600
1601 else:
1601 else:
1602 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
1602 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
1603 opts.get('remote_head'))
1603 opts.get('remote_head'))
1604 localrevs = opts.get('local_head')
1604 localrevs = opts.get('local_head')
1605 doit(localrevs, remoterevs)
1605 doit(localrevs, remoterevs)
1606
1606
1607 @command('debugfileset', [], ('REVSPEC'))
1607 @command('debugfileset', [], ('REVSPEC'))
1608 def debugfileset(ui, repo, expr):
1608 def debugfileset(ui, repo, expr):
1609 '''parse and apply a fileset specification'''
1609 '''parse and apply a fileset specification'''
1610 if ui.verbose:
1610 if ui.verbose:
1611 tree = fileset.parse(expr)[0]
1611 tree = fileset.parse(expr)[0]
1612 ui.note(tree, "\n")
1612 ui.note(tree, "\n")
1613
1613
1614 for f in fileset.getfileset(repo[None], expr):
1614 for f in fileset.getfileset(repo[None], expr):
1615 ui.write("%s\n" % f)
1615 ui.write("%s\n" % f)
1616
1616
1617 @command('debugfsinfo', [], _('[PATH]'))
1617 @command('debugfsinfo', [], _('[PATH]'))
1618 def debugfsinfo(ui, path = "."):
1618 def debugfsinfo(ui, path = "."):
1619 """show information detected about current filesystem"""
1619 """show information detected about current filesystem"""
1620 util.writefile('.debugfsinfo', '')
1620 util.writefile('.debugfsinfo', '')
1621 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1621 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1622 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1622 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1623 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1623 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1624 and 'yes' or 'no'))
1624 and 'yes' or 'no'))
1625 os.unlink('.debugfsinfo')
1625 os.unlink('.debugfsinfo')
1626
1626
1627 @command('debuggetbundle',
1627 @command('debuggetbundle',
1628 [('H', 'head', [], _('id of head node'), _('ID')),
1628 [('H', 'head', [], _('id of head node'), _('ID')),
1629 ('C', 'common', [], _('id of common node'), _('ID')),
1629 ('C', 'common', [], _('id of common node'), _('ID')),
1630 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
1630 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
1631 _('REPO FILE [-H|-C ID]...'))
1631 _('REPO FILE [-H|-C ID]...'))
1632 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1632 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1633 """retrieves a bundle from a repo
1633 """retrieves a bundle from a repo
1634
1634
1635 Every ID must be a full-length hex node id string. Saves the bundle to the
1635 Every ID must be a full-length hex node id string. Saves the bundle to the
1636 given file.
1636 given file.
1637 """
1637 """
1638 repo = hg.peer(ui, opts, repopath)
1638 repo = hg.peer(ui, opts, repopath)
1639 if not repo.capable('getbundle'):
1639 if not repo.capable('getbundle'):
1640 raise util.Abort("getbundle() not supported by target repository")
1640 raise util.Abort("getbundle() not supported by target repository")
1641 args = {}
1641 args = {}
1642 if common:
1642 if common:
1643 args['common'] = [bin(s) for s in common]
1643 args['common'] = [bin(s) for s in common]
1644 if head:
1644 if head:
1645 args['heads'] = [bin(s) for s in head]
1645 args['heads'] = [bin(s) for s in head]
1646 bundle = repo.getbundle('debug', **args)
1646 bundle = repo.getbundle('debug', **args)
1647
1647
1648 bundletype = opts.get('type', 'bzip2').lower()
1648 bundletype = opts.get('type', 'bzip2').lower()
1649 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1649 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1650 bundletype = btypes.get(bundletype)
1650 bundletype = btypes.get(bundletype)
1651 if bundletype not in changegroup.bundletypes:
1651 if bundletype not in changegroup.bundletypes:
1652 raise util.Abort(_('unknown bundle type specified with --type'))
1652 raise util.Abort(_('unknown bundle type specified with --type'))
1653 changegroup.writebundle(bundle, bundlepath, bundletype)
1653 changegroup.writebundle(bundle, bundlepath, bundletype)
1654
1654
1655 @command('debugignore', [], '')
1655 @command('debugignore', [], '')
1656 def debugignore(ui, repo, *values, **opts):
1656 def debugignore(ui, repo, *values, **opts):
1657 """display the combined ignore pattern"""
1657 """display the combined ignore pattern"""
1658 ignore = repo.dirstate._ignore
1658 ignore = repo.dirstate._ignore
1659 if hasattr(ignore, 'includepat'):
1659 if hasattr(ignore, 'includepat'):
1660 ui.write("%s\n" % ignore.includepat)
1660 ui.write("%s\n" % ignore.includepat)
1661 else:
1661 else:
1662 raise util.Abort(_("no ignore patterns found"))
1662 raise util.Abort(_("no ignore patterns found"))
1663
1663
1664 @command('debugindex',
1664 @command('debugindex',
1665 [('c', 'changelog', False, _('open changelog')),
1665 [('c', 'changelog', False, _('open changelog')),
1666 ('m', 'manifest', False, _('open manifest')),
1666 ('m', 'manifest', False, _('open manifest')),
1667 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1667 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1668 _('[-f FORMAT] -c|-m|FILE'))
1668 _('[-f FORMAT] -c|-m|FILE'))
1669 def debugindex(ui, repo, file_ = None, **opts):
1669 def debugindex(ui, repo, file_ = None, **opts):
1670 """dump the contents of an index file"""
1670 """dump the contents of an index file"""
1671 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
1671 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
1672 format = opts.get('format', 0)
1672 format = opts.get('format', 0)
1673 if format not in (0, 1):
1673 if format not in (0, 1):
1674 raise util.Abort(_("unknown format %d") % format)
1674 raise util.Abort(_("unknown format %d") % format)
1675
1675
1676 generaldelta = r.version & revlog.REVLOGGENERALDELTA
1676 generaldelta = r.version & revlog.REVLOGGENERALDELTA
1677 if generaldelta:
1677 if generaldelta:
1678 basehdr = ' delta'
1678 basehdr = ' delta'
1679 else:
1679 else:
1680 basehdr = ' base'
1680 basehdr = ' base'
1681
1681
1682 if format == 0:
1682 if format == 0:
1683 ui.write(" rev offset length " + basehdr + " linkrev"
1683 ui.write(" rev offset length " + basehdr + " linkrev"
1684 " nodeid p1 p2\n")
1684 " nodeid p1 p2\n")
1685 elif format == 1:
1685 elif format == 1:
1686 ui.write(" rev flag offset length"
1686 ui.write(" rev flag offset length"
1687 " size " + basehdr + " link p1 p2 nodeid\n")
1687 " size " + basehdr + " link p1 p2 nodeid\n")
1688
1688
1689 for i in r:
1689 for i in r:
1690 node = r.node(i)
1690 node = r.node(i)
1691 if generaldelta:
1691 if generaldelta:
1692 base = r.deltaparent(i)
1692 base = r.deltaparent(i)
1693 else:
1693 else:
1694 base = r.chainbase(i)
1694 base = r.chainbase(i)
1695 if format == 0:
1695 if format == 0:
1696 try:
1696 try:
1697 pp = r.parents(node)
1697 pp = r.parents(node)
1698 except:
1698 except:
1699 pp = [nullid, nullid]
1699 pp = [nullid, nullid]
1700 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1700 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1701 i, r.start(i), r.length(i), base, r.linkrev(i),
1701 i, r.start(i), r.length(i), base, r.linkrev(i),
1702 short(node), short(pp[0]), short(pp[1])))
1702 short(node), short(pp[0]), short(pp[1])))
1703 elif format == 1:
1703 elif format == 1:
1704 pr = r.parentrevs(i)
1704 pr = r.parentrevs(i)
1705 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1705 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1706 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1706 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1707 base, r.linkrev(i), pr[0], pr[1], short(node)))
1707 base, r.linkrev(i), pr[0], pr[1], short(node)))
1708
1708
1709 @command('debugindexdot', [], _('FILE'))
1709 @command('debugindexdot', [], _('FILE'))
1710 def debugindexdot(ui, repo, file_):
1710 def debugindexdot(ui, repo, file_):
1711 """dump an index DAG as a graphviz dot file"""
1711 """dump an index DAG as a graphviz dot file"""
1712 r = None
1712 r = None
1713 if repo:
1713 if repo:
1714 filelog = repo.file(file_)
1714 filelog = repo.file(file_)
1715 if len(filelog):
1715 if len(filelog):
1716 r = filelog
1716 r = filelog
1717 if not r:
1717 if not r:
1718 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1718 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1719 ui.write("digraph G {\n")
1719 ui.write("digraph G {\n")
1720 for i in r:
1720 for i in r:
1721 node = r.node(i)
1721 node = r.node(i)
1722 pp = r.parents(node)
1722 pp = r.parents(node)
1723 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1723 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1724 if pp[1] != nullid:
1724 if pp[1] != nullid:
1725 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1725 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1726 ui.write("}\n")
1726 ui.write("}\n")
1727
1727
1728 @command('debuginstall', [], '')
1728 @command('debuginstall', [], '')
1729 def debuginstall(ui):
1729 def debuginstall(ui):
1730 '''test Mercurial installation
1730 '''test Mercurial installation
1731
1731
1732 Returns 0 on success.
1732 Returns 0 on success.
1733 '''
1733 '''
1734
1734
1735 def writetemp(contents):
1735 def writetemp(contents):
1736 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1736 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1737 f = os.fdopen(fd, "wb")
1737 f = os.fdopen(fd, "wb")
1738 f.write(contents)
1738 f.write(contents)
1739 f.close()
1739 f.close()
1740 return name
1740 return name
1741
1741
1742 problems = 0
1742 problems = 0
1743
1743
1744 # encoding
1744 # encoding
1745 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1745 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1746 try:
1746 try:
1747 encoding.fromlocal("test")
1747 encoding.fromlocal("test")
1748 except util.Abort, inst:
1748 except util.Abort, inst:
1749 ui.write(" %s\n" % inst)
1749 ui.write(" %s\n" % inst)
1750 ui.write(_(" (check that your locale is properly set)\n"))
1750 ui.write(_(" (check that your locale is properly set)\n"))
1751 problems += 1
1751 problems += 1
1752
1752
1753 # compiled modules
1753 # compiled modules
1754 ui.status(_("Checking installed modules (%s)...\n")
1754 ui.status(_("Checking installed modules (%s)...\n")
1755 % os.path.dirname(__file__))
1755 % os.path.dirname(__file__))
1756 try:
1756 try:
1757 import bdiff, mpatch, base85, osutil
1757 import bdiff, mpatch, base85, osutil
1758 except Exception, inst:
1758 except Exception, inst:
1759 ui.write(" %s\n" % inst)
1759 ui.write(" %s\n" % inst)
1760 ui.write(_(" One or more extensions could not be found"))
1760 ui.write(_(" One or more extensions could not be found"))
1761 ui.write(_(" (check that you compiled the extensions)\n"))
1761 ui.write(_(" (check that you compiled the extensions)\n"))
1762 problems += 1
1762 problems += 1
1763
1763
1764 # templates
1764 # templates
1765 ui.status(_("Checking templates...\n"))
1765 ui.status(_("Checking templates...\n"))
1766 try:
1766 try:
1767 import templater
1767 import templater
1768 templater.templater(templater.templatepath("map-cmdline.default"))
1768 templater.templater(templater.templatepath("map-cmdline.default"))
1769 except Exception, inst:
1769 except Exception, inst:
1770 ui.write(" %s\n" % inst)
1770 ui.write(" %s\n" % inst)
1771 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1771 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1772 problems += 1
1772 problems += 1
1773
1773
1774 # editor
1774 # editor
1775 ui.status(_("Checking commit editor...\n"))
1775 ui.status(_("Checking commit editor...\n"))
1776 editor = ui.geteditor()
1776 editor = ui.geteditor()
1777 cmdpath = util.findexe(editor) or util.findexe(editor.split()[0])
1777 cmdpath = util.findexe(editor) or util.findexe(editor.split()[0])
1778 if not cmdpath:
1778 if not cmdpath:
1779 if editor == 'vi':
1779 if editor == 'vi':
1780 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1780 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1781 ui.write(_(" (specify a commit editor in your configuration"
1781 ui.write(_(" (specify a commit editor in your configuration"
1782 " file)\n"))
1782 " file)\n"))
1783 else:
1783 else:
1784 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1784 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1785 ui.write(_(" (specify a commit editor in your configuration"
1785 ui.write(_(" (specify a commit editor in your configuration"
1786 " file)\n"))
1786 " file)\n"))
1787 problems += 1
1787 problems += 1
1788
1788
1789 # check username
1789 # check username
1790 ui.status(_("Checking username...\n"))
1790 ui.status(_("Checking username...\n"))
1791 try:
1791 try:
1792 ui.username()
1792 ui.username()
1793 except util.Abort, e:
1793 except util.Abort, e:
1794 ui.write(" %s\n" % e)
1794 ui.write(" %s\n" % e)
1795 ui.write(_(" (specify a username in your configuration file)\n"))
1795 ui.write(_(" (specify a username in your configuration file)\n"))
1796 problems += 1
1796 problems += 1
1797
1797
1798 if not problems:
1798 if not problems:
1799 ui.status(_("No problems detected\n"))
1799 ui.status(_("No problems detected\n"))
1800 else:
1800 else:
1801 ui.write(_("%s problems detected,"
1801 ui.write(_("%s problems detected,"
1802 " please check your install!\n") % problems)
1802 " please check your install!\n") % problems)
1803
1803
1804 return problems
1804 return problems
1805
1805
1806 @command('debugknown', [], _('REPO ID...'))
1806 @command('debugknown', [], _('REPO ID...'))
1807 def debugknown(ui, repopath, *ids, **opts):
1807 def debugknown(ui, repopath, *ids, **opts):
1808 """test whether node ids are known to a repo
1808 """test whether node ids are known to a repo
1809
1809
1810 Every ID must be a full-length hex node id string. Returns a list of 0s and 1s
1810 Every ID must be a full-length hex node id string. Returns a list of 0s and 1s
1811 indicating unknown/known.
1811 indicating unknown/known.
1812 """
1812 """
1813 repo = hg.peer(ui, opts, repopath)
1813 repo = hg.peer(ui, opts, repopath)
1814 if not repo.capable('known'):
1814 if not repo.capable('known'):
1815 raise util.Abort("known() not supported by target repository")
1815 raise util.Abort("known() not supported by target repository")
1816 flags = repo.known([bin(s) for s in ids])
1816 flags = repo.known([bin(s) for s in ids])
1817 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1817 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1818
1818
1819 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'))
1819 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'))
1820 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
1820 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
1821 '''access the pushkey key/value protocol
1821 '''access the pushkey key/value protocol
1822
1822
1823 With two args, list the keys in the given namespace.
1823 With two args, list the keys in the given namespace.
1824
1824
1825 With five args, set a key to new if it currently is set to old.
1825 With five args, set a key to new if it currently is set to old.
1826 Reports success or failure.
1826 Reports success or failure.
1827 '''
1827 '''
1828
1828
1829 target = hg.peer(ui, {}, repopath)
1829 target = hg.peer(ui, {}, repopath)
1830 if keyinfo:
1830 if keyinfo:
1831 key, old, new = keyinfo
1831 key, old, new = keyinfo
1832 r = target.pushkey(namespace, key, old, new)
1832 r = target.pushkey(namespace, key, old, new)
1833 ui.status(str(r) + '\n')
1833 ui.status(str(r) + '\n')
1834 return not r
1834 return not r
1835 else:
1835 else:
1836 for k, v in target.listkeys(namespace).iteritems():
1836 for k, v in target.listkeys(namespace).iteritems():
1837 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1837 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1838 v.encode('string-escape')))
1838 v.encode('string-escape')))
1839
1839
1840 @command('debugrebuildstate',
1840 @command('debugrebuildstate',
1841 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
1841 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
1842 _('[-r REV] [REV]'))
1842 _('[-r REV] [REV]'))
1843 def debugrebuildstate(ui, repo, rev="tip"):
1843 def debugrebuildstate(ui, repo, rev="tip"):
1844 """rebuild the dirstate as it would look like for the given revision"""
1844 """rebuild the dirstate as it would look like for the given revision"""
1845 ctx = scmutil.revsingle(repo, rev)
1845 ctx = scmutil.revsingle(repo, rev)
1846 wlock = repo.wlock()
1846 wlock = repo.wlock()
1847 try:
1847 try:
1848 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1848 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1849 finally:
1849 finally:
1850 wlock.release()
1850 wlock.release()
1851
1851
1852 @command('debugrename',
1852 @command('debugrename',
1853 [('r', 'rev', '', _('revision to debug'), _('REV'))],
1853 [('r', 'rev', '', _('revision to debug'), _('REV'))],
1854 _('[-r REV] FILE'))
1854 _('[-r REV] FILE'))
1855 def debugrename(ui, repo, file1, *pats, **opts):
1855 def debugrename(ui, repo, file1, *pats, **opts):
1856 """dump rename information"""
1856 """dump rename information"""
1857
1857
1858 ctx = scmutil.revsingle(repo, opts.get('rev'))
1858 ctx = scmutil.revsingle(repo, opts.get('rev'))
1859 m = scmutil.match(ctx, (file1,) + pats, opts)
1859 m = scmutil.match(ctx, (file1,) + pats, opts)
1860 for abs in ctx.walk(m):
1860 for abs in ctx.walk(m):
1861 fctx = ctx[abs]
1861 fctx = ctx[abs]
1862 o = fctx.filelog().renamed(fctx.filenode())
1862 o = fctx.filelog().renamed(fctx.filenode())
1863 rel = m.rel(abs)
1863 rel = m.rel(abs)
1864 if o:
1864 if o:
1865 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1865 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1866 else:
1866 else:
1867 ui.write(_("%s not renamed\n") % rel)
1867 ui.write(_("%s not renamed\n") % rel)
1868
1868
1869 @command('debugrevlog',
1869 @command('debugrevlog',
1870 [('c', 'changelog', False, _('open changelog')),
1870 [('c', 'changelog', False, _('open changelog')),
1871 ('m', 'manifest', False, _('open manifest')),
1871 ('m', 'manifest', False, _('open manifest')),
1872 ('d', 'dump', False, _('dump index data'))],
1872 ('d', 'dump', False, _('dump index data'))],
1873 _('-c|-m|FILE'))
1873 _('-c|-m|FILE'))
1874 def debugrevlog(ui, repo, file_ = None, **opts):
1874 def debugrevlog(ui, repo, file_ = None, **opts):
1875 """show data and statistics about a revlog"""
1875 """show data and statistics about a revlog"""
1876 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
1876 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
1877
1877
1878 if opts.get("dump"):
1878 if opts.get("dump"):
1879 numrevs = len(r)
1879 numrevs = len(r)
1880 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
1880 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
1881 " rawsize totalsize compression heads\n")
1881 " rawsize totalsize compression heads\n")
1882 ts = 0
1882 ts = 0
1883 heads = set()
1883 heads = set()
1884 for rev in xrange(numrevs):
1884 for rev in xrange(numrevs):
1885 dbase = r.deltaparent(rev)
1885 dbase = r.deltaparent(rev)
1886 if dbase == -1:
1886 if dbase == -1:
1887 dbase = rev
1887 dbase = rev
1888 cbase = r.chainbase(rev)
1888 cbase = r.chainbase(rev)
1889 p1, p2 = r.parentrevs(rev)
1889 p1, p2 = r.parentrevs(rev)
1890 rs = r.rawsize(rev)
1890 rs = r.rawsize(rev)
1891 ts = ts + rs
1891 ts = ts + rs
1892 heads -= set(r.parentrevs(rev))
1892 heads -= set(r.parentrevs(rev))
1893 heads.add(rev)
1893 heads.add(rev)
1894 ui.write("%d %d %d %d %d %d %d %d %d %d %d %d %d\n" %
1894 ui.write("%d %d %d %d %d %d %d %d %d %d %d %d %d\n" %
1895 (rev, p1, p2, r.start(rev), r.end(rev),
1895 (rev, p1, p2, r.start(rev), r.end(rev),
1896 r.start(dbase), r.start(cbase),
1896 r.start(dbase), r.start(cbase),
1897 r.start(p1), r.start(p2),
1897 r.start(p1), r.start(p2),
1898 rs, ts, ts / r.end(rev), len(heads)))
1898 rs, ts, ts / r.end(rev), len(heads)))
1899 return 0
1899 return 0
1900
1900
1901 v = r.version
1901 v = r.version
1902 format = v & 0xFFFF
1902 format = v & 0xFFFF
1903 flags = []
1903 flags = []
1904 gdelta = False
1904 gdelta = False
1905 if v & revlog.REVLOGNGINLINEDATA:
1905 if v & revlog.REVLOGNGINLINEDATA:
1906 flags.append('inline')
1906 flags.append('inline')
1907 if v & revlog.REVLOGGENERALDELTA:
1907 if v & revlog.REVLOGGENERALDELTA:
1908 gdelta = True
1908 gdelta = True
1909 flags.append('generaldelta')
1909 flags.append('generaldelta')
1910 if not flags:
1910 if not flags:
1911 flags = ['(none)']
1911 flags = ['(none)']
1912
1912
1913 nummerges = 0
1913 nummerges = 0
1914 numfull = 0
1914 numfull = 0
1915 numprev = 0
1915 numprev = 0
1916 nump1 = 0
1916 nump1 = 0
1917 nump2 = 0
1917 nump2 = 0
1918 numother = 0
1918 numother = 0
1919 nump1prev = 0
1919 nump1prev = 0
1920 nump2prev = 0
1920 nump2prev = 0
1921 chainlengths = []
1921 chainlengths = []
1922
1922
1923 datasize = [None, 0, 0L]
1923 datasize = [None, 0, 0L]
1924 fullsize = [None, 0, 0L]
1924 fullsize = [None, 0, 0L]
1925 deltasize = [None, 0, 0L]
1925 deltasize = [None, 0, 0L]
1926
1926
1927 def addsize(size, l):
1927 def addsize(size, l):
1928 if l[0] is None or size < l[0]:
1928 if l[0] is None or size < l[0]:
1929 l[0] = size
1929 l[0] = size
1930 if size > l[1]:
1930 if size > l[1]:
1931 l[1] = size
1931 l[1] = size
1932 l[2] += size
1932 l[2] += size
1933
1933
1934 numrevs = len(r)
1934 numrevs = len(r)
1935 for rev in xrange(numrevs):
1935 for rev in xrange(numrevs):
1936 p1, p2 = r.parentrevs(rev)
1936 p1, p2 = r.parentrevs(rev)
1937 delta = r.deltaparent(rev)
1937 delta = r.deltaparent(rev)
1938 if format > 0:
1938 if format > 0:
1939 addsize(r.rawsize(rev), datasize)
1939 addsize(r.rawsize(rev), datasize)
1940 if p2 != nullrev:
1940 if p2 != nullrev:
1941 nummerges += 1
1941 nummerges += 1
1942 size = r.length(rev)
1942 size = r.length(rev)
1943 if delta == nullrev:
1943 if delta == nullrev:
1944 chainlengths.append(0)
1944 chainlengths.append(0)
1945 numfull += 1
1945 numfull += 1
1946 addsize(size, fullsize)
1946 addsize(size, fullsize)
1947 else:
1947 else:
1948 chainlengths.append(chainlengths[delta] + 1)
1948 chainlengths.append(chainlengths[delta] + 1)
1949 addsize(size, deltasize)
1949 addsize(size, deltasize)
1950 if delta == rev - 1:
1950 if delta == rev - 1:
1951 numprev += 1
1951 numprev += 1
1952 if delta == p1:
1952 if delta == p1:
1953 nump1prev += 1
1953 nump1prev += 1
1954 elif delta == p2:
1954 elif delta == p2:
1955 nump2prev += 1
1955 nump2prev += 1
1956 elif delta == p1:
1956 elif delta == p1:
1957 nump1 += 1
1957 nump1 += 1
1958 elif delta == p2:
1958 elif delta == p2:
1959 nump2 += 1
1959 nump2 += 1
1960 elif delta != nullrev:
1960 elif delta != nullrev:
1961 numother += 1
1961 numother += 1
1962
1962
1963 numdeltas = numrevs - numfull
1963 numdeltas = numrevs - numfull
1964 numoprev = numprev - nump1prev - nump2prev
1964 numoprev = numprev - nump1prev - nump2prev
1965 totalrawsize = datasize[2]
1965 totalrawsize = datasize[2]
1966 datasize[2] /= numrevs
1966 datasize[2] /= numrevs
1967 fulltotal = fullsize[2]
1967 fulltotal = fullsize[2]
1968 fullsize[2] /= numfull
1968 fullsize[2] /= numfull
1969 deltatotal = deltasize[2]
1969 deltatotal = deltasize[2]
1970 deltasize[2] /= numrevs - numfull
1970 deltasize[2] /= numrevs - numfull
1971 totalsize = fulltotal + deltatotal
1971 totalsize = fulltotal + deltatotal
1972 avgchainlen = sum(chainlengths) / numrevs
1972 avgchainlen = sum(chainlengths) / numrevs
1973 compratio = totalrawsize / totalsize
1973 compratio = totalrawsize / totalsize
1974
1974
1975 basedfmtstr = '%%%dd\n'
1975 basedfmtstr = '%%%dd\n'
1976 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
1976 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
1977
1977
1978 def dfmtstr(max):
1978 def dfmtstr(max):
1979 return basedfmtstr % len(str(max))
1979 return basedfmtstr % len(str(max))
1980 def pcfmtstr(max, padding=0):
1980 def pcfmtstr(max, padding=0):
1981 return basepcfmtstr % (len(str(max)), ' ' * padding)
1981 return basepcfmtstr % (len(str(max)), ' ' * padding)
1982
1982
1983 def pcfmt(value, total):
1983 def pcfmt(value, total):
1984 return (value, 100 * float(value) / total)
1984 return (value, 100 * float(value) / total)
1985
1985
1986 ui.write('format : %d\n' % format)
1986 ui.write('format : %d\n' % format)
1987 ui.write('flags : %s\n' % ', '.join(flags))
1987 ui.write('flags : %s\n' % ', '.join(flags))
1988
1988
1989 ui.write('\n')
1989 ui.write('\n')
1990 fmt = pcfmtstr(totalsize)
1990 fmt = pcfmtstr(totalsize)
1991 fmt2 = dfmtstr(totalsize)
1991 fmt2 = dfmtstr(totalsize)
1992 ui.write('revisions : ' + fmt2 % numrevs)
1992 ui.write('revisions : ' + fmt2 % numrevs)
1993 ui.write(' merges : ' + fmt % pcfmt(nummerges, numrevs))
1993 ui.write(' merges : ' + fmt % pcfmt(nummerges, numrevs))
1994 ui.write(' normal : ' + fmt % pcfmt(numrevs - nummerges, numrevs))
1994 ui.write(' normal : ' + fmt % pcfmt(numrevs - nummerges, numrevs))
1995 ui.write('revisions : ' + fmt2 % numrevs)
1995 ui.write('revisions : ' + fmt2 % numrevs)
1996 ui.write(' full : ' + fmt % pcfmt(numfull, numrevs))
1996 ui.write(' full : ' + fmt % pcfmt(numfull, numrevs))
1997 ui.write(' deltas : ' + fmt % pcfmt(numdeltas, numrevs))
1997 ui.write(' deltas : ' + fmt % pcfmt(numdeltas, numrevs))
1998 ui.write('revision size : ' + fmt2 % totalsize)
1998 ui.write('revision size : ' + fmt2 % totalsize)
1999 ui.write(' full : ' + fmt % pcfmt(fulltotal, totalsize))
1999 ui.write(' full : ' + fmt % pcfmt(fulltotal, totalsize))
2000 ui.write(' deltas : ' + fmt % pcfmt(deltatotal, totalsize))
2000 ui.write(' deltas : ' + fmt % pcfmt(deltatotal, totalsize))
2001
2001
2002 ui.write('\n')
2002 ui.write('\n')
2003 fmt = dfmtstr(max(avgchainlen, compratio))
2003 fmt = dfmtstr(max(avgchainlen, compratio))
2004 ui.write('avg chain length : ' + fmt % avgchainlen)
2004 ui.write('avg chain length : ' + fmt % avgchainlen)
2005 ui.write('compression ratio : ' + fmt % compratio)
2005 ui.write('compression ratio : ' + fmt % compratio)
2006
2006
2007 if format > 0:
2007 if format > 0:
2008 ui.write('\n')
2008 ui.write('\n')
2009 ui.write('uncompressed data size (min/max/avg) : %d / %d / %d\n'
2009 ui.write('uncompressed data size (min/max/avg) : %d / %d / %d\n'
2010 % tuple(datasize))
2010 % tuple(datasize))
2011 ui.write('full revision size (min/max/avg) : %d / %d / %d\n'
2011 ui.write('full revision size (min/max/avg) : %d / %d / %d\n'
2012 % tuple(fullsize))
2012 % tuple(fullsize))
2013 ui.write('delta size (min/max/avg) : %d / %d / %d\n'
2013 ui.write('delta size (min/max/avg) : %d / %d / %d\n'
2014 % tuple(deltasize))
2014 % tuple(deltasize))
2015
2015
2016 if numdeltas > 0:
2016 if numdeltas > 0:
2017 ui.write('\n')
2017 ui.write('\n')
2018 fmt = pcfmtstr(numdeltas)
2018 fmt = pcfmtstr(numdeltas)
2019 fmt2 = pcfmtstr(numdeltas, 4)
2019 fmt2 = pcfmtstr(numdeltas, 4)
2020 ui.write('deltas against prev : ' + fmt % pcfmt(numprev, numdeltas))
2020 ui.write('deltas against prev : ' + fmt % pcfmt(numprev, numdeltas))
2021 if numprev > 0:
2021 if numprev > 0:
2022 ui.write(' where prev = p1 : ' + fmt2 % pcfmt(nump1prev, numprev))
2022 ui.write(' where prev = p1 : ' + fmt2 % pcfmt(nump1prev, numprev))
2023 ui.write(' where prev = p2 : ' + fmt2 % pcfmt(nump2prev, numprev))
2023 ui.write(' where prev = p2 : ' + fmt2 % pcfmt(nump2prev, numprev))
2024 ui.write(' other : ' + fmt2 % pcfmt(numoprev, numprev))
2024 ui.write(' other : ' + fmt2 % pcfmt(numoprev, numprev))
2025 if gdelta:
2025 if gdelta:
2026 ui.write('deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas))
2026 ui.write('deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas))
2027 ui.write('deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas))
2027 ui.write('deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas))
2028 ui.write('deltas against other : ' + fmt % pcfmt(numother, numdeltas))
2028 ui.write('deltas against other : ' + fmt % pcfmt(numother, numdeltas))
2029
2029
2030 @command('debugrevspec', [], ('REVSPEC'))
2030 @command('debugrevspec', [], ('REVSPEC'))
2031 def debugrevspec(ui, repo, expr):
2031 def debugrevspec(ui, repo, expr):
2032 '''parse and apply a revision specification'''
2032 '''parse and apply a revision specification'''
2033 if ui.verbose:
2033 if ui.verbose:
2034 tree = revset.parse(expr)[0]
2034 tree = revset.parse(expr)[0]
2035 ui.note(tree, "\n")
2035 ui.note(tree, "\n")
2036 newtree = revset.findaliases(ui, tree)
2036 newtree = revset.findaliases(ui, tree)
2037 if newtree != tree:
2037 if newtree != tree:
2038 ui.note(newtree, "\n")
2038 ui.note(newtree, "\n")
2039 func = revset.match(ui, expr)
2039 func = revset.match(ui, expr)
2040 for c in func(repo, range(len(repo))):
2040 for c in func(repo, range(len(repo))):
2041 ui.write("%s\n" % c)
2041 ui.write("%s\n" % c)
2042
2042
2043 @command('debugsetparents', [], _('REV1 [REV2]'))
2043 @command('debugsetparents', [], _('REV1 [REV2]'))
2044 def debugsetparents(ui, repo, rev1, rev2=None):
2044 def debugsetparents(ui, repo, rev1, rev2=None):
2045 """manually set the parents of the current working directory
2045 """manually set the parents of the current working directory
2046
2046
2047 This is useful for writing repository conversion tools, but should
2047 This is useful for writing repository conversion tools, but should
2048 be used with care.
2048 be used with care.
2049
2049
2050 Returns 0 on success.
2050 Returns 0 on success.
2051 """
2051 """
2052
2052
2053 r1 = scmutil.revsingle(repo, rev1).node()
2053 r1 = scmutil.revsingle(repo, rev1).node()
2054 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2054 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2055
2055
2056 wlock = repo.wlock()
2056 wlock = repo.wlock()
2057 try:
2057 try:
2058 repo.dirstate.setparents(r1, r2)
2058 repo.dirstate.setparents(r1, r2)
2059 finally:
2059 finally:
2060 wlock.release()
2060 wlock.release()
2061
2061
2062 @command('debugstate',
2062 @command('debugstate',
2063 [('', 'nodates', None, _('do not display the saved mtime')),
2063 [('', 'nodates', None, _('do not display the saved mtime')),
2064 ('', 'datesort', None, _('sort by saved mtime'))],
2064 ('', 'datesort', None, _('sort by saved mtime'))],
2065 _('[OPTION]...'))
2065 _('[OPTION]...'))
2066 def debugstate(ui, repo, nodates=None, datesort=None):
2066 def debugstate(ui, repo, nodates=None, datesort=None):
2067 """show the contents of the current dirstate"""
2067 """show the contents of the current dirstate"""
2068 timestr = ""
2068 timestr = ""
2069 showdate = not nodates
2069 showdate = not nodates
2070 if datesort:
2070 if datesort:
2071 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2071 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2072 else:
2072 else:
2073 keyfunc = None # sort by filename
2073 keyfunc = None # sort by filename
2074 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2074 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2075 if showdate:
2075 if showdate:
2076 if ent[3] == -1:
2076 if ent[3] == -1:
2077 # Pad or slice to locale representation
2077 # Pad or slice to locale representation
2078 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
2078 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
2079 time.localtime(0)))
2079 time.localtime(0)))
2080 timestr = 'unset'
2080 timestr = 'unset'
2081 timestr = (timestr[:locale_len] +
2081 timestr = (timestr[:locale_len] +
2082 ' ' * (locale_len - len(timestr)))
2082 ' ' * (locale_len - len(timestr)))
2083 else:
2083 else:
2084 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2084 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2085 time.localtime(ent[3]))
2085 time.localtime(ent[3]))
2086 if ent[1] & 020000:
2086 if ent[1] & 020000:
2087 mode = 'lnk'
2087 mode = 'lnk'
2088 else:
2088 else:
2089 mode = '%3o' % (ent[1] & 0777)
2089 mode = '%3o' % (ent[1] & 0777)
2090 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2090 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2091 for f in repo.dirstate.copies():
2091 for f in repo.dirstate.copies():
2092 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2092 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2093
2093
2094 @command('debugsub',
2094 @command('debugsub',
2095 [('r', 'rev', '',
2095 [('r', 'rev', '',
2096 _('revision to check'), _('REV'))],
2096 _('revision to check'), _('REV'))],
2097 _('[-r REV] [REV]'))
2097 _('[-r REV] [REV]'))
2098 def debugsub(ui, repo, rev=None):
2098 def debugsub(ui, repo, rev=None):
2099 ctx = scmutil.revsingle(repo, rev, None)
2099 ctx = scmutil.revsingle(repo, rev, None)
2100 for k, v in sorted(ctx.substate.items()):
2100 for k, v in sorted(ctx.substate.items()):
2101 ui.write('path %s\n' % k)
2101 ui.write('path %s\n' % k)
2102 ui.write(' source %s\n' % v[0])
2102 ui.write(' source %s\n' % v[0])
2103 ui.write(' revision %s\n' % v[1])
2103 ui.write(' revision %s\n' % v[1])
2104
2104
2105 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'))
2105 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'))
2106 def debugwalk(ui, repo, *pats, **opts):
2106 def debugwalk(ui, repo, *pats, **opts):
2107 """show how files match on given patterns"""
2107 """show how files match on given patterns"""
2108 m = scmutil.match(repo[None], pats, opts)
2108 m = scmutil.match(repo[None], pats, opts)
2109 items = list(repo.walk(m))
2109 items = list(repo.walk(m))
2110 if not items:
2110 if not items:
2111 return
2111 return
2112 fmt = 'f %%-%ds %%-%ds %%s' % (
2112 fmt = 'f %%-%ds %%-%ds %%s' % (
2113 max([len(abs) for abs in items]),
2113 max([len(abs) for abs in items]),
2114 max([len(m.rel(abs)) for abs in items]))
2114 max([len(m.rel(abs)) for abs in items]))
2115 for abs in items:
2115 for abs in items:
2116 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
2116 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
2117 ui.write("%s\n" % line.rstrip())
2117 ui.write("%s\n" % line.rstrip())
2118
2118
2119 @command('debugwireargs',
2119 @command('debugwireargs',
2120 [('', 'three', '', 'three'),
2120 [('', 'three', '', 'three'),
2121 ('', 'four', '', 'four'),
2121 ('', 'four', '', 'four'),
2122 ('', 'five', '', 'five'),
2122 ('', 'five', '', 'five'),
2123 ] + remoteopts,
2123 ] + remoteopts,
2124 _('REPO [OPTIONS]... [ONE [TWO]]'))
2124 _('REPO [OPTIONS]... [ONE [TWO]]'))
2125 def debugwireargs(ui, repopath, *vals, **opts):
2125 def debugwireargs(ui, repopath, *vals, **opts):
2126 repo = hg.peer(ui, opts, repopath)
2126 repo = hg.peer(ui, opts, repopath)
2127 for opt in remoteopts:
2127 for opt in remoteopts:
2128 del opts[opt[1]]
2128 del opts[opt[1]]
2129 args = {}
2129 args = {}
2130 for k, v in opts.iteritems():
2130 for k, v in opts.iteritems():
2131 if v:
2131 if v:
2132 args[k] = v
2132 args[k] = v
2133 # run twice to check that we don't mess up the stream for the next command
2133 # run twice to check that we don't mess up the stream for the next command
2134 res1 = repo.debugwireargs(*vals, **args)
2134 res1 = repo.debugwireargs(*vals, **args)
2135 res2 = repo.debugwireargs(*vals, **args)
2135 res2 = repo.debugwireargs(*vals, **args)
2136 ui.write("%s\n" % res1)
2136 ui.write("%s\n" % res1)
2137 if res1 != res2:
2137 if res1 != res2:
2138 ui.warn("%s\n" % res2)
2138 ui.warn("%s\n" % res2)
2139
2139
2140 @command('^diff',
2140 @command('^diff',
2141 [('r', 'rev', [], _('revision'), _('REV')),
2141 [('r', 'rev', [], _('revision'), _('REV')),
2142 ('c', 'change', '', _('change made by revision'), _('REV'))
2142 ('c', 'change', '', _('change made by revision'), _('REV'))
2143 ] + diffopts + diffopts2 + walkopts + subrepoopts,
2143 ] + diffopts + diffopts2 + walkopts + subrepoopts,
2144 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'))
2144 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'))
2145 def diff(ui, repo, *pats, **opts):
2145 def diff(ui, repo, *pats, **opts):
2146 """diff repository (or selected files)
2146 """diff repository (or selected files)
2147
2147
2148 Show differences between revisions for the specified files.
2148 Show differences between revisions for the specified files.
2149
2149
2150 Differences between files are shown using the unified diff format.
2150 Differences between files are shown using the unified diff format.
2151
2151
2152 .. note::
2152 .. note::
2153 diff may generate unexpected results for merges, as it will
2153 diff may generate unexpected results for merges, as it will
2154 default to comparing against the working directory's first
2154 default to comparing against the working directory's first
2155 parent changeset if no revisions are specified.
2155 parent changeset if no revisions are specified.
2156
2156
2157 When two revision arguments are given, then changes are shown
2157 When two revision arguments are given, then changes are shown
2158 between those revisions. If only one revision is specified then
2158 between those revisions. If only one revision is specified then
2159 that revision is compared to the working directory, and, when no
2159 that revision is compared to the working directory, and, when no
2160 revisions are specified, the working directory files are compared
2160 revisions are specified, the working directory files are compared
2161 to its parent.
2161 to its parent.
2162
2162
2163 Alternatively you can specify -c/--change with a revision to see
2163 Alternatively you can specify -c/--change with a revision to see
2164 the changes in that changeset relative to its first parent.
2164 the changes in that changeset relative to its first parent.
2165
2165
2166 Without the -a/--text option, diff will avoid generating diffs of
2166 Without the -a/--text option, diff will avoid generating diffs of
2167 files it detects as binary. With -a, diff will generate a diff
2167 files it detects as binary. With -a, diff will generate a diff
2168 anyway, probably with undesirable results.
2168 anyway, probably with undesirable results.
2169
2169
2170 Use the -g/--git option to generate diffs in the git extended diff
2170 Use the -g/--git option to generate diffs in the git extended diff
2171 format. For more information, read :hg:`help diffs`.
2171 format. For more information, read :hg:`help diffs`.
2172
2172
2173 Returns 0 on success.
2173 Returns 0 on success.
2174 """
2174 """
2175
2175
2176 revs = opts.get('rev')
2176 revs = opts.get('rev')
2177 change = opts.get('change')
2177 change = opts.get('change')
2178 stat = opts.get('stat')
2178 stat = opts.get('stat')
2179 reverse = opts.get('reverse')
2179 reverse = opts.get('reverse')
2180
2180
2181 if revs and change:
2181 if revs and change:
2182 msg = _('cannot specify --rev and --change at the same time')
2182 msg = _('cannot specify --rev and --change at the same time')
2183 raise util.Abort(msg)
2183 raise util.Abort(msg)
2184 elif change:
2184 elif change:
2185 node2 = scmutil.revsingle(repo, change, None).node()
2185 node2 = scmutil.revsingle(repo, change, None).node()
2186 node1 = repo[node2].p1().node()
2186 node1 = repo[node2].p1().node()
2187 else:
2187 else:
2188 node1, node2 = scmutil.revpair(repo, revs)
2188 node1, node2 = scmutil.revpair(repo, revs)
2189
2189
2190 if reverse:
2190 if reverse:
2191 node1, node2 = node2, node1
2191 node1, node2 = node2, node1
2192
2192
2193 diffopts = patch.diffopts(ui, opts)
2193 diffopts = patch.diffopts(ui, opts)
2194 m = scmutil.match(repo[node2], pats, opts)
2194 m = scmutil.match(repo[node2], pats, opts)
2195 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2195 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2196 listsubrepos=opts.get('subrepos'))
2196 listsubrepos=opts.get('subrepos'))
2197
2197
2198 @command('^export',
2198 @command('^export',
2199 [('o', 'output', '',
2199 [('o', 'output', '',
2200 _('print output to file with formatted name'), _('FORMAT')),
2200 _('print output to file with formatted name'), _('FORMAT')),
2201 ('', 'switch-parent', None, _('diff against the second parent')),
2201 ('', 'switch-parent', None, _('diff against the second parent')),
2202 ('r', 'rev', [], _('revisions to export'), _('REV')),
2202 ('r', 'rev', [], _('revisions to export'), _('REV')),
2203 ] + diffopts,
2203 ] + diffopts,
2204 _('[OPTION]... [-o OUTFILESPEC] REV...'))
2204 _('[OPTION]... [-o OUTFILESPEC] REV...'))
2205 def export(ui, repo, *changesets, **opts):
2205 def export(ui, repo, *changesets, **opts):
2206 """dump the header and diffs for one or more changesets
2206 """dump the header and diffs for one or more changesets
2207
2207
2208 Print the changeset header and diffs for one or more revisions.
2208 Print the changeset header and diffs for one or more revisions.
2209
2209
2210 The information shown in the changeset header is: author, date,
2210 The information shown in the changeset header is: author, date,
2211 branch name (if non-default), changeset hash, parent(s) and commit
2211 branch name (if non-default), changeset hash, parent(s) and commit
2212 comment.
2212 comment.
2213
2213
2214 .. note::
2214 .. note::
2215 export may generate unexpected diff output for merge
2215 export may generate unexpected diff output for merge
2216 changesets, as it will compare the merge changeset against its
2216 changesets, as it will compare the merge changeset against its
2217 first parent only.
2217 first parent only.
2218
2218
2219 Output may be to a file, in which case the name of the file is
2219 Output may be to a file, in which case the name of the file is
2220 given using a format string. The formatting rules are as follows:
2220 given using a format string. The formatting rules are as follows:
2221
2221
2222 :``%%``: literal "%" character
2222 :``%%``: literal "%" character
2223 :``%H``: changeset hash (40 hexadecimal digits)
2223 :``%H``: changeset hash (40 hexadecimal digits)
2224 :``%N``: number of patches being generated
2224 :``%N``: number of patches being generated
2225 :``%R``: changeset revision number
2225 :``%R``: changeset revision number
2226 :``%b``: basename of the exporting repository
2226 :``%b``: basename of the exporting repository
2227 :``%h``: short-form changeset hash (12 hexadecimal digits)
2227 :``%h``: short-form changeset hash (12 hexadecimal digits)
2228 :``%n``: zero-padded sequence number, starting at 1
2228 :``%n``: zero-padded sequence number, starting at 1
2229 :``%r``: zero-padded changeset revision number
2229 :``%r``: zero-padded changeset revision number
2230
2230
2231 Without the -a/--text option, export will avoid generating diffs
2231 Without the -a/--text option, export will avoid generating diffs
2232 of files it detects as binary. With -a, export will generate a
2232 of files it detects as binary. With -a, export will generate a
2233 diff anyway, probably with undesirable results.
2233 diff anyway, probably with undesirable results.
2234
2234
2235 Use the -g/--git option to generate diffs in the git extended diff
2235 Use the -g/--git option to generate diffs in the git extended diff
2236 format. See :hg:`help diffs` for more information.
2236 format. See :hg:`help diffs` for more information.
2237
2237
2238 With the --switch-parent option, the diff will be against the
2238 With the --switch-parent option, the diff will be against the
2239 second parent. It can be useful to review a merge.
2239 second parent. It can be useful to review a merge.
2240
2240
2241 Returns 0 on success.
2241 Returns 0 on success.
2242 """
2242 """
2243 changesets += tuple(opts.get('rev', []))
2243 changesets += tuple(opts.get('rev', []))
2244 if not changesets:
2244 if not changesets:
2245 raise util.Abort(_("export requires at least one changeset"))
2245 raise util.Abort(_("export requires at least one changeset"))
2246 revs = scmutil.revrange(repo, changesets)
2246 revs = scmutil.revrange(repo, changesets)
2247 if len(revs) > 1:
2247 if len(revs) > 1:
2248 ui.note(_('exporting patches:\n'))
2248 ui.note(_('exporting patches:\n'))
2249 else:
2249 else:
2250 ui.note(_('exporting patch:\n'))
2250 ui.note(_('exporting patch:\n'))
2251 cmdutil.export(repo, revs, template=opts.get('output'),
2251 cmdutil.export(repo, revs, template=opts.get('output'),
2252 switch_parent=opts.get('switch_parent'),
2252 switch_parent=opts.get('switch_parent'),
2253 opts=patch.diffopts(ui, opts))
2253 opts=patch.diffopts(ui, opts))
2254
2254
2255 @command('^forget', walkopts, _('[OPTION]... FILE...'))
2255 @command('^forget', walkopts, _('[OPTION]... FILE...'))
2256 def forget(ui, repo, *pats, **opts):
2256 def forget(ui, repo, *pats, **opts):
2257 """forget the specified files on the next commit
2257 """forget the specified files on the next commit
2258
2258
2259 Mark the specified files so they will no longer be tracked
2259 Mark the specified files so they will no longer be tracked
2260 after the next commit.
2260 after the next commit.
2261
2261
2262 This only removes files from the current branch, not from the
2262 This only removes files from the current branch, not from the
2263 entire project history, and it does not delete them from the
2263 entire project history, and it does not delete them from the
2264 working directory.
2264 working directory.
2265
2265
2266 To undo a forget before the next commit, see :hg:`add`.
2266 To undo a forget before the next commit, see :hg:`add`.
2267
2267
2268 Returns 0 on success.
2268 Returns 0 on success.
2269 """
2269 """
2270
2270
2271 if not pats:
2271 if not pats:
2272 raise util.Abort(_('no files specified'))
2272 raise util.Abort(_('no files specified'))
2273
2273
2274 m = scmutil.match(repo[None], pats, opts)
2274 m = scmutil.match(repo[None], pats, opts)
2275 s = repo.status(match=m, clean=True)
2275 s = repo.status(match=m, clean=True)
2276 forget = sorted(s[0] + s[1] + s[3] + s[6])
2276 forget = sorted(s[0] + s[1] + s[3] + s[6])
2277 errs = 0
2277 errs = 0
2278
2278
2279 for f in m.files():
2279 for f in m.files():
2280 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2280 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2281 if os.path.exists(m.rel(f)):
2281 if os.path.exists(m.rel(f)):
2282 ui.warn(_('not removing %s: file is already untracked\n')
2282 ui.warn(_('not removing %s: file is already untracked\n')
2283 % m.rel(f))
2283 % m.rel(f))
2284 errs = 1
2284 errs = 1
2285
2285
2286 for f in forget:
2286 for f in forget:
2287 if ui.verbose or not m.exact(f):
2287 if ui.verbose or not m.exact(f):
2288 ui.status(_('removing %s\n') % m.rel(f))
2288 ui.status(_('removing %s\n') % m.rel(f))
2289
2289
2290 repo[None].forget(forget)
2290 repo[None].forget(forget)
2291 return errs
2291 return errs
2292
2292
2293 @command('grep',
2293 @command('grep',
2294 [('0', 'print0', None, _('end fields with NUL')),
2294 [('0', 'print0', None, _('end fields with NUL')),
2295 ('', 'all', None, _('print all revisions that match')),
2295 ('', 'all', None, _('print all revisions that match')),
2296 ('a', 'text', None, _('treat all files as text')),
2296 ('a', 'text', None, _('treat all files as text')),
2297 ('f', 'follow', None,
2297 ('f', 'follow', None,
2298 _('follow changeset history,'
2298 _('follow changeset history,'
2299 ' or file history across copies and renames')),
2299 ' or file history across copies and renames')),
2300 ('i', 'ignore-case', None, _('ignore case when matching')),
2300 ('i', 'ignore-case', None, _('ignore case when matching')),
2301 ('l', 'files-with-matches', None,
2301 ('l', 'files-with-matches', None,
2302 _('print only filenames and revisions that match')),
2302 _('print only filenames and revisions that match')),
2303 ('n', 'line-number', None, _('print matching line numbers')),
2303 ('n', 'line-number', None, _('print matching line numbers')),
2304 ('r', 'rev', [],
2304 ('r', 'rev', [],
2305 _('only search files changed within revision range'), _('REV')),
2305 _('only search files changed within revision range'), _('REV')),
2306 ('u', 'user', None, _('list the author (long with -v)')),
2306 ('u', 'user', None, _('list the author (long with -v)')),
2307 ('d', 'date', None, _('list the date (short with -q)')),
2307 ('d', 'date', None, _('list the date (short with -q)')),
2308 ] + walkopts,
2308 ] + walkopts,
2309 _('[OPTION]... PATTERN [FILE]...'))
2309 _('[OPTION]... PATTERN [FILE]...'))
2310 def grep(ui, repo, pattern, *pats, **opts):
2310 def grep(ui, repo, pattern, *pats, **opts):
2311 """search for a pattern in specified files and revisions
2311 """search for a pattern in specified files and revisions
2312
2312
2313 Search revisions of files for a regular expression.
2313 Search revisions of files for a regular expression.
2314
2314
2315 This command behaves differently than Unix grep. It only accepts
2315 This command behaves differently than Unix grep. It only accepts
2316 Python/Perl regexps. It searches repository history, not the
2316 Python/Perl regexps. It searches repository history, not the
2317 working directory. It always prints the revision number in which a
2317 working directory. It always prints the revision number in which a
2318 match appears.
2318 match appears.
2319
2319
2320 By default, grep only prints output for the first revision of a
2320 By default, grep only prints output for the first revision of a
2321 file in which it finds a match. To get it to print every revision
2321 file in which it finds a match. To get it to print every revision
2322 that contains a change in match status ("-" for a match that
2322 that contains a change in match status ("-" for a match that
2323 becomes a non-match, or "+" for a non-match that becomes a match),
2323 becomes a non-match, or "+" for a non-match that becomes a match),
2324 use the --all flag.
2324 use the --all flag.
2325
2325
2326 Returns 0 if a match is found, 1 otherwise.
2326 Returns 0 if a match is found, 1 otherwise.
2327 """
2327 """
2328 reflags = 0
2328 reflags = 0
2329 if opts.get('ignore_case'):
2329 if opts.get('ignore_case'):
2330 reflags |= re.I
2330 reflags |= re.I
2331 try:
2331 try:
2332 regexp = re.compile(pattern, reflags)
2332 regexp = re.compile(pattern, reflags)
2333 except re.error, inst:
2333 except re.error, inst:
2334 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2334 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2335 return 1
2335 return 1
2336 sep, eol = ':', '\n'
2336 sep, eol = ':', '\n'
2337 if opts.get('print0'):
2337 if opts.get('print0'):
2338 sep = eol = '\0'
2338 sep = eol = '\0'
2339
2339
2340 getfile = util.lrucachefunc(repo.file)
2340 getfile = util.lrucachefunc(repo.file)
2341
2341
2342 def matchlines(body):
2342 def matchlines(body):
2343 begin = 0
2343 begin = 0
2344 linenum = 0
2344 linenum = 0
2345 while True:
2345 while True:
2346 match = regexp.search(body, begin)
2346 match = regexp.search(body, begin)
2347 if not match:
2347 if not match:
2348 break
2348 break
2349 mstart, mend = match.span()
2349 mstart, mend = match.span()
2350 linenum += body.count('\n', begin, mstart) + 1
2350 linenum += body.count('\n', begin, mstart) + 1
2351 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2351 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2352 begin = body.find('\n', mend) + 1 or len(body)
2352 begin = body.find('\n', mend) + 1 or len(body)
2353 lend = begin - 1
2353 lend = begin - 1
2354 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2354 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2355
2355
2356 class linestate(object):
2356 class linestate(object):
2357 def __init__(self, line, linenum, colstart, colend):
2357 def __init__(self, line, linenum, colstart, colend):
2358 self.line = line
2358 self.line = line
2359 self.linenum = linenum
2359 self.linenum = linenum
2360 self.colstart = colstart
2360 self.colstart = colstart
2361 self.colend = colend
2361 self.colend = colend
2362
2362
2363 def __hash__(self):
2363 def __hash__(self):
2364 return hash((self.linenum, self.line))
2364 return hash((self.linenum, self.line))
2365
2365
2366 def __eq__(self, other):
2366 def __eq__(self, other):
2367 return self.line == other.line
2367 return self.line == other.line
2368
2368
2369 matches = {}
2369 matches = {}
2370 copies = {}
2370 copies = {}
2371 def grepbody(fn, rev, body):
2371 def grepbody(fn, rev, body):
2372 matches[rev].setdefault(fn, [])
2372 matches[rev].setdefault(fn, [])
2373 m = matches[rev][fn]
2373 m = matches[rev][fn]
2374 for lnum, cstart, cend, line in matchlines(body):
2374 for lnum, cstart, cend, line in matchlines(body):
2375 s = linestate(line, lnum, cstart, cend)
2375 s = linestate(line, lnum, cstart, cend)
2376 m.append(s)
2376 m.append(s)
2377
2377
2378 def difflinestates(a, b):
2378 def difflinestates(a, b):
2379 sm = difflib.SequenceMatcher(None, a, b)
2379 sm = difflib.SequenceMatcher(None, a, b)
2380 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2380 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2381 if tag == 'insert':
2381 if tag == 'insert':
2382 for i in xrange(blo, bhi):
2382 for i in xrange(blo, bhi):
2383 yield ('+', b[i])
2383 yield ('+', b[i])
2384 elif tag == 'delete':
2384 elif tag == 'delete':
2385 for i in xrange(alo, ahi):
2385 for i in xrange(alo, ahi):
2386 yield ('-', a[i])
2386 yield ('-', a[i])
2387 elif tag == 'replace':
2387 elif tag == 'replace':
2388 for i in xrange(alo, ahi):
2388 for i in xrange(alo, ahi):
2389 yield ('-', a[i])
2389 yield ('-', a[i])
2390 for i in xrange(blo, bhi):
2390 for i in xrange(blo, bhi):
2391 yield ('+', b[i])
2391 yield ('+', b[i])
2392
2392
2393 def display(fn, ctx, pstates, states):
2393 def display(fn, ctx, pstates, states):
2394 rev = ctx.rev()
2394 rev = ctx.rev()
2395 datefunc = ui.quiet and util.shortdate or util.datestr
2395 datefunc = ui.quiet and util.shortdate or util.datestr
2396 found = False
2396 found = False
2397 filerevmatches = {}
2397 filerevmatches = {}
2398 def binary():
2398 def binary():
2399 flog = getfile(fn)
2399 flog = getfile(fn)
2400 return util.binary(flog.read(ctx.filenode(fn)))
2400 return util.binary(flog.read(ctx.filenode(fn)))
2401
2401
2402 if opts.get('all'):
2402 if opts.get('all'):
2403 iter = difflinestates(pstates, states)
2403 iter = difflinestates(pstates, states)
2404 else:
2404 else:
2405 iter = [('', l) for l in states]
2405 iter = [('', l) for l in states]
2406 for change, l in iter:
2406 for change, l in iter:
2407 cols = [fn, str(rev)]
2407 cols = [fn, str(rev)]
2408 before, match, after = None, None, None
2408 before, match, after = None, None, None
2409 if opts.get('line_number'):
2409 if opts.get('line_number'):
2410 cols.append(str(l.linenum))
2410 cols.append(str(l.linenum))
2411 if opts.get('all'):
2411 if opts.get('all'):
2412 cols.append(change)
2412 cols.append(change)
2413 if opts.get('user'):
2413 if opts.get('user'):
2414 cols.append(ui.shortuser(ctx.user()))
2414 cols.append(ui.shortuser(ctx.user()))
2415 if opts.get('date'):
2415 if opts.get('date'):
2416 cols.append(datefunc(ctx.date()))
2416 cols.append(datefunc(ctx.date()))
2417 if opts.get('files_with_matches'):
2417 if opts.get('files_with_matches'):
2418 c = (fn, rev)
2418 c = (fn, rev)
2419 if c in filerevmatches:
2419 if c in filerevmatches:
2420 continue
2420 continue
2421 filerevmatches[c] = 1
2421 filerevmatches[c] = 1
2422 else:
2422 else:
2423 before = l.line[:l.colstart]
2423 before = l.line[:l.colstart]
2424 match = l.line[l.colstart:l.colend]
2424 match = l.line[l.colstart:l.colend]
2425 after = l.line[l.colend:]
2425 after = l.line[l.colend:]
2426 ui.write(sep.join(cols))
2426 ui.write(sep.join(cols))
2427 if before is not None:
2427 if before is not None:
2428 if not opts.get('text') and binary():
2428 if not opts.get('text') and binary():
2429 ui.write(sep + " Binary file matches")
2429 ui.write(sep + " Binary file matches")
2430 else:
2430 else:
2431 ui.write(sep + before)
2431 ui.write(sep + before)
2432 ui.write(match, label='grep.match')
2432 ui.write(match, label='grep.match')
2433 ui.write(after)
2433 ui.write(after)
2434 ui.write(eol)
2434 ui.write(eol)
2435 found = True
2435 found = True
2436 return found
2436 return found
2437
2437
2438 skip = {}
2438 skip = {}
2439 revfiles = {}
2439 revfiles = {}
2440 matchfn = scmutil.match(repo[None], pats, opts)
2440 matchfn = scmutil.match(repo[None], pats, opts)
2441 found = False
2441 found = False
2442 follow = opts.get('follow')
2442 follow = opts.get('follow')
2443
2443
2444 def prep(ctx, fns):
2444 def prep(ctx, fns):
2445 rev = ctx.rev()
2445 rev = ctx.rev()
2446 pctx = ctx.p1()
2446 pctx = ctx.p1()
2447 parent = pctx.rev()
2447 parent = pctx.rev()
2448 matches.setdefault(rev, {})
2448 matches.setdefault(rev, {})
2449 matches.setdefault(parent, {})
2449 matches.setdefault(parent, {})
2450 files = revfiles.setdefault(rev, [])
2450 files = revfiles.setdefault(rev, [])
2451 for fn in fns:
2451 for fn in fns:
2452 flog = getfile(fn)
2452 flog = getfile(fn)
2453 try:
2453 try:
2454 fnode = ctx.filenode(fn)
2454 fnode = ctx.filenode(fn)
2455 except error.LookupError:
2455 except error.LookupError:
2456 continue
2456 continue
2457
2457
2458 copied = flog.renamed(fnode)
2458 copied = flog.renamed(fnode)
2459 copy = follow and copied and copied[0]
2459 copy = follow and copied and copied[0]
2460 if copy:
2460 if copy:
2461 copies.setdefault(rev, {})[fn] = copy
2461 copies.setdefault(rev, {})[fn] = copy
2462 if fn in skip:
2462 if fn in skip:
2463 if copy:
2463 if copy:
2464 skip[copy] = True
2464 skip[copy] = True
2465 continue
2465 continue
2466 files.append(fn)
2466 files.append(fn)
2467
2467
2468 if fn not in matches[rev]:
2468 if fn not in matches[rev]:
2469 grepbody(fn, rev, flog.read(fnode))
2469 grepbody(fn, rev, flog.read(fnode))
2470
2470
2471 pfn = copy or fn
2471 pfn = copy or fn
2472 if pfn not in matches[parent]:
2472 if pfn not in matches[parent]:
2473 try:
2473 try:
2474 fnode = pctx.filenode(pfn)
2474 fnode = pctx.filenode(pfn)
2475 grepbody(pfn, parent, flog.read(fnode))
2475 grepbody(pfn, parent, flog.read(fnode))
2476 except error.LookupError:
2476 except error.LookupError:
2477 pass
2477 pass
2478
2478
2479 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2479 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2480 rev = ctx.rev()
2480 rev = ctx.rev()
2481 parent = ctx.p1().rev()
2481 parent = ctx.p1().rev()
2482 for fn in sorted(revfiles.get(rev, [])):
2482 for fn in sorted(revfiles.get(rev, [])):
2483 states = matches[rev][fn]
2483 states = matches[rev][fn]
2484 copy = copies.get(rev, {}).get(fn)
2484 copy = copies.get(rev, {}).get(fn)
2485 if fn in skip:
2485 if fn in skip:
2486 if copy:
2486 if copy:
2487 skip[copy] = True
2487 skip[copy] = True
2488 continue
2488 continue
2489 pstates = matches.get(parent, {}).get(copy or fn, [])
2489 pstates = matches.get(parent, {}).get(copy or fn, [])
2490 if pstates or states:
2490 if pstates or states:
2491 r = display(fn, ctx, pstates, states)
2491 r = display(fn, ctx, pstates, states)
2492 found = found or r
2492 found = found or r
2493 if r and not opts.get('all'):
2493 if r and not opts.get('all'):
2494 skip[fn] = True
2494 skip[fn] = True
2495 if copy:
2495 if copy:
2496 skip[copy] = True
2496 skip[copy] = True
2497 del matches[rev]
2497 del matches[rev]
2498 del revfiles[rev]
2498 del revfiles[rev]
2499
2499
2500 return not found
2500 return not found
2501
2501
2502 @command('heads',
2502 @command('heads',
2503 [('r', 'rev', '',
2503 [('r', 'rev', '',
2504 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2504 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2505 ('t', 'topo', False, _('show topological heads only')),
2505 ('t', 'topo', False, _('show topological heads only')),
2506 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2506 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2507 ('c', 'closed', False, _('show normal and closed branch heads')),
2507 ('c', 'closed', False, _('show normal and closed branch heads')),
2508 ] + templateopts,
2508 ] + templateopts,
2509 _('[-ac] [-r STARTREV] [REV]...'))
2509 _('[-ac] [-r STARTREV] [REV]...'))
2510 def heads(ui, repo, *branchrevs, **opts):
2510 def heads(ui, repo, *branchrevs, **opts):
2511 """show current repository heads or show branch heads
2511 """show current repository heads or show branch heads
2512
2512
2513 With no arguments, show all repository branch heads.
2513 With no arguments, show all repository branch heads.
2514
2514
2515 Repository "heads" are changesets with no child changesets. They are
2515 Repository "heads" are changesets with no child changesets. They are
2516 where development generally takes place and are the usual targets
2516 where development generally takes place and are the usual targets
2517 for update and merge operations. Branch heads are changesets that have
2517 for update and merge operations. Branch heads are changesets that have
2518 no child changeset on the same branch.
2518 no child changeset on the same branch.
2519
2519
2520 If one or more REVs are given, only branch heads on the branches
2520 If one or more REVs are given, only branch heads on the branches
2521 associated with the specified changesets are shown.
2521 associated with the specified changesets are shown.
2522
2522
2523 If -c/--closed is specified, also show branch heads marked closed
2523 If -c/--closed is specified, also show branch heads marked closed
2524 (see :hg:`commit --close-branch`).
2524 (see :hg:`commit --close-branch`).
2525
2525
2526 If STARTREV is specified, only those heads that are descendants of
2526 If STARTREV is specified, only those heads that are descendants of
2527 STARTREV will be displayed.
2527 STARTREV will be displayed.
2528
2528
2529 If -t/--topo is specified, named branch mechanics will be ignored and only
2529 If -t/--topo is specified, named branch mechanics will be ignored and only
2530 changesets without children will be shown.
2530 changesets without children will be shown.
2531
2531
2532 Returns 0 if matching heads are found, 1 if not.
2532 Returns 0 if matching heads are found, 1 if not.
2533 """
2533 """
2534
2534
2535 start = None
2535 start = None
2536 if 'rev' in opts:
2536 if 'rev' in opts:
2537 start = scmutil.revsingle(repo, opts['rev'], None).node()
2537 start = scmutil.revsingle(repo, opts['rev'], None).node()
2538
2538
2539 if opts.get('topo'):
2539 if opts.get('topo'):
2540 heads = [repo[h] for h in repo.heads(start)]
2540 heads = [repo[h] for h in repo.heads(start)]
2541 else:
2541 else:
2542 heads = []
2542 heads = []
2543 for branch in repo.branchmap():
2543 for branch in repo.branchmap():
2544 heads += repo.branchheads(branch, start, opts.get('closed'))
2544 heads += repo.branchheads(branch, start, opts.get('closed'))
2545 heads = [repo[h] for h in heads]
2545 heads = [repo[h] for h in heads]
2546
2546
2547 if branchrevs:
2547 if branchrevs:
2548 branches = set(repo[br].branch() for br in branchrevs)
2548 branches = set(repo[br].branch() for br in branchrevs)
2549 heads = [h for h in heads if h.branch() in branches]
2549 heads = [h for h in heads if h.branch() in branches]
2550
2550
2551 if opts.get('active') and branchrevs:
2551 if opts.get('active') and branchrevs:
2552 dagheads = repo.heads(start)
2552 dagheads = repo.heads(start)
2553 heads = [h for h in heads if h.node() in dagheads]
2553 heads = [h for h in heads if h.node() in dagheads]
2554
2554
2555 if branchrevs:
2555 if branchrevs:
2556 haveheads = set(h.branch() for h in heads)
2556 haveheads = set(h.branch() for h in heads)
2557 if branches - haveheads:
2557 if branches - haveheads:
2558 headless = ', '.join(b for b in branches - haveheads)
2558 headless = ', '.join(b for b in branches - haveheads)
2559 msg = _('no open branch heads found on branches %s')
2559 msg = _('no open branch heads found on branches %s')
2560 if opts.get('rev'):
2560 if opts.get('rev'):
2561 msg += _(' (started at %s)' % opts['rev'])
2561 msg += _(' (started at %s)' % opts['rev'])
2562 ui.warn((msg + '\n') % headless)
2562 ui.warn((msg + '\n') % headless)
2563
2563
2564 if not heads:
2564 if not heads:
2565 return 1
2565 return 1
2566
2566
2567 heads = sorted(heads, key=lambda x: -x.rev())
2567 heads = sorted(heads, key=lambda x: -x.rev())
2568 displayer = cmdutil.show_changeset(ui, repo, opts)
2568 displayer = cmdutil.show_changeset(ui, repo, opts)
2569 for ctx in heads:
2569 for ctx in heads:
2570 displayer.show(ctx)
2570 displayer.show(ctx)
2571 displayer.close()
2571 displayer.close()
2572
2572
2573 @command('help',
2573 @command('help',
2574 [('e', 'extension', None, _('show only help for extensions')),
2574 [('e', 'extension', None, _('show only help for extensions')),
2575 ('c', 'command', None, _('show only help for commands'))],
2575 ('c', 'command', None, _('show only help for commands'))],
2576 _('[-ec] [TOPIC]'))
2576 _('[-ec] [TOPIC]'))
2577 def help_(ui, name=None, with_version=False, unknowncmd=False, full=True, **opts):
2577 def help_(ui, name=None, with_version=False, unknowncmd=False, full=True, **opts):
2578 """show help for a given topic or a help overview
2578 """show help for a given topic or a help overview
2579
2579
2580 With no arguments, print a list of commands with short help messages.
2580 With no arguments, print a list of commands with short help messages.
2581
2581
2582 Given a topic, extension, or command name, print help for that
2582 Given a topic, extension, or command name, print help for that
2583 topic.
2583 topic.
2584
2584
2585 Returns 0 if successful.
2585 Returns 0 if successful.
2586 """
2586 """
2587 option_lists = []
2587 option_lists = []
2588 textwidth = min(ui.termwidth(), 80) - 2
2588 textwidth = min(ui.termwidth(), 80) - 2
2589
2589
2590 def addglobalopts(aliases):
2590 def addglobalopts(aliases):
2591 if ui.verbose:
2591 if ui.verbose:
2592 option_lists.append((_("global options:"), globalopts))
2592 option_lists.append((_("global options:"), globalopts))
2593 if name == 'shortlist':
2593 if name == 'shortlist':
2594 option_lists.append((_('use "hg help" for the full list '
2594 option_lists.append((_('use "hg help" for the full list '
2595 'of commands'), ()))
2595 'of commands'), ()))
2596 else:
2596 else:
2597 if name == 'shortlist':
2597 if name == 'shortlist':
2598 msg = _('use "hg help" for the full list of commands '
2598 msg = _('use "hg help" for the full list of commands '
2599 'or "hg -v" for details')
2599 'or "hg -v" for details')
2600 elif name and not full:
2600 elif name and not full:
2601 msg = _('use "hg help %s" to show the full help text' % name)
2601 msg = _('use "hg help %s" to show the full help text' % name)
2602 elif aliases:
2602 elif aliases:
2603 msg = _('use "hg -v help%s" to show builtin aliases and '
2603 msg = _('use "hg -v help%s" to show builtin aliases and '
2604 'global options') % (name and " " + name or "")
2604 'global options') % (name and " " + name or "")
2605 else:
2605 else:
2606 msg = _('use "hg -v help %s" to show global options') % name
2606 msg = _('use "hg -v help %s" to show global options') % name
2607 option_lists.append((msg, ()))
2607 option_lists.append((msg, ()))
2608
2608
2609 def helpcmd(name):
2609 def helpcmd(name):
2610 if with_version:
2610 if with_version:
2611 version_(ui)
2611 version_(ui)
2612 ui.write('\n')
2612 ui.write('\n')
2613
2613
2614 try:
2614 try:
2615 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
2615 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
2616 except error.AmbiguousCommand, inst:
2616 except error.AmbiguousCommand, inst:
2617 # py3k fix: except vars can't be used outside the scope of the
2617 # py3k fix: except vars can't be used outside the scope of the
2618 # except block, nor can be used inside a lambda. python issue4617
2618 # except block, nor can be used inside a lambda. python issue4617
2619 prefix = inst.args[0]
2619 prefix = inst.args[0]
2620 select = lambda c: c.lstrip('^').startswith(prefix)
2620 select = lambda c: c.lstrip('^').startswith(prefix)
2621 helplist(_('list of commands:\n\n'), select)
2621 helplist(_('list of commands:\n\n'), select)
2622 return
2622 return
2623
2623
2624 # check if it's an invalid alias and display its error if it is
2624 # check if it's an invalid alias and display its error if it is
2625 if getattr(entry[0], 'badalias', False):
2625 if getattr(entry[0], 'badalias', False):
2626 if not unknowncmd:
2626 if not unknowncmd:
2627 entry[0](ui)
2627 entry[0](ui)
2628 return
2628 return
2629
2629
2630 # synopsis
2630 # synopsis
2631 if len(entry) > 2:
2631 if len(entry) > 2:
2632 if entry[2].startswith('hg'):
2632 if entry[2].startswith('hg'):
2633 ui.write("%s\n" % entry[2])
2633 ui.write("%s\n" % entry[2])
2634 else:
2634 else:
2635 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
2635 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
2636 else:
2636 else:
2637 ui.write('hg %s\n' % aliases[0])
2637 ui.write('hg %s\n' % aliases[0])
2638
2638
2639 # aliases
2639 # aliases
2640 if full and not ui.quiet and len(aliases) > 1:
2640 if full and not ui.quiet and len(aliases) > 1:
2641 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
2641 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
2642
2642
2643 # description
2643 # description
2644 doc = gettext(entry[0].__doc__)
2644 doc = gettext(entry[0].__doc__)
2645 if not doc:
2645 if not doc:
2646 doc = _("(no help text available)")
2646 doc = _("(no help text available)")
2647 if hasattr(entry[0], 'definition'): # aliased command
2647 if hasattr(entry[0], 'definition'): # aliased command
2648 if entry[0].definition.startswith('!'): # shell alias
2648 if entry[0].definition.startswith('!'): # shell alias
2649 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
2649 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
2650 else:
2650 else:
2651 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
2651 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
2652 if ui.quiet or not full:
2652 if ui.quiet or not full:
2653 doc = doc.splitlines()[0]
2653 doc = doc.splitlines()[0]
2654 keep = ui.verbose and ['verbose'] or []
2654 keep = ui.verbose and ['verbose'] or []
2655 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
2655 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
2656 ui.write("\n%s\n" % formatted)
2656 ui.write("\n%s\n" % formatted)
2657 if pruned:
2657 if pruned:
2658 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
2658 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
2659
2659
2660 if not ui.quiet:
2660 if not ui.quiet:
2661 # options
2661 # options
2662 if entry[1]:
2662 if entry[1]:
2663 option_lists.append((_("options:\n"), entry[1]))
2663 option_lists.append((_("options:\n"), entry[1]))
2664
2664
2665 addglobalopts(False)
2665 addglobalopts(False)
2666
2666
2667 # check if this command shadows a non-trivial (multi-line)
2667 # check if this command shadows a non-trivial (multi-line)
2668 # extension help text
2668 # extension help text
2669 try:
2669 try:
2670 mod = extensions.find(name)
2670 mod = extensions.find(name)
2671 doc = gettext(mod.__doc__) or ''
2671 doc = gettext(mod.__doc__) or ''
2672 if '\n' in doc.strip():
2672 if '\n' in doc.strip():
2673 msg = _('use "hg help -e %s" to show help for '
2673 msg = _('use "hg help -e %s" to show help for '
2674 'the %s extension') % (name, name)
2674 'the %s extension') % (name, name)
2675 ui.write('\n%s\n' % msg)
2675 ui.write('\n%s\n' % msg)
2676 except KeyError:
2676 except KeyError:
2677 pass
2677 pass
2678
2678
2679 def helplist(header, select=None):
2679 def helplist(header, select=None):
2680 h = {}
2680 h = {}
2681 cmds = {}
2681 cmds = {}
2682 for c, e in table.iteritems():
2682 for c, e in table.iteritems():
2683 f = c.split("|", 1)[0]
2683 f = c.split("|", 1)[0]
2684 if select and not select(f):
2684 if select and not select(f):
2685 continue
2685 continue
2686 if (not select and name != 'shortlist' and
2686 if (not select and name != 'shortlist' and
2687 e[0].__module__ != __name__):
2687 e[0].__module__ != __name__):
2688 continue
2688 continue
2689 if name == "shortlist" and not f.startswith("^"):
2689 if name == "shortlist" and not f.startswith("^"):
2690 continue
2690 continue
2691 f = f.lstrip("^")
2691 f = f.lstrip("^")
2692 if not ui.debugflag and f.startswith("debug"):
2692 if not ui.debugflag and f.startswith("debug"):
2693 continue
2693 continue
2694 doc = e[0].__doc__
2694 doc = e[0].__doc__
2695 if doc and 'DEPRECATED' in doc and not ui.verbose:
2695 if doc and 'DEPRECATED' in doc and not ui.verbose:
2696 continue
2696 continue
2697 doc = gettext(doc)
2697 doc = gettext(doc)
2698 if not doc:
2698 if not doc:
2699 doc = _("(no help text available)")
2699 doc = _("(no help text available)")
2700 h[f] = doc.splitlines()[0].rstrip()
2700 h[f] = doc.splitlines()[0].rstrip()
2701 cmds[f] = c.lstrip("^")
2701 cmds[f] = c.lstrip("^")
2702
2702
2703 if not h:
2703 if not h:
2704 ui.status(_('no commands defined\n'))
2704 ui.status(_('no commands defined\n'))
2705 return
2705 return
2706
2706
2707 ui.status(header)
2707 ui.status(header)
2708 fns = sorted(h)
2708 fns = sorted(h)
2709 m = max(map(len, fns))
2709 m = max(map(len, fns))
2710 for f in fns:
2710 for f in fns:
2711 if ui.verbose:
2711 if ui.verbose:
2712 commands = cmds[f].replace("|",", ")
2712 commands = cmds[f].replace("|",", ")
2713 ui.write(" %s:\n %s\n"%(commands, h[f]))
2713 ui.write(" %s:\n %s\n"%(commands, h[f]))
2714 else:
2714 else:
2715 ui.write('%s\n' % (util.wrap(h[f], textwidth,
2715 ui.write('%s\n' % (util.wrap(h[f], textwidth,
2716 initindent=' %-*s ' % (m, f),
2716 initindent=' %-*s ' % (m, f),
2717 hangindent=' ' * (m + 4))))
2717 hangindent=' ' * (m + 4))))
2718
2718
2719 if not ui.quiet:
2719 if not ui.quiet:
2720 addglobalopts(True)
2720 addglobalopts(True)
2721
2721
2722 def helptopic(name):
2722 def helptopic(name):
2723 for names, header, doc in help.helptable:
2723 for names, header, doc in help.helptable:
2724 if name in names:
2724 if name in names:
2725 break
2725 break
2726 else:
2726 else:
2727 raise error.UnknownCommand(name)
2727 raise error.UnknownCommand(name)
2728
2728
2729 # description
2729 # description
2730 if not doc:
2730 if not doc:
2731 doc = _("(no help text available)")
2731 doc = _("(no help text available)")
2732 if hasattr(doc, '__call__'):
2732 if hasattr(doc, '__call__'):
2733 doc = doc()
2733 doc = doc()
2734
2734
2735 ui.write("%s\n\n" % header)
2735 ui.write("%s\n\n" % header)
2736 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
2736 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
2737 try:
2737 try:
2738 cmdutil.findcmd(name, table)
2738 cmdutil.findcmd(name, table)
2739 ui.write(_('\nuse "hg help -c %s" to see help for '
2739 ui.write(_('\nuse "hg help -c %s" to see help for '
2740 'the %s command\n') % (name, name))
2740 'the %s command\n') % (name, name))
2741 except error.UnknownCommand:
2741 except error.UnknownCommand:
2742 pass
2742 pass
2743
2743
2744 def helpext(name):
2744 def helpext(name):
2745 try:
2745 try:
2746 mod = extensions.find(name)
2746 mod = extensions.find(name)
2747 doc = gettext(mod.__doc__) or _('no help text available')
2747 doc = gettext(mod.__doc__) or _('no help text available')
2748 except KeyError:
2748 except KeyError:
2749 mod = None
2749 mod = None
2750 doc = extensions.disabledext(name)
2750 doc = extensions.disabledext(name)
2751 if not doc:
2751 if not doc:
2752 raise error.UnknownCommand(name)
2752 raise error.UnknownCommand(name)
2753
2753
2754 if '\n' not in doc:
2754 if '\n' not in doc:
2755 head, tail = doc, ""
2755 head, tail = doc, ""
2756 else:
2756 else:
2757 head, tail = doc.split('\n', 1)
2757 head, tail = doc.split('\n', 1)
2758 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
2758 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
2759 if tail:
2759 if tail:
2760 ui.write(minirst.format(tail, textwidth))
2760 ui.write(minirst.format(tail, textwidth))
2761 ui.status('\n\n')
2761 ui.status('\n\n')
2762
2762
2763 if mod:
2763 if mod:
2764 try:
2764 try:
2765 ct = mod.cmdtable
2765 ct = mod.cmdtable
2766 except AttributeError:
2766 except AttributeError:
2767 ct = {}
2767 ct = {}
2768 modcmds = set([c.split('|', 1)[0] for c in ct])
2768 modcmds = set([c.split('|', 1)[0] for c in ct])
2769 helplist(_('list of commands:\n\n'), modcmds.__contains__)
2769 helplist(_('list of commands:\n\n'), modcmds.__contains__)
2770 else:
2770 else:
2771 ui.write(_('use "hg help extensions" for information on enabling '
2771 ui.write(_('use "hg help extensions" for information on enabling '
2772 'extensions\n'))
2772 'extensions\n'))
2773
2773
2774 def helpextcmd(name):
2774 def helpextcmd(name):
2775 cmd, ext, mod = extensions.disabledcmd(ui, name, ui.config('ui', 'strict'))
2775 cmd, ext, mod = extensions.disabledcmd(ui, name, ui.config('ui', 'strict'))
2776 doc = gettext(mod.__doc__).splitlines()[0]
2776 doc = gettext(mod.__doc__).splitlines()[0]
2777
2777
2778 msg = help.listexts(_("'%s' is provided by the following "
2778 msg = help.listexts(_("'%s' is provided by the following "
2779 "extension:") % cmd, {ext: doc}, indent=4)
2779 "extension:") % cmd, {ext: doc}, indent=4)
2780 ui.write(minirst.format(msg, textwidth))
2780 ui.write(minirst.format(msg, textwidth))
2781 ui.write('\n\n')
2781 ui.write('\n\n')
2782 ui.write(_('use "hg help extensions" for information on enabling '
2782 ui.write(_('use "hg help extensions" for information on enabling '
2783 'extensions\n'))
2783 'extensions\n'))
2784
2784
2785 if name and name != 'shortlist':
2785 if name and name != 'shortlist':
2786 i = None
2786 i = None
2787 if unknowncmd:
2787 if unknowncmd:
2788 queries = (helpextcmd,)
2788 queries = (helpextcmd,)
2789 elif opts.get('extension'):
2789 elif opts.get('extension'):
2790 queries = (helpext,)
2790 queries = (helpext,)
2791 elif opts.get('command'):
2791 elif opts.get('command'):
2792 queries = (helpcmd,)
2792 queries = (helpcmd,)
2793 else:
2793 else:
2794 queries = (helptopic, helpcmd, helpext, helpextcmd)
2794 queries = (helptopic, helpcmd, helpext, helpextcmd)
2795 for f in queries:
2795 for f in queries:
2796 try:
2796 try:
2797 f(name)
2797 f(name)
2798 i = None
2798 i = None
2799 break
2799 break
2800 except error.UnknownCommand, inst:
2800 except error.UnknownCommand, inst:
2801 i = inst
2801 i = inst
2802 if i:
2802 if i:
2803 raise i
2803 raise i
2804
2804
2805 else:
2805 else:
2806 # program name
2806 # program name
2807 if ui.verbose or with_version:
2807 if ui.verbose or with_version:
2808 version_(ui)
2808 version_(ui)
2809 else:
2809 else:
2810 ui.status(_("Mercurial Distributed SCM\n"))
2810 ui.status(_("Mercurial Distributed SCM\n"))
2811 ui.status('\n')
2811 ui.status('\n')
2812
2812
2813 # list of commands
2813 # list of commands
2814 if name == "shortlist":
2814 if name == "shortlist":
2815 header = _('basic commands:\n\n')
2815 header = _('basic commands:\n\n')
2816 else:
2816 else:
2817 header = _('list of commands:\n\n')
2817 header = _('list of commands:\n\n')
2818
2818
2819 helplist(header)
2819 helplist(header)
2820 if name != 'shortlist':
2820 if name != 'shortlist':
2821 text = help.listexts(_('enabled extensions:'), extensions.enabled())
2821 text = help.listexts(_('enabled extensions:'), extensions.enabled())
2822 if text:
2822 if text:
2823 ui.write("\n%s\n" % minirst.format(text, textwidth))
2823 ui.write("\n%s\n" % minirst.format(text, textwidth))
2824
2824
2825 # list all option lists
2825 # list all option lists
2826 opt_output = []
2826 opt_output = []
2827 multioccur = False
2827 multioccur = False
2828 for title, options in option_lists:
2828 for title, options in option_lists:
2829 opt_output.append(("\n%s" % title, None))
2829 opt_output.append(("\n%s" % title, None))
2830 for option in options:
2830 for option in options:
2831 if len(option) == 5:
2831 if len(option) == 5:
2832 shortopt, longopt, default, desc, optlabel = option
2832 shortopt, longopt, default, desc, optlabel = option
2833 else:
2833 else:
2834 shortopt, longopt, default, desc = option
2834 shortopt, longopt, default, desc = option
2835 optlabel = _("VALUE") # default label
2835 optlabel = _("VALUE") # default label
2836
2836
2837 if _("DEPRECATED") in desc and not ui.verbose:
2837 if _("DEPRECATED") in desc and not ui.verbose:
2838 continue
2838 continue
2839 if isinstance(default, list):
2839 if isinstance(default, list):
2840 numqualifier = " %s [+]" % optlabel
2840 numqualifier = " %s [+]" % optlabel
2841 multioccur = True
2841 multioccur = True
2842 elif (default is not None) and not isinstance(default, bool):
2842 elif (default is not None) and not isinstance(default, bool):
2843 numqualifier = " %s" % optlabel
2843 numqualifier = " %s" % optlabel
2844 else:
2844 else:
2845 numqualifier = ""
2845 numqualifier = ""
2846 opt_output.append(("%2s%s" %
2846 opt_output.append(("%2s%s" %
2847 (shortopt and "-%s" % shortopt,
2847 (shortopt and "-%s" % shortopt,
2848 longopt and " --%s%s" %
2848 longopt and " --%s%s" %
2849 (longopt, numqualifier)),
2849 (longopt, numqualifier)),
2850 "%s%s" % (desc,
2850 "%s%s" % (desc,
2851 default
2851 default
2852 and _(" (default: %s)") % default
2852 and _(" (default: %s)") % default
2853 or "")))
2853 or "")))
2854 if multioccur:
2854 if multioccur:
2855 msg = _("\n[+] marked option can be specified multiple times")
2855 msg = _("\n[+] marked option can be specified multiple times")
2856 if ui.verbose and name != 'shortlist':
2856 if ui.verbose and name != 'shortlist':
2857 opt_output.append((msg, None))
2857 opt_output.append((msg, None))
2858 else:
2858 else:
2859 opt_output.insert(-1, (msg, None))
2859 opt_output.insert(-1, (msg, None))
2860
2860
2861 if not name:
2861 if not name:
2862 ui.write(_("\nadditional help topics:\n\n"))
2862 ui.write(_("\nadditional help topics:\n\n"))
2863 topics = []
2863 topics = []
2864 for names, header, doc in help.helptable:
2864 for names, header, doc in help.helptable:
2865 topics.append((sorted(names, key=len, reverse=True)[0], header))
2865 topics.append((sorted(names, key=len, reverse=True)[0], header))
2866 topics_len = max([len(s[0]) for s in topics])
2866 topics_len = max([len(s[0]) for s in topics])
2867 for t, desc in topics:
2867 for t, desc in topics:
2868 ui.write(" %-*s %s\n" % (topics_len, t, desc))
2868 ui.write(" %-*s %s\n" % (topics_len, t, desc))
2869
2869
2870 if opt_output:
2870 if opt_output:
2871 colwidth = encoding.colwidth
2871 colwidth = encoding.colwidth
2872 # normalize: (opt or message, desc or None, width of opt)
2872 # normalize: (opt or message, desc or None, width of opt)
2873 entries = [desc and (opt, desc, colwidth(opt)) or (opt, None, 0)
2873 entries = [desc and (opt, desc, colwidth(opt)) or (opt, None, 0)
2874 for opt, desc in opt_output]
2874 for opt, desc in opt_output]
2875 hanging = max([e[2] for e in entries])
2875 hanging = max([e[2] for e in entries])
2876 for opt, desc, width in entries:
2876 for opt, desc, width in entries:
2877 if desc:
2877 if desc:
2878 initindent = ' %s%s ' % (opt, ' ' * (hanging - width))
2878 initindent = ' %s%s ' % (opt, ' ' * (hanging - width))
2879 hangindent = ' ' * (hanging + 3)
2879 hangindent = ' ' * (hanging + 3)
2880 ui.write('%s\n' % (util.wrap(desc, textwidth,
2880 ui.write('%s\n' % (util.wrap(desc, textwidth,
2881 initindent=initindent,
2881 initindent=initindent,
2882 hangindent=hangindent)))
2882 hangindent=hangindent)))
2883 else:
2883 else:
2884 ui.write("%s\n" % opt)
2884 ui.write("%s\n" % opt)
2885
2885
2886 @command('identify|id',
2886 @command('identify|id',
2887 [('r', 'rev', '',
2887 [('r', 'rev', '',
2888 _('identify the specified revision'), _('REV')),
2888 _('identify the specified revision'), _('REV')),
2889 ('n', 'num', None, _('show local revision number')),
2889 ('n', 'num', None, _('show local revision number')),
2890 ('i', 'id', None, _('show global revision id')),
2890 ('i', 'id', None, _('show global revision id')),
2891 ('b', 'branch', None, _('show branch')),
2891 ('b', 'branch', None, _('show branch')),
2892 ('t', 'tags', None, _('show tags')),
2892 ('t', 'tags', None, _('show tags')),
2893 ('B', 'bookmarks', None, _('show bookmarks'))],
2893 ('B', 'bookmarks', None, _('show bookmarks'))],
2894 _('[-nibtB] [-r REV] [SOURCE]'))
2894 _('[-nibtB] [-r REV] [SOURCE]'))
2895 def identify(ui, repo, source=None, rev=None,
2895 def identify(ui, repo, source=None, rev=None,
2896 num=None, id=None, branch=None, tags=None, bookmarks=None):
2896 num=None, id=None, branch=None, tags=None, bookmarks=None):
2897 """identify the working copy or specified revision
2897 """identify the working copy or specified revision
2898
2898
2899 Print a summary identifying the repository state at REV using one or
2899 Print a summary identifying the repository state at REV using one or
2900 two parent hash identifiers, followed by a "+" if the working
2900 two parent hash identifiers, followed by a "+" if the working
2901 directory has uncommitted changes, the branch name (if not default),
2901 directory has uncommitted changes, the branch name (if not default),
2902 a list of tags, and a list of bookmarks.
2902 a list of tags, and a list of bookmarks.
2903
2903
2904 When REV is not given, print a summary of the current state of the
2904 When REV is not given, print a summary of the current state of the
2905 repository.
2905 repository.
2906
2906
2907 Specifying a path to a repository root or Mercurial bundle will
2907 Specifying a path to a repository root or Mercurial bundle will
2908 cause lookup to operate on that repository/bundle.
2908 cause lookup to operate on that repository/bundle.
2909
2909
2910 Returns 0 if successful.
2910 Returns 0 if successful.
2911 """
2911 """
2912
2912
2913 if not repo and not source:
2913 if not repo and not source:
2914 raise util.Abort(_("there is no Mercurial repository here "
2914 raise util.Abort(_("there is no Mercurial repository here "
2915 "(.hg not found)"))
2915 "(.hg not found)"))
2916
2916
2917 hexfunc = ui.debugflag and hex or short
2917 hexfunc = ui.debugflag and hex or short
2918 default = not (num or id or branch or tags or bookmarks)
2918 default = not (num or id or branch or tags or bookmarks)
2919 output = []
2919 output = []
2920 revs = []
2920 revs = []
2921
2921
2922 if source:
2922 if source:
2923 source, branches = hg.parseurl(ui.expandpath(source))
2923 source, branches = hg.parseurl(ui.expandpath(source))
2924 repo = hg.peer(ui, {}, source)
2924 repo = hg.peer(ui, {}, source)
2925 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
2925 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
2926
2926
2927 if not repo.local():
2927 if not repo.local():
2928 if num or branch or tags:
2928 if num or branch or tags:
2929 raise util.Abort(
2929 raise util.Abort(
2930 _("can't query remote revision number, branch, or tags"))
2930 _("can't query remote revision number, branch, or tags"))
2931 if not rev and revs:
2931 if not rev and revs:
2932 rev = revs[0]
2932 rev = revs[0]
2933 if not rev:
2933 if not rev:
2934 rev = "tip"
2934 rev = "tip"
2935
2935
2936 remoterev = repo.lookup(rev)
2936 remoterev = repo.lookup(rev)
2937 if default or id:
2937 if default or id:
2938 output = [hexfunc(remoterev)]
2938 output = [hexfunc(remoterev)]
2939
2939
2940 def getbms():
2940 def getbms():
2941 bms = []
2941 bms = []
2942
2942
2943 if 'bookmarks' in repo.listkeys('namespaces'):
2943 if 'bookmarks' in repo.listkeys('namespaces'):
2944 hexremoterev = hex(remoterev)
2944 hexremoterev = hex(remoterev)
2945 bms = [bm for bm, bmr in repo.listkeys('bookmarks').iteritems()
2945 bms = [bm for bm, bmr in repo.listkeys('bookmarks').iteritems()
2946 if bmr == hexremoterev]
2946 if bmr == hexremoterev]
2947
2947
2948 return bms
2948 return bms
2949
2949
2950 if bookmarks:
2950 if bookmarks:
2951 output.extend(getbms())
2951 output.extend(getbms())
2952 elif default and not ui.quiet:
2952 elif default and not ui.quiet:
2953 # multiple bookmarks for a single parent separated by '/'
2953 # multiple bookmarks for a single parent separated by '/'
2954 bm = '/'.join(getbms())
2954 bm = '/'.join(getbms())
2955 if bm:
2955 if bm:
2956 output.append(bm)
2956 output.append(bm)
2957 else:
2957 else:
2958 if not rev:
2958 if not rev:
2959 ctx = repo[None]
2959 ctx = repo[None]
2960 parents = ctx.parents()
2960 parents = ctx.parents()
2961 changed = ""
2961 changed = ""
2962 if default or id or num:
2962 if default or id or num:
2963 changed = util.any(repo.status()) and "+" or ""
2963 changed = util.any(repo.status()) and "+" or ""
2964 if default or id:
2964 if default or id:
2965 output = ["%s%s" %
2965 output = ["%s%s" %
2966 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
2966 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
2967 if num:
2967 if num:
2968 output.append("%s%s" %
2968 output.append("%s%s" %
2969 ('+'.join([str(p.rev()) for p in parents]), changed))
2969 ('+'.join([str(p.rev()) for p in parents]), changed))
2970 else:
2970 else:
2971 ctx = scmutil.revsingle(repo, rev)
2971 ctx = scmutil.revsingle(repo, rev)
2972 if default or id:
2972 if default or id:
2973 output = [hexfunc(ctx.node())]
2973 output = [hexfunc(ctx.node())]
2974 if num:
2974 if num:
2975 output.append(str(ctx.rev()))
2975 output.append(str(ctx.rev()))
2976
2976
2977 if default and not ui.quiet:
2977 if default and not ui.quiet:
2978 b = ctx.branch()
2978 b = ctx.branch()
2979 if b != 'default':
2979 if b != 'default':
2980 output.append("(%s)" % b)
2980 output.append("(%s)" % b)
2981
2981
2982 # multiple tags for a single parent separated by '/'
2982 # multiple tags for a single parent separated by '/'
2983 t = '/'.join(ctx.tags())
2983 t = '/'.join(ctx.tags())
2984 if t:
2984 if t:
2985 output.append(t)
2985 output.append(t)
2986
2986
2987 # multiple bookmarks for a single parent separated by '/'
2987 # multiple bookmarks for a single parent separated by '/'
2988 bm = '/'.join(ctx.bookmarks())
2988 bm = '/'.join(ctx.bookmarks())
2989 if bm:
2989 if bm:
2990 output.append(bm)
2990 output.append(bm)
2991 else:
2991 else:
2992 if branch:
2992 if branch:
2993 output.append(ctx.branch())
2993 output.append(ctx.branch())
2994
2994
2995 if tags:
2995 if tags:
2996 output.extend(ctx.tags())
2996 output.extend(ctx.tags())
2997
2997
2998 if bookmarks:
2998 if bookmarks:
2999 output.extend(ctx.bookmarks())
2999 output.extend(ctx.bookmarks())
3000
3000
3001 ui.write("%s\n" % ' '.join(output))
3001 ui.write("%s\n" % ' '.join(output))
3002
3002
3003 @command('import|patch',
3003 @command('import|patch',
3004 [('p', 'strip', 1,
3004 [('p', 'strip', 1,
3005 _('directory strip option for patch. This has the same '
3005 _('directory strip option for patch. This has the same '
3006 'meaning as the corresponding patch option'), _('NUM')),
3006 'meaning as the corresponding patch option'), _('NUM')),
3007 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3007 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3008 ('f', 'force', None, _('skip check for outstanding uncommitted changes')),
3008 ('f', 'force', None, _('skip check for outstanding uncommitted changes')),
3009 ('', 'no-commit', None,
3009 ('', 'no-commit', None,
3010 _("don't commit, just update the working directory")),
3010 _("don't commit, just update the working directory")),
3011 ('', 'bypass', None,
3011 ('', 'bypass', None,
3012 _("apply patch without touching the working directory")),
3012 _("apply patch without touching the working directory")),
3013 ('', 'exact', None,
3013 ('', 'exact', None,
3014 _('apply patch to the nodes from which it was generated')),
3014 _('apply patch to the nodes from which it was generated')),
3015 ('', 'import-branch', None,
3015 ('', 'import-branch', None,
3016 _('use any branch information in patch (implied by --exact)'))] +
3016 _('use any branch information in patch (implied by --exact)'))] +
3017 commitopts + commitopts2 + similarityopts,
3017 commitopts + commitopts2 + similarityopts,
3018 _('[OPTION]... PATCH...'))
3018 _('[OPTION]... PATCH...'))
3019 def import_(ui, repo, patch1, *patches, **opts):
3019 def import_(ui, repo, patch1, *patches, **opts):
3020 """import an ordered set of patches
3020 """import an ordered set of patches
3021
3021
3022 Import a list of patches and commit them individually (unless
3022 Import a list of patches and commit them individually (unless
3023 --no-commit is specified).
3023 --no-commit is specified).
3024
3024
3025 If there are outstanding changes in the working directory, import
3025 If there are outstanding changes in the working directory, import
3026 will abort unless given the -f/--force flag.
3026 will abort unless given the -f/--force flag.
3027
3027
3028 You can import a patch straight from a mail message. Even patches
3028 You can import a patch straight from a mail message. Even patches
3029 as attachments work (to use the body part, it must have type
3029 as attachments work (to use the body part, it must have type
3030 text/plain or text/x-patch). From and Subject headers of email
3030 text/plain or text/x-patch). From and Subject headers of email
3031 message are used as default committer and commit message. All
3031 message are used as default committer and commit message. All
3032 text/plain body parts before first diff are added to commit
3032 text/plain body parts before first diff are added to commit
3033 message.
3033 message.
3034
3034
3035 If the imported patch was generated by :hg:`export`, user and
3035 If the imported patch was generated by :hg:`export`, user and
3036 description from patch override values from message headers and
3036 description from patch override values from message headers and
3037 body. Values given on command line with -m/--message and -u/--user
3037 body. Values given on command line with -m/--message and -u/--user
3038 override these.
3038 override these.
3039
3039
3040 If --exact is specified, import will set the working directory to
3040 If --exact is specified, import will set the working directory to
3041 the parent of each patch before applying it, and will abort if the
3041 the parent of each patch before applying it, and will abort if the
3042 resulting changeset has a different ID than the one recorded in
3042 resulting changeset has a different ID than the one recorded in
3043 the patch. This may happen due to character set problems or other
3043 the patch. This may happen due to character set problems or other
3044 deficiencies in the text patch format.
3044 deficiencies in the text patch format.
3045
3045
3046 Use --bypass to apply and commit patches directly to the
3046 Use --bypass to apply and commit patches directly to the
3047 repository, not touching the working directory. Without --exact,
3047 repository, not touching the working directory. Without --exact,
3048 patches will be applied on top of the working directory parent
3048 patches will be applied on top of the working directory parent
3049 revision.
3049 revision.
3050
3050
3051 With -s/--similarity, hg will attempt to discover renames and
3051 With -s/--similarity, hg will attempt to discover renames and
3052 copies in the patch in the same way as 'addremove'.
3052 copies in the patch in the same way as 'addremove'.
3053
3053
3054 To read a patch from standard input, use "-" as the patch name. If
3054 To read a patch from standard input, use "-" as the patch name. If
3055 a URL is specified, the patch will be downloaded from it.
3055 a URL is specified, the patch will be downloaded from it.
3056 See :hg:`help dates` for a list of formats valid for -d/--date.
3056 See :hg:`help dates` for a list of formats valid for -d/--date.
3057
3057
3058 Returns 0 on success.
3058 Returns 0 on success.
3059 """
3059 """
3060 patches = (patch1,) + patches
3060 patches = (patch1,) + patches
3061
3061
3062 date = opts.get('date')
3062 date = opts.get('date')
3063 if date:
3063 if date:
3064 opts['date'] = util.parsedate(date)
3064 opts['date'] = util.parsedate(date)
3065
3065
3066 update = not opts.get('bypass')
3066 update = not opts.get('bypass')
3067 if not update and opts.get('no_commit'):
3067 if not update and opts.get('no_commit'):
3068 raise util.Abort(_('cannot use --no-commit with --bypass'))
3068 raise util.Abort(_('cannot use --no-commit with --bypass'))
3069 try:
3069 try:
3070 sim = float(opts.get('similarity') or 0)
3070 sim = float(opts.get('similarity') or 0)
3071 except ValueError:
3071 except ValueError:
3072 raise util.Abort(_('similarity must be a number'))
3072 raise util.Abort(_('similarity must be a number'))
3073 if sim < 0 or sim > 100:
3073 if sim < 0 or sim > 100:
3074 raise util.Abort(_('similarity must be between 0 and 100'))
3074 raise util.Abort(_('similarity must be between 0 and 100'))
3075 if sim and not update:
3075 if sim and not update:
3076 raise util.Abort(_('cannot use --similarity with --bypass'))
3076 raise util.Abort(_('cannot use --similarity with --bypass'))
3077
3077
3078 if (opts.get('exact') or not opts.get('force')) and update:
3078 if (opts.get('exact') or not opts.get('force')) and update:
3079 cmdutil.bailifchanged(repo)
3079 cmdutil.bailifchanged(repo)
3080
3080
3081 d = opts["base"]
3081 d = opts["base"]
3082 strip = opts["strip"]
3082 strip = opts["strip"]
3083 wlock = lock = None
3083 wlock = lock = None
3084 msgs = []
3084 msgs = []
3085
3085
3086 def checkexact(repo, n, nodeid):
3086 def checkexact(repo, n, nodeid):
3087 if opts.get('exact') and hex(n) != nodeid:
3087 if opts.get('exact') and hex(n) != nodeid:
3088 repo.rollback()
3088 repo.rollback()
3089 raise util.Abort(_('patch is damaged or loses information'))
3089 raise util.Abort(_('patch is damaged or loses information'))
3090
3090
3091 def tryone(ui, hunk, parents):
3091 def tryone(ui, hunk, parents):
3092 tmpname, message, user, date, branch, nodeid, p1, p2 = \
3092 tmpname, message, user, date, branch, nodeid, p1, p2 = \
3093 patch.extract(ui, hunk)
3093 patch.extract(ui, hunk)
3094
3094
3095 if not tmpname:
3095 if not tmpname:
3096 return None
3096 return None
3097 commitid = _('to working directory')
3097 commitid = _('to working directory')
3098
3098
3099 try:
3099 try:
3100 cmdline_message = cmdutil.logmessage(ui, opts)
3100 cmdline_message = cmdutil.logmessage(ui, opts)
3101 if cmdline_message:
3101 if cmdline_message:
3102 # pickup the cmdline msg
3102 # pickup the cmdline msg
3103 message = cmdline_message
3103 message = cmdline_message
3104 elif message:
3104 elif message:
3105 # pickup the patch msg
3105 # pickup the patch msg
3106 message = message.strip()
3106 message = message.strip()
3107 else:
3107 else:
3108 # launch the editor
3108 # launch the editor
3109 message = None
3109 message = None
3110 ui.debug('message:\n%s\n' % message)
3110 ui.debug('message:\n%s\n' % message)
3111
3111
3112 if len(parents) == 1:
3112 if len(parents) == 1:
3113 parents.append(repo[nullid])
3113 parents.append(repo[nullid])
3114 if opts.get('exact'):
3114 if opts.get('exact'):
3115 if not nodeid or not p1:
3115 if not nodeid or not p1:
3116 raise util.Abort(_('not a Mercurial patch'))
3116 raise util.Abort(_('not a Mercurial patch'))
3117 p1 = repo[p1]
3117 p1 = repo[p1]
3118 p2 = repo[p2 or nullid]
3118 p2 = repo[p2 or nullid]
3119 elif p2:
3119 elif p2:
3120 try:
3120 try:
3121 p1 = repo[p1]
3121 p1 = repo[p1]
3122 p2 = repo[p2]
3122 p2 = repo[p2]
3123 except error.RepoError:
3123 except error.RepoError:
3124 p1, p2 = parents
3124 p1, p2 = parents
3125 else:
3125 else:
3126 p1, p2 = parents
3126 p1, p2 = parents
3127
3127
3128 n = None
3128 n = None
3129 if update:
3129 if update:
3130 if opts.get('exact') and p1 != parents[0]:
3130 if opts.get('exact') and p1 != parents[0]:
3131 hg.clean(repo, p1.node())
3131 hg.clean(repo, p1.node())
3132 if p1 != parents[0] and p2 != parents[1]:
3132 if p1 != parents[0] and p2 != parents[1]:
3133 repo.dirstate.setparents(p1.node(), p2.node())
3133 repo.dirstate.setparents(p1.node(), p2.node())
3134
3134
3135 if opts.get('exact') or opts.get('import_branch'):
3135 if opts.get('exact') or opts.get('import_branch'):
3136 repo.dirstate.setbranch(branch or 'default')
3136 repo.dirstate.setbranch(branch or 'default')
3137
3137
3138 files = set()
3138 files = set()
3139 patch.patch(ui, repo, tmpname, strip=strip, files=files,
3139 patch.patch(ui, repo, tmpname, strip=strip, files=files,
3140 eolmode=None, similarity=sim / 100.0)
3140 eolmode=None, similarity=sim / 100.0)
3141 files = list(files)
3141 files = list(files)
3142 if opts.get('no_commit'):
3142 if opts.get('no_commit'):
3143 if message:
3143 if message:
3144 msgs.append(message)
3144 msgs.append(message)
3145 else:
3145 else:
3146 if opts.get('exact'):
3146 if opts.get('exact'):
3147 m = None
3147 m = None
3148 else:
3148 else:
3149 m = scmutil.matchfiles(repo, files or [])
3149 m = scmutil.matchfiles(repo, files or [])
3150 n = repo.commit(message, opts.get('user') or user,
3150 n = repo.commit(message, opts.get('user') or user,
3151 opts.get('date') or date, match=m,
3151 opts.get('date') or date, match=m,
3152 editor=cmdutil.commiteditor)
3152 editor=cmdutil.commiteditor)
3153 checkexact(repo, n, nodeid)
3153 checkexact(repo, n, nodeid)
3154 # Force a dirstate write so that the next transaction
3154 # Force a dirstate write so that the next transaction
3155 # backups an up-to-date file.
3155 # backups an up-to-date file.
3156 repo.dirstate.write()
3156 repo.dirstate.write()
3157 else:
3157 else:
3158 if opts.get('exact') or opts.get('import_branch'):
3158 if opts.get('exact') or opts.get('import_branch'):
3159 branch = branch or 'default'
3159 branch = branch or 'default'
3160 else:
3160 else:
3161 branch = p1.branch()
3161 branch = p1.branch()
3162 store = patch.filestore()
3162 store = patch.filestore()
3163 try:
3163 try:
3164 files = set()
3164 files = set()
3165 try:
3165 try:
3166 patch.patchrepo(ui, repo, p1, store, tmpname, strip,
3166 patch.patchrepo(ui, repo, p1, store, tmpname, strip,
3167 files, eolmode=None)
3167 files, eolmode=None)
3168 except patch.PatchError, e:
3168 except patch.PatchError, e:
3169 raise util.Abort(str(e))
3169 raise util.Abort(str(e))
3170 memctx = patch.makememctx(repo, (p1.node(), p2.node()),
3170 memctx = patch.makememctx(repo, (p1.node(), p2.node()),
3171 message,
3171 message,
3172 opts.get('user') or user,
3172 opts.get('user') or user,
3173 opts.get('date') or date,
3173 opts.get('date') or date,
3174 branch, files, store,
3174 branch, files, store,
3175 editor=cmdutil.commiteditor)
3175 editor=cmdutil.commiteditor)
3176 repo.savecommitmessage(memctx.description())
3176 repo.savecommitmessage(memctx.description())
3177 n = memctx.commit()
3177 n = memctx.commit()
3178 checkexact(repo, n, nodeid)
3178 checkexact(repo, n, nodeid)
3179 finally:
3179 finally:
3180 store.close()
3180 store.close()
3181 if n:
3181 if n:
3182 commitid = short(n)
3182 commitid = short(n)
3183 return commitid
3183 return commitid
3184 finally:
3184 finally:
3185 os.unlink(tmpname)
3185 os.unlink(tmpname)
3186
3186
3187 try:
3187 try:
3188 wlock = repo.wlock()
3188 wlock = repo.wlock()
3189 lock = repo.lock()
3189 lock = repo.lock()
3190 parents = repo.parents()
3190 parents = repo.parents()
3191 lastcommit = None
3191 lastcommit = None
3192 for p in patches:
3192 for p in patches:
3193 pf = os.path.join(d, p)
3193 pf = os.path.join(d, p)
3194
3194
3195 if pf == '-':
3195 if pf == '-':
3196 ui.status(_("applying patch from stdin\n"))
3196 ui.status(_("applying patch from stdin\n"))
3197 pf = ui.fin
3197 pf = ui.fin
3198 else:
3198 else:
3199 ui.status(_("applying %s\n") % p)
3199 ui.status(_("applying %s\n") % p)
3200 pf = url.open(ui, pf)
3200 pf = url.open(ui, pf)
3201
3201
3202 haspatch = False
3202 haspatch = False
3203 for hunk in patch.split(pf):
3203 for hunk in patch.split(pf):
3204 commitid = tryone(ui, hunk, parents)
3204 commitid = tryone(ui, hunk, parents)
3205 if commitid:
3205 if commitid:
3206 haspatch = True
3206 haspatch = True
3207 if lastcommit:
3207 if lastcommit:
3208 ui.status(_('applied %s\n') % lastcommit)
3208 ui.status(_('applied %s\n') % lastcommit)
3209 lastcommit = commitid
3209 lastcommit = commitid
3210 if update or opts.get('exact'):
3210 if update or opts.get('exact'):
3211 parents = repo.parents()
3211 parents = repo.parents()
3212 else:
3212 else:
3213 parents = [repo[commitid]]
3213 parents = [repo[commitid]]
3214
3214
3215 if not haspatch:
3215 if not haspatch:
3216 raise util.Abort(_('no diffs found'))
3216 raise util.Abort(_('no diffs found'))
3217
3217
3218 if msgs:
3218 if msgs:
3219 repo.savecommitmessage('\n* * *\n'.join(msgs))
3219 repo.savecommitmessage('\n* * *\n'.join(msgs))
3220 finally:
3220 finally:
3221 release(lock, wlock)
3221 release(lock, wlock)
3222
3222
3223 @command('incoming|in',
3223 @command('incoming|in',
3224 [('f', 'force', None,
3224 [('f', 'force', None,
3225 _('run even if remote repository is unrelated')),
3225 _('run even if remote repository is unrelated')),
3226 ('n', 'newest-first', None, _('show newest record first')),
3226 ('n', 'newest-first', None, _('show newest record first')),
3227 ('', 'bundle', '',
3227 ('', 'bundle', '',
3228 _('file to store the bundles into'), _('FILE')),
3228 _('file to store the bundles into'), _('FILE')),
3229 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3229 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3230 ('B', 'bookmarks', False, _("compare bookmarks")),
3230 ('B', 'bookmarks', False, _("compare bookmarks")),
3231 ('b', 'branch', [],
3231 ('b', 'branch', [],
3232 _('a specific branch you would like to pull'), _('BRANCH')),
3232 _('a specific branch you would like to pull'), _('BRANCH')),
3233 ] + logopts + remoteopts + subrepoopts,
3233 ] + logopts + remoteopts + subrepoopts,
3234 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3234 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3235 def incoming(ui, repo, source="default", **opts):
3235 def incoming(ui, repo, source="default", **opts):
3236 """show new changesets found in source
3236 """show new changesets found in source
3237
3237
3238 Show new changesets found in the specified path/URL or the default
3238 Show new changesets found in the specified path/URL or the default
3239 pull location. These are the changesets that would have been pulled
3239 pull location. These are the changesets that would have been pulled
3240 if a pull at the time you issued this command.
3240 if a pull at the time you issued this command.
3241
3241
3242 For remote repository, using --bundle avoids downloading the
3242 For remote repository, using --bundle avoids downloading the
3243 changesets twice if the incoming is followed by a pull.
3243 changesets twice if the incoming is followed by a pull.
3244
3244
3245 See pull for valid source format details.
3245 See pull for valid source format details.
3246
3246
3247 Returns 0 if there are incoming changes, 1 otherwise.
3247 Returns 0 if there are incoming changes, 1 otherwise.
3248 """
3248 """
3249 if opts.get('bundle') and opts.get('subrepos'):
3249 if opts.get('bundle') and opts.get('subrepos'):
3250 raise util.Abort(_('cannot combine --bundle and --subrepos'))
3250 raise util.Abort(_('cannot combine --bundle and --subrepos'))
3251
3251
3252 if opts.get('bookmarks'):
3252 if opts.get('bookmarks'):
3253 source, branches = hg.parseurl(ui.expandpath(source),
3253 source, branches = hg.parseurl(ui.expandpath(source),
3254 opts.get('branch'))
3254 opts.get('branch'))
3255 other = hg.peer(repo, opts, source)
3255 other = hg.peer(repo, opts, source)
3256 if 'bookmarks' not in other.listkeys('namespaces'):
3256 if 'bookmarks' not in other.listkeys('namespaces'):
3257 ui.warn(_("remote doesn't support bookmarks\n"))
3257 ui.warn(_("remote doesn't support bookmarks\n"))
3258 return 0
3258 return 0
3259 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3259 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3260 return bookmarks.diff(ui, repo, other)
3260 return bookmarks.diff(ui, repo, other)
3261
3261
3262 repo._subtoppath = ui.expandpath(source)
3262 repo._subtoppath = ui.expandpath(source)
3263 try:
3263 try:
3264 return hg.incoming(ui, repo, source, opts)
3264 return hg.incoming(ui, repo, source, opts)
3265 finally:
3265 finally:
3266 del repo._subtoppath
3266 del repo._subtoppath
3267
3267
3268
3268
3269 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'))
3269 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'))
3270 def init(ui, dest=".", **opts):
3270 def init(ui, dest=".", **opts):
3271 """create a new repository in the given directory
3271 """create a new repository in the given directory
3272
3272
3273 Initialize a new repository in the given directory. If the given
3273 Initialize a new repository in the given directory. If the given
3274 directory does not exist, it will be created.
3274 directory does not exist, it will be created.
3275
3275
3276 If no directory is given, the current directory is used.
3276 If no directory is given, the current directory is used.
3277
3277
3278 It is possible to specify an ``ssh://`` URL as the destination.
3278 It is possible to specify an ``ssh://`` URL as the destination.
3279 See :hg:`help urls` for more information.
3279 See :hg:`help urls` for more information.
3280
3280
3281 Returns 0 on success.
3281 Returns 0 on success.
3282 """
3282 """
3283 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3283 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3284
3284
3285 @command('locate',
3285 @command('locate',
3286 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3286 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3287 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3287 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3288 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3288 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3289 ] + walkopts,
3289 ] + walkopts,
3290 _('[OPTION]... [PATTERN]...'))
3290 _('[OPTION]... [PATTERN]...'))
3291 def locate(ui, repo, *pats, **opts):
3291 def locate(ui, repo, *pats, **opts):
3292 """locate files matching specific patterns
3292 """locate files matching specific patterns
3293
3293
3294 Print files under Mercurial control in the working directory whose
3294 Print files under Mercurial control in the working directory whose
3295 names match the given patterns.
3295 names match the given patterns.
3296
3296
3297 By default, this command searches all directories in the working
3297 By default, this command searches all directories in the working
3298 directory. To search just the current directory and its
3298 directory. To search just the current directory and its
3299 subdirectories, use "--include .".
3299 subdirectories, use "--include .".
3300
3300
3301 If no patterns are given to match, this command prints the names
3301 If no patterns are given to match, this command prints the names
3302 of all files under Mercurial control in the working directory.
3302 of all files under Mercurial control in the working directory.
3303
3303
3304 If you want to feed the output of this command into the "xargs"
3304 If you want to feed the output of this command into the "xargs"
3305 command, use the -0 option to both this command and "xargs". This
3305 command, use the -0 option to both this command and "xargs". This
3306 will avoid the problem of "xargs" treating single filenames that
3306 will avoid the problem of "xargs" treating single filenames that
3307 contain whitespace as multiple filenames.
3307 contain whitespace as multiple filenames.
3308
3308
3309 Returns 0 if a match is found, 1 otherwise.
3309 Returns 0 if a match is found, 1 otherwise.
3310 """
3310 """
3311 end = opts.get('print0') and '\0' or '\n'
3311 end = opts.get('print0') and '\0' or '\n'
3312 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3312 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3313
3313
3314 ret = 1
3314 ret = 1
3315 m = scmutil.match(repo[rev], pats, opts, default='relglob')
3315 m = scmutil.match(repo[rev], pats, opts, default='relglob')
3316 m.bad = lambda x, y: False
3316 m.bad = lambda x, y: False
3317 for abs in repo[rev].walk(m):
3317 for abs in repo[rev].walk(m):
3318 if not rev and abs not in repo.dirstate:
3318 if not rev and abs not in repo.dirstate:
3319 continue
3319 continue
3320 if opts.get('fullpath'):
3320 if opts.get('fullpath'):
3321 ui.write(repo.wjoin(abs), end)
3321 ui.write(repo.wjoin(abs), end)
3322 else:
3322 else:
3323 ui.write(((pats and m.rel(abs)) or abs), end)
3323 ui.write(((pats and m.rel(abs)) or abs), end)
3324 ret = 0
3324 ret = 0
3325
3325
3326 return ret
3326 return ret
3327
3327
3328 @command('^log|history',
3328 @command('^log|history',
3329 [('f', 'follow', None,
3329 [('f', 'follow', None,
3330 _('follow changeset history, or file history across copies and renames')),
3330 _('follow changeset history, or file history across copies and renames')),
3331 ('', 'follow-first', None,
3331 ('', 'follow-first', None,
3332 _('only follow the first parent of merge changesets')),
3332 _('only follow the first parent of merge changesets')),
3333 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3333 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3334 ('C', 'copies', None, _('show copied files')),
3334 ('C', 'copies', None, _('show copied files')),
3335 ('k', 'keyword', [],
3335 ('k', 'keyword', [],
3336 _('do case-insensitive search for a given text'), _('TEXT')),
3336 _('do case-insensitive search for a given text'), _('TEXT')),
3337 ('r', 'rev', [], _('show the specified revision or range'), _('REV')),
3337 ('r', 'rev', [], _('show the specified revision or range'), _('REV')),
3338 ('', 'removed', None, _('include revisions where files were removed')),
3338 ('', 'removed', None, _('include revisions where files were removed')),
3339 ('m', 'only-merges', None, _('show only merges')),
3339 ('m', 'only-merges', None, _('show only merges')),
3340 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3340 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3341 ('', 'only-branch', [],
3341 ('', 'only-branch', [],
3342 _('show only changesets within the given named branch (DEPRECATED)'),
3342 _('show only changesets within the given named branch (DEPRECATED)'),
3343 _('BRANCH')),
3343 _('BRANCH')),
3344 ('b', 'branch', [],
3344 ('b', 'branch', [],
3345 _('show changesets within the given named branch'), _('BRANCH')),
3345 _('show changesets within the given named branch'), _('BRANCH')),
3346 ('P', 'prune', [],
3346 ('P', 'prune', [],
3347 _('do not display revision or any of its ancestors'), _('REV')),
3347 _('do not display revision or any of its ancestors'), _('REV')),
3348 ('h', 'hidden', False, _('show hidden changesets')),
3348 ('h', 'hidden', False, _('show hidden changesets')),
3349 ] + logopts + walkopts,
3349 ] + logopts + walkopts,
3350 _('[OPTION]... [FILE]'))
3350 _('[OPTION]... [FILE]'))
3351 def log(ui, repo, *pats, **opts):
3351 def log(ui, repo, *pats, **opts):
3352 """show revision history of entire repository or files
3352 """show revision history of entire repository or files
3353
3353
3354 Print the revision history of the specified files or the entire
3354 Print the revision history of the specified files or the entire
3355 project.
3355 project.
3356
3356
3357 File history is shown without following rename or copy history of
3357 File history is shown without following rename or copy history of
3358 files. Use -f/--follow with a filename to follow history across
3358 files. Use -f/--follow with a filename to follow history across
3359 renames and copies. --follow without a filename will only show
3359 renames and copies. --follow without a filename will only show
3360 ancestors or descendants of the starting revision. --follow-first
3360 ancestors or descendants of the starting revision. --follow-first
3361 only follows the first parent of merge revisions.
3361 only follows the first parent of merge revisions.
3362
3362
3363 If no revision range is specified, the default is ``tip:0`` unless
3363 If no revision range is specified, the default is ``tip:0`` unless
3364 --follow is set, in which case the working directory parent is
3364 --follow is set, in which case the working directory parent is
3365 used as the starting revision. You can specify a revision set for
3365 used as the starting revision. You can specify a revision set for
3366 log, see :hg:`help revsets` for more information.
3366 log, see :hg:`help revsets` for more information.
3367
3367
3368 See :hg:`help dates` for a list of formats valid for -d/--date.
3368 See :hg:`help dates` for a list of formats valid for -d/--date.
3369
3369
3370 By default this command prints revision number and changeset id,
3370 By default this command prints revision number and changeset id,
3371 tags, non-trivial parents, user, date and time, and a summary for
3371 tags, non-trivial parents, user, date and time, and a summary for
3372 each commit. When the -v/--verbose switch is used, the list of
3372 each commit. When the -v/--verbose switch is used, the list of
3373 changed files and full commit message are shown.
3373 changed files and full commit message are shown.
3374
3374
3375 .. note::
3375 .. note::
3376 log -p/--patch may generate unexpected diff output for merge
3376 log -p/--patch may generate unexpected diff output for merge
3377 changesets, as it will only compare the merge changeset against
3377 changesets, as it will only compare the merge changeset against
3378 its first parent. Also, only files different from BOTH parents
3378 its first parent. Also, only files different from BOTH parents
3379 will appear in files:.
3379 will appear in files:.
3380
3380
3381 Returns 0 on success.
3381 Returns 0 on success.
3382 """
3382 """
3383
3383
3384 matchfn = scmutil.match(repo[None], pats, opts)
3384 matchfn = scmutil.match(repo[None], pats, opts)
3385 limit = cmdutil.loglimit(opts)
3385 limit = cmdutil.loglimit(opts)
3386 count = 0
3386 count = 0
3387
3387
3388 endrev = None
3388 endrev = None
3389 if opts.get('copies') and opts.get('rev'):
3389 if opts.get('copies') and opts.get('rev'):
3390 endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1
3390 endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1
3391
3391
3392 df = False
3392 df = False
3393 if opts["date"]:
3393 if opts["date"]:
3394 df = util.matchdate(opts["date"])
3394 df = util.matchdate(opts["date"])
3395
3395
3396 branches = opts.get('branch', []) + opts.get('only_branch', [])
3396 branches = opts.get('branch', []) + opts.get('only_branch', [])
3397 opts['branch'] = [repo.lookupbranch(b) for b in branches]
3397 opts['branch'] = [repo.lookupbranch(b) for b in branches]
3398
3398
3399 displayer = cmdutil.show_changeset(ui, repo, opts, True)
3399 displayer = cmdutil.show_changeset(ui, repo, opts, True)
3400 def prep(ctx, fns):
3400 def prep(ctx, fns):
3401 rev = ctx.rev()
3401 rev = ctx.rev()
3402 parents = [p for p in repo.changelog.parentrevs(rev)
3402 parents = [p for p in repo.changelog.parentrevs(rev)
3403 if p != nullrev]
3403 if p != nullrev]
3404 if opts.get('no_merges') and len(parents) == 2:
3404 if opts.get('no_merges') and len(parents) == 2:
3405 return
3405 return
3406 if opts.get('only_merges') and len(parents) != 2:
3406 if opts.get('only_merges') and len(parents) != 2:
3407 return
3407 return
3408 if opts.get('branch') and ctx.branch() not in opts['branch']:
3408 if opts.get('branch') and ctx.branch() not in opts['branch']:
3409 return
3409 return
3410 if not opts.get('hidden') and ctx.hidden():
3410 if not opts.get('hidden') and ctx.hidden():
3411 return
3411 return
3412 if df and not df(ctx.date()[0]):
3412 if df and not df(ctx.date()[0]):
3413 return
3413 return
3414 if opts['user'] and not [k for k in opts['user']
3414 if opts['user'] and not [k for k in opts['user']
3415 if k.lower() in ctx.user().lower()]:
3415 if k.lower() in ctx.user().lower()]:
3416 return
3416 return
3417 if opts.get('keyword'):
3417 if opts.get('keyword'):
3418 for k in [kw.lower() for kw in opts['keyword']]:
3418 for k in [kw.lower() for kw in opts['keyword']]:
3419 if (k in ctx.user().lower() or
3419 if (k in ctx.user().lower() or
3420 k in ctx.description().lower() or
3420 k in ctx.description().lower() or
3421 k in " ".join(ctx.files()).lower()):
3421 k in " ".join(ctx.files()).lower()):
3422 break
3422 break
3423 else:
3423 else:
3424 return
3424 return
3425
3425
3426 copies = None
3426 copies = None
3427 if opts.get('copies') and rev:
3427 if opts.get('copies') and rev:
3428 copies = []
3428 copies = []
3429 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3429 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3430 for fn in ctx.files():
3430 for fn in ctx.files():
3431 rename = getrenamed(fn, rev)
3431 rename = getrenamed(fn, rev)
3432 if rename:
3432 if rename:
3433 copies.append((fn, rename[0]))
3433 copies.append((fn, rename[0]))
3434
3434
3435 revmatchfn = None
3435 revmatchfn = None
3436 if opts.get('patch') or opts.get('stat'):
3436 if opts.get('patch') or opts.get('stat'):
3437 if opts.get('follow') or opts.get('follow_first'):
3437 if opts.get('follow') or opts.get('follow_first'):
3438 # note: this might be wrong when following through merges
3438 # note: this might be wrong when following through merges
3439 revmatchfn = scmutil.match(repo[None], fns, default='path')
3439 revmatchfn = scmutil.match(repo[None], fns, default='path')
3440 else:
3440 else:
3441 revmatchfn = matchfn
3441 revmatchfn = matchfn
3442
3442
3443 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
3443 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
3444
3444
3445 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3445 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3446 if count == limit:
3446 if count == limit:
3447 break
3447 break
3448 if displayer.flush(ctx.rev()):
3448 if displayer.flush(ctx.rev()):
3449 count += 1
3449 count += 1
3450 displayer.close()
3450 displayer.close()
3451
3451
3452 @command('manifest',
3452 @command('manifest',
3453 [('r', 'rev', '', _('revision to display'), _('REV')),
3453 [('r', 'rev', '', _('revision to display'), _('REV')),
3454 ('', 'all', False, _("list files from all revisions"))],
3454 ('', 'all', False, _("list files from all revisions"))],
3455 _('[-r REV]'))
3455 _('[-r REV]'))
3456 def manifest(ui, repo, node=None, rev=None, **opts):
3456 def manifest(ui, repo, node=None, rev=None, **opts):
3457 """output the current or given revision of the project manifest
3457 """output the current or given revision of the project manifest
3458
3458
3459 Print a list of version controlled files for the given revision.
3459 Print a list of version controlled files for the given revision.
3460 If no revision is given, the first parent of the working directory
3460 If no revision is given, the first parent of the working directory
3461 is used, or the null revision if no revision is checked out.
3461 is used, or the null revision if no revision is checked out.
3462
3462
3463 With -v, print file permissions, symlink and executable bits.
3463 With -v, print file permissions, symlink and executable bits.
3464 With --debug, print file revision hashes.
3464 With --debug, print file revision hashes.
3465
3465
3466 If option --all is specified, the list of all files from all revisions
3466 If option --all is specified, the list of all files from all revisions
3467 is printed. This includes deleted and renamed files.
3467 is printed. This includes deleted and renamed files.
3468
3468
3469 Returns 0 on success.
3469 Returns 0 on success.
3470 """
3470 """
3471 if opts.get('all'):
3471 if opts.get('all'):
3472 if rev or node:
3472 if rev or node:
3473 raise util.Abort(_("can't specify a revision with --all"))
3473 raise util.Abort(_("can't specify a revision with --all"))
3474
3474
3475 res = []
3475 res = []
3476 prefix = "data/"
3476 prefix = "data/"
3477 suffix = ".i"
3477 suffix = ".i"
3478 plen = len(prefix)
3478 plen = len(prefix)
3479 slen = len(suffix)
3479 slen = len(suffix)
3480 lock = repo.lock()
3480 lock = repo.lock()
3481 try:
3481 try:
3482 for fn, b, size in repo.store.datafiles():
3482 for fn, b, size in repo.store.datafiles():
3483 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
3483 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
3484 res.append(fn[plen:-slen])
3484 res.append(fn[plen:-slen])
3485 finally:
3485 finally:
3486 lock.release()
3486 lock.release()
3487 for f in sorted(res):
3487 for f in sorted(res):
3488 ui.write("%s\n" % f)
3488 ui.write("%s\n" % f)
3489 return
3489 return
3490
3490
3491 if rev and node:
3491 if rev and node:
3492 raise util.Abort(_("please specify just one revision"))
3492 raise util.Abort(_("please specify just one revision"))
3493
3493
3494 if not node:
3494 if not node:
3495 node = rev
3495 node = rev
3496
3496
3497 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
3497 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
3498 ctx = scmutil.revsingle(repo, node)
3498 ctx = scmutil.revsingle(repo, node)
3499 for f in ctx:
3499 for f in ctx:
3500 if ui.debugflag:
3500 if ui.debugflag:
3501 ui.write("%40s " % hex(ctx.manifest()[f]))
3501 ui.write("%40s " % hex(ctx.manifest()[f]))
3502 if ui.verbose:
3502 if ui.verbose:
3503 ui.write(decor[ctx.flags(f)])
3503 ui.write(decor[ctx.flags(f)])
3504 ui.write("%s\n" % f)
3504 ui.write("%s\n" % f)
3505
3505
3506 @command('^merge',
3506 @command('^merge',
3507 [('f', 'force', None, _('force a merge with outstanding changes')),
3507 [('f', 'force', None, _('force a merge with outstanding changes')),
3508 ('t', 'tool', '', _('specify merge tool')),
3508 ('t', 'tool', '', _('specify merge tool')),
3509 ('r', 'rev', '', _('revision to merge'), _('REV')),
3509 ('r', 'rev', '', _('revision to merge'), _('REV')),
3510 ('P', 'preview', None,
3510 ('P', 'preview', None,
3511 _('review revisions to merge (no merge is performed)'))],
3511 _('review revisions to merge (no merge is performed)'))],
3512 _('[-P] [-f] [[-r] REV]'))
3512 _('[-P] [-f] [[-r] REV]'))
3513 def merge(ui, repo, node=None, **opts):
3513 def merge(ui, repo, node=None, **opts):
3514 """merge working directory with another revision
3514 """merge working directory with another revision
3515
3515
3516 The current working directory is updated with all changes made in
3516 The current working directory is updated with all changes made in
3517 the requested revision since the last common predecessor revision.
3517 the requested revision since the last common predecessor revision.
3518
3518
3519 Files that changed between either parent are marked as changed for
3519 Files that changed between either parent are marked as changed for
3520 the next commit and a commit must be performed before any further
3520 the next commit and a commit must be performed before any further
3521 updates to the repository are allowed. The next commit will have
3521 updates to the repository are allowed. The next commit will have
3522 two parents.
3522 two parents.
3523
3523
3524 ``--tool`` can be used to specify the merge tool used for file
3524 ``--tool`` can be used to specify the merge tool used for file
3525 merges. It overrides the HGMERGE environment variable and your
3525 merges. It overrides the HGMERGE environment variable and your
3526 configuration files. See :hg:`help merge-tools` for options.
3526 configuration files. See :hg:`help merge-tools` for options.
3527
3527
3528 If no revision is specified, the working directory's parent is a
3528 If no revision is specified, the working directory's parent is a
3529 head revision, and the current branch contains exactly one other
3529 head revision, and the current branch contains exactly one other
3530 head, the other head is merged with by default. Otherwise, an
3530 head, the other head is merged with by default. Otherwise, an
3531 explicit revision with which to merge with must be provided.
3531 explicit revision with which to merge with must be provided.
3532
3532
3533 :hg:`resolve` must be used to resolve unresolved files.
3533 :hg:`resolve` must be used to resolve unresolved files.
3534
3534
3535 To undo an uncommitted merge, use :hg:`update --clean .` which
3535 To undo an uncommitted merge, use :hg:`update --clean .` which
3536 will check out a clean copy of the original merge parent, losing
3536 will check out a clean copy of the original merge parent, losing
3537 all changes.
3537 all changes.
3538
3538
3539 Returns 0 on success, 1 if there are unresolved files.
3539 Returns 0 on success, 1 if there are unresolved files.
3540 """
3540 """
3541
3541
3542 if opts.get('rev') and node:
3542 if opts.get('rev') and node:
3543 raise util.Abort(_("please specify just one revision"))
3543 raise util.Abort(_("please specify just one revision"))
3544 if not node:
3544 if not node:
3545 node = opts.get('rev')
3545 node = opts.get('rev')
3546
3546
3547 if not node:
3547 if not node:
3548 branch = repo[None].branch()
3548 branch = repo[None].branch()
3549 bheads = repo.branchheads(branch)
3549 bheads = repo.branchheads(branch)
3550 if len(bheads) > 2:
3550 if len(bheads) > 2:
3551 raise util.Abort(_("branch '%s' has %d heads - "
3551 raise util.Abort(_("branch '%s' has %d heads - "
3552 "please merge with an explicit rev")
3552 "please merge with an explicit rev")
3553 % (branch, len(bheads)),
3553 % (branch, len(bheads)),
3554 hint=_("run 'hg heads .' to see heads"))
3554 hint=_("run 'hg heads .' to see heads"))
3555
3555
3556 parent = repo.dirstate.p1()
3556 parent = repo.dirstate.p1()
3557 if len(bheads) == 1:
3557 if len(bheads) == 1:
3558 if len(repo.heads()) > 1:
3558 if len(repo.heads()) > 1:
3559 raise util.Abort(_("branch '%s' has one head - "
3559 raise util.Abort(_("branch '%s' has one head - "
3560 "please merge with an explicit rev")
3560 "please merge with an explicit rev")
3561 % branch,
3561 % branch,
3562 hint=_("run 'hg heads' to see all heads"))
3562 hint=_("run 'hg heads' to see all heads"))
3563 msg = _('there is nothing to merge')
3563 msg = _('there is nothing to merge')
3564 if parent != repo.lookup(repo[None].branch()):
3564 if parent != repo.lookup(repo[None].branch()):
3565 msg = _('%s - use "hg update" instead') % msg
3565 msg = _('%s - use "hg update" instead') % msg
3566 raise util.Abort(msg)
3566 raise util.Abort(msg)
3567
3567
3568 if parent not in bheads:
3568 if parent not in bheads:
3569 raise util.Abort(_('working directory not at a head revision'),
3569 raise util.Abort(_('working directory not at a head revision'),
3570 hint=_("use 'hg update' or merge with an "
3570 hint=_("use 'hg update' or merge with an "
3571 "explicit revision"))
3571 "explicit revision"))
3572 node = parent == bheads[0] and bheads[-1] or bheads[0]
3572 node = parent == bheads[0] and bheads[-1] or bheads[0]
3573 else:
3573 else:
3574 node = scmutil.revsingle(repo, node).node()
3574 node = scmutil.revsingle(repo, node).node()
3575
3575
3576 if opts.get('preview'):
3576 if opts.get('preview'):
3577 # find nodes that are ancestors of p2 but not of p1
3577 # find nodes that are ancestors of p2 but not of p1
3578 p1 = repo.lookup('.')
3578 p1 = repo.lookup('.')
3579 p2 = repo.lookup(node)
3579 p2 = repo.lookup(node)
3580 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
3580 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
3581
3581
3582 displayer = cmdutil.show_changeset(ui, repo, opts)
3582 displayer = cmdutil.show_changeset(ui, repo, opts)
3583 for node in nodes:
3583 for node in nodes:
3584 displayer.show(repo[node])
3584 displayer.show(repo[node])
3585 displayer.close()
3585 displayer.close()
3586 return 0
3586 return 0
3587
3587
3588 try:
3588 try:
3589 # ui.forcemerge is an internal variable, do not document
3589 # ui.forcemerge is an internal variable, do not document
3590 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
3590 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
3591 return hg.merge(repo, node, force=opts.get('force'))
3591 return hg.merge(repo, node, force=opts.get('force'))
3592 finally:
3592 finally:
3593 ui.setconfig('ui', 'forcemerge', '')
3593 ui.setconfig('ui', 'forcemerge', '')
3594
3594
3595 @command('outgoing|out',
3595 @command('outgoing|out',
3596 [('f', 'force', None, _('run even when the destination is unrelated')),
3596 [('f', 'force', None, _('run even when the destination is unrelated')),
3597 ('r', 'rev', [],
3597 ('r', 'rev', [],
3598 _('a changeset intended to be included in the destination'), _('REV')),
3598 _('a changeset intended to be included in the destination'), _('REV')),
3599 ('n', 'newest-first', None, _('show newest record first')),
3599 ('n', 'newest-first', None, _('show newest record first')),
3600 ('B', 'bookmarks', False, _('compare bookmarks')),
3600 ('B', 'bookmarks', False, _('compare bookmarks')),
3601 ('b', 'branch', [], _('a specific branch you would like to push'),
3601 ('b', 'branch', [], _('a specific branch you would like to push'),
3602 _('BRANCH')),
3602 _('BRANCH')),
3603 ] + logopts + remoteopts + subrepoopts,
3603 ] + logopts + remoteopts + subrepoopts,
3604 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
3604 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
3605 def outgoing(ui, repo, dest=None, **opts):
3605 def outgoing(ui, repo, dest=None, **opts):
3606 """show changesets not found in the destination
3606 """show changesets not found in the destination
3607
3607
3608 Show changesets not found in the specified destination repository
3608 Show changesets not found in the specified destination repository
3609 or the default push location. These are the changesets that would
3609 or the default push location. These are the changesets that would
3610 be pushed if a push was requested.
3610 be pushed if a push was requested.
3611
3611
3612 See pull for details of valid destination formats.
3612 See pull for details of valid destination formats.
3613
3613
3614 Returns 0 if there are outgoing changes, 1 otherwise.
3614 Returns 0 if there are outgoing changes, 1 otherwise.
3615 """
3615 """
3616
3616
3617 if opts.get('bookmarks'):
3617 if opts.get('bookmarks'):
3618 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3618 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3619 dest, branches = hg.parseurl(dest, opts.get('branch'))
3619 dest, branches = hg.parseurl(dest, opts.get('branch'))
3620 other = hg.peer(repo, opts, dest)
3620 other = hg.peer(repo, opts, dest)
3621 if 'bookmarks' not in other.listkeys('namespaces'):
3621 if 'bookmarks' not in other.listkeys('namespaces'):
3622 ui.warn(_("remote doesn't support bookmarks\n"))
3622 ui.warn(_("remote doesn't support bookmarks\n"))
3623 return 0
3623 return 0
3624 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
3624 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
3625 return bookmarks.diff(ui, other, repo)
3625 return bookmarks.diff(ui, other, repo)
3626
3626
3627 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
3627 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
3628 try:
3628 try:
3629 return hg.outgoing(ui, repo, dest, opts)
3629 return hg.outgoing(ui, repo, dest, opts)
3630 finally:
3630 finally:
3631 del repo._subtoppath
3631 del repo._subtoppath
3632
3632
3633 @command('parents',
3633 @command('parents',
3634 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
3634 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
3635 ] + templateopts,
3635 ] + templateopts,
3636 _('[-r REV] [FILE]'))
3636 _('[-r REV] [FILE]'))
3637 def parents(ui, repo, file_=None, **opts):
3637 def parents(ui, repo, file_=None, **opts):
3638 """show the parents of the working directory or revision
3638 """show the parents of the working directory or revision
3639
3639
3640 Print the working directory's parent revisions. If a revision is
3640 Print the working directory's parent revisions. If a revision is
3641 given via -r/--rev, the parent of that revision will be printed.
3641 given via -r/--rev, the parent of that revision will be printed.
3642 If a file argument is given, the revision in which the file was
3642 If a file argument is given, the revision in which the file was
3643 last changed (before the working directory revision or the
3643 last changed (before the working directory revision or the
3644 argument to --rev if given) is printed.
3644 argument to --rev if given) is printed.
3645
3645
3646 Returns 0 on success.
3646 Returns 0 on success.
3647 """
3647 """
3648
3648
3649 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3649 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3650
3650
3651 if file_:
3651 if file_:
3652 m = scmutil.match(ctx, (file_,), opts)
3652 m = scmutil.match(ctx, (file_,), opts)
3653 if m.anypats() or len(m.files()) != 1:
3653 if m.anypats() or len(m.files()) != 1:
3654 raise util.Abort(_('can only specify an explicit filename'))
3654 raise util.Abort(_('can only specify an explicit filename'))
3655 file_ = m.files()[0]
3655 file_ = m.files()[0]
3656 filenodes = []
3656 filenodes = []
3657 for cp in ctx.parents():
3657 for cp in ctx.parents():
3658 if not cp:
3658 if not cp:
3659 continue
3659 continue
3660 try:
3660 try:
3661 filenodes.append(cp.filenode(file_))
3661 filenodes.append(cp.filenode(file_))
3662 except error.LookupError:
3662 except error.LookupError:
3663 pass
3663 pass
3664 if not filenodes:
3664 if not filenodes:
3665 raise util.Abort(_("'%s' not found in manifest!") % file_)
3665 raise util.Abort(_("'%s' not found in manifest!") % file_)
3666 fl = repo.file(file_)
3666 fl = repo.file(file_)
3667 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
3667 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
3668 else:
3668 else:
3669 p = [cp.node() for cp in ctx.parents()]
3669 p = [cp.node() for cp in ctx.parents()]
3670
3670
3671 displayer = cmdutil.show_changeset(ui, repo, opts)
3671 displayer = cmdutil.show_changeset(ui, repo, opts)
3672 for n in p:
3672 for n in p:
3673 if n != nullid:
3673 if n != nullid:
3674 displayer.show(repo[n])
3674 displayer.show(repo[n])
3675 displayer.close()
3675 displayer.close()
3676
3676
3677 @command('paths', [], _('[NAME]'))
3677 @command('paths', [], _('[NAME]'))
3678 def paths(ui, repo, search=None):
3678 def paths(ui, repo, search=None):
3679 """show aliases for remote repositories
3679 """show aliases for remote repositories
3680
3680
3681 Show definition of symbolic path name NAME. If no name is given,
3681 Show definition of symbolic path name NAME. If no name is given,
3682 show definition of all available names.
3682 show definition of all available names.
3683
3683
3684 Option -q/--quiet suppresses all output when searching for NAME
3684 Option -q/--quiet suppresses all output when searching for NAME
3685 and shows only the path names when listing all definitions.
3685 and shows only the path names when listing all definitions.
3686
3686
3687 Path names are defined in the [paths] section of your
3687 Path names are defined in the [paths] section of your
3688 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
3688 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
3689 repository, ``.hg/hgrc`` is used, too.
3689 repository, ``.hg/hgrc`` is used, too.
3690
3690
3691 The path names ``default`` and ``default-push`` have a special
3691 The path names ``default`` and ``default-push`` have a special
3692 meaning. When performing a push or pull operation, they are used
3692 meaning. When performing a push or pull operation, they are used
3693 as fallbacks if no location is specified on the command-line.
3693 as fallbacks if no location is specified on the command-line.
3694 When ``default-push`` is set, it will be used for push and
3694 When ``default-push`` is set, it will be used for push and
3695 ``default`` will be used for pull; otherwise ``default`` is used
3695 ``default`` will be used for pull; otherwise ``default`` is used
3696 as the fallback for both. When cloning a repository, the clone
3696 as the fallback for both. When cloning a repository, the clone
3697 source is written as ``default`` in ``.hg/hgrc``. Note that
3697 source is written as ``default`` in ``.hg/hgrc``. Note that
3698 ``default`` and ``default-push`` apply to all inbound (e.g.
3698 ``default`` and ``default-push`` apply to all inbound (e.g.
3699 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
3699 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
3700 :hg:`bundle`) operations.
3700 :hg:`bundle`) operations.
3701
3701
3702 See :hg:`help urls` for more information.
3702 See :hg:`help urls` for more information.
3703
3703
3704 Returns 0 on success.
3704 Returns 0 on success.
3705 """
3705 """
3706 if search:
3706 if search:
3707 for name, path in ui.configitems("paths"):
3707 for name, path in ui.configitems("paths"):
3708 if name == search:
3708 if name == search:
3709 ui.status("%s\n" % util.hidepassword(path))
3709 ui.status("%s\n" % util.hidepassword(path))
3710 return
3710 return
3711 if not ui.quiet:
3711 if not ui.quiet:
3712 ui.warn(_("not found!\n"))
3712 ui.warn(_("not found!\n"))
3713 return 1
3713 return 1
3714 else:
3714 else:
3715 for name, path in ui.configitems("paths"):
3715 for name, path in ui.configitems("paths"):
3716 if ui.quiet:
3716 if ui.quiet:
3717 ui.write("%s\n" % name)
3717 ui.write("%s\n" % name)
3718 else:
3718 else:
3719 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
3719 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
3720
3720
3721 def postincoming(ui, repo, modheads, optupdate, checkout):
3721 def postincoming(ui, repo, modheads, optupdate, checkout):
3722 if modheads == 0:
3722 if modheads == 0:
3723 return
3723 return
3724 if optupdate:
3724 if optupdate:
3725 try:
3725 try:
3726 return hg.update(repo, checkout)
3726 return hg.update(repo, checkout)
3727 except util.Abort, inst:
3727 except util.Abort, inst:
3728 ui.warn(_("not updating: %s\n" % str(inst)))
3728 ui.warn(_("not updating: %s\n" % str(inst)))
3729 return 0
3729 return 0
3730 if modheads > 1:
3730 if modheads > 1:
3731 currentbranchheads = len(repo.branchheads())
3731 currentbranchheads = len(repo.branchheads())
3732 if currentbranchheads == modheads:
3732 if currentbranchheads == modheads:
3733 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
3733 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
3734 elif currentbranchheads > 1:
3734 elif currentbranchheads > 1:
3735 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to merge)\n"))
3735 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to merge)\n"))
3736 else:
3736 else:
3737 ui.status(_("(run 'hg heads' to see heads)\n"))
3737 ui.status(_("(run 'hg heads' to see heads)\n"))
3738 else:
3738 else:
3739 ui.status(_("(run 'hg update' to get a working copy)\n"))
3739 ui.status(_("(run 'hg update' to get a working copy)\n"))
3740
3740
3741 @command('^pull',
3741 @command('^pull',
3742 [('u', 'update', None,
3742 [('u', 'update', None,
3743 _('update to new branch head if changesets were pulled')),
3743 _('update to new branch head if changesets were pulled')),
3744 ('f', 'force', None, _('run even when remote repository is unrelated')),
3744 ('f', 'force', None, _('run even when remote repository is unrelated')),
3745 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3745 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3746 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
3746 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
3747 ('b', 'branch', [], _('a specific branch you would like to pull'),
3747 ('b', 'branch', [], _('a specific branch you would like to pull'),
3748 _('BRANCH')),
3748 _('BRANCH')),
3749 ] + remoteopts,
3749 ] + remoteopts,
3750 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
3750 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
3751 def pull(ui, repo, source="default", **opts):
3751 def pull(ui, repo, source="default", **opts):
3752 """pull changes from the specified source
3752 """pull changes from the specified source
3753
3753
3754 Pull changes from a remote repository to a local one.
3754 Pull changes from a remote repository to a local one.
3755
3755
3756 This finds all changes from the repository at the specified path
3756 This finds all changes from the repository at the specified path
3757 or URL and adds them to a local repository (the current one unless
3757 or URL and adds them to a local repository (the current one unless
3758 -R is specified). By default, this does not update the copy of the
3758 -R is specified). By default, this does not update the copy of the
3759 project in the working directory.
3759 project in the working directory.
3760
3760
3761 Use :hg:`incoming` if you want to see what would have been added
3761 Use :hg:`incoming` if you want to see what would have been added
3762 by a pull at the time you issued this command. If you then decide
3762 by a pull at the time you issued this command. If you then decide
3763 to add those changes to the repository, you should use :hg:`pull
3763 to add those changes to the repository, you should use :hg:`pull
3764 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
3764 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
3765
3765
3766 If SOURCE is omitted, the 'default' path will be used.
3766 If SOURCE is omitted, the 'default' path will be used.
3767 See :hg:`help urls` for more information.
3767 See :hg:`help urls` for more information.
3768
3768
3769 Returns 0 on success, 1 if an update had unresolved files.
3769 Returns 0 on success, 1 if an update had unresolved files.
3770 """
3770 """
3771 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
3771 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
3772 other = hg.peer(repo, opts, source)
3772 other = hg.peer(repo, opts, source)
3773 ui.status(_('pulling from %s\n') % util.hidepassword(source))
3773 ui.status(_('pulling from %s\n') % util.hidepassword(source))
3774 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3774 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3775
3775
3776 if opts.get('bookmark'):
3776 if opts.get('bookmark'):
3777 if not revs:
3777 if not revs:
3778 revs = []
3778 revs = []
3779 rb = other.listkeys('bookmarks')
3779 rb = other.listkeys('bookmarks')
3780 for b in opts['bookmark']:
3780 for b in opts['bookmark']:
3781 if b not in rb:
3781 if b not in rb:
3782 raise util.Abort(_('remote bookmark %s not found!') % b)
3782 raise util.Abort(_('remote bookmark %s not found!') % b)
3783 revs.append(rb[b])
3783 revs.append(rb[b])
3784
3784
3785 if revs:
3785 if revs:
3786 try:
3786 try:
3787 revs = [other.lookup(rev) for rev in revs]
3787 revs = [other.lookup(rev) for rev in revs]
3788 except error.CapabilityError:
3788 except error.CapabilityError:
3789 err = _("other repository doesn't support revision lookup, "
3789 err = _("other repository doesn't support revision lookup, "
3790 "so a rev cannot be specified.")
3790 "so a rev cannot be specified.")
3791 raise util.Abort(err)
3791 raise util.Abort(err)
3792
3792
3793 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
3793 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
3794 bookmarks.updatefromremote(ui, repo, other)
3794 bookmarks.updatefromremote(ui, repo, other)
3795 if checkout:
3795 if checkout:
3796 checkout = str(repo.changelog.rev(other.lookup(checkout)))
3796 checkout = str(repo.changelog.rev(other.lookup(checkout)))
3797 repo._subtoppath = source
3797 repo._subtoppath = source
3798 try:
3798 try:
3799 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
3799 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
3800
3800
3801 finally:
3801 finally:
3802 del repo._subtoppath
3802 del repo._subtoppath
3803
3803
3804 # update specified bookmarks
3804 # update specified bookmarks
3805 if opts.get('bookmark'):
3805 if opts.get('bookmark'):
3806 for b in opts['bookmark']:
3806 for b in opts['bookmark']:
3807 # explicit pull overrides local bookmark if any
3807 # explicit pull overrides local bookmark if any
3808 ui.status(_("importing bookmark %s\n") % b)
3808 ui.status(_("importing bookmark %s\n") % b)
3809 repo._bookmarks[b] = repo[rb[b]].node()
3809 repo._bookmarks[b] = repo[rb[b]].node()
3810 bookmarks.write(repo)
3810 bookmarks.write(repo)
3811
3811
3812 return ret
3812 return ret
3813
3813
3814 @command('^push',
3814 @command('^push',
3815 [('f', 'force', None, _('force push')),
3815 [('f', 'force', None, _('force push')),
3816 ('r', 'rev', [],
3816 ('r', 'rev', [],
3817 _('a changeset intended to be included in the destination'),
3817 _('a changeset intended to be included in the destination'),
3818 _('REV')),
3818 _('REV')),
3819 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
3819 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
3820 ('b', 'branch', [],
3820 ('b', 'branch', [],
3821 _('a specific branch you would like to push'), _('BRANCH')),
3821 _('a specific branch you would like to push'), _('BRANCH')),
3822 ('', 'new-branch', False, _('allow pushing a new branch')),
3822 ('', 'new-branch', False, _('allow pushing a new branch')),
3823 ] + remoteopts,
3823 ] + remoteopts,
3824 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
3824 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
3825 def push(ui, repo, dest=None, **opts):
3825 def push(ui, repo, dest=None, **opts):
3826 """push changes to the specified destination
3826 """push changes to the specified destination
3827
3827
3828 Push changesets from the local repository to the specified
3828 Push changesets from the local repository to the specified
3829 destination.
3829 destination.
3830
3830
3831 This operation is symmetrical to pull: it is identical to a pull
3831 This operation is symmetrical to pull: it is identical to a pull
3832 in the destination repository from the current one.
3832 in the destination repository from the current one.
3833
3833
3834 By default, push will not allow creation of new heads at the
3834 By default, push will not allow creation of new heads at the
3835 destination, since multiple heads would make it unclear which head
3835 destination, since multiple heads would make it unclear which head
3836 to use. In this situation, it is recommended to pull and merge
3836 to use. In this situation, it is recommended to pull and merge
3837 before pushing.
3837 before pushing.
3838
3838
3839 Use --new-branch if you want to allow push to create a new named
3839 Use --new-branch if you want to allow push to create a new named
3840 branch that is not present at the destination. This allows you to
3840 branch that is not present at the destination. This allows you to
3841 only create a new branch without forcing other changes.
3841 only create a new branch without forcing other changes.
3842
3842
3843 Use -f/--force to override the default behavior and push all
3843 Use -f/--force to override the default behavior and push all
3844 changesets on all branches.
3844 changesets on all branches.
3845
3845
3846 If -r/--rev is used, the specified revision and all its ancestors
3846 If -r/--rev is used, the specified revision and all its ancestors
3847 will be pushed to the remote repository.
3847 will be pushed to the remote repository.
3848
3848
3849 Please see :hg:`help urls` for important details about ``ssh://``
3849 Please see :hg:`help urls` for important details about ``ssh://``
3850 URLs. If DESTINATION is omitted, a default path will be used.
3850 URLs. If DESTINATION is omitted, a default path will be used.
3851
3851
3852 Returns 0 if push was successful, 1 if nothing to push.
3852 Returns 0 if push was successful, 1 if nothing to push.
3853 """
3853 """
3854
3854
3855 if opts.get('bookmark'):
3855 if opts.get('bookmark'):
3856 for b in opts['bookmark']:
3856 for b in opts['bookmark']:
3857 # translate -B options to -r so changesets get pushed
3857 # translate -B options to -r so changesets get pushed
3858 if b in repo._bookmarks:
3858 if b in repo._bookmarks:
3859 opts.setdefault('rev', []).append(b)
3859 opts.setdefault('rev', []).append(b)
3860 else:
3860 else:
3861 # if we try to push a deleted bookmark, translate it to null
3861 # if we try to push a deleted bookmark, translate it to null
3862 # this lets simultaneous -r, -b options continue working
3862 # this lets simultaneous -r, -b options continue working
3863 opts.setdefault('rev', []).append("null")
3863 opts.setdefault('rev', []).append("null")
3864
3864
3865 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3865 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3866 dest, branches = hg.parseurl(dest, opts.get('branch'))
3866 dest, branches = hg.parseurl(dest, opts.get('branch'))
3867 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
3867 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
3868 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
3868 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
3869 other = hg.peer(repo, opts, dest)
3869 other = hg.peer(repo, opts, dest)
3870 if revs:
3870 if revs:
3871 revs = [repo.lookup(rev) for rev in revs]
3871 revs = [repo.lookup(rev) for rev in revs]
3872
3872
3873 repo._subtoppath = dest
3873 repo._subtoppath = dest
3874 try:
3874 try:
3875 # push subrepos depth-first for coherent ordering
3875 # push subrepos depth-first for coherent ordering
3876 c = repo['']
3876 c = repo['']
3877 subs = c.substate # only repos that are committed
3877 subs = c.substate # only repos that are committed
3878 for s in sorted(subs):
3878 for s in sorted(subs):
3879 if not c.sub(s).push(opts.get('force')):
3879 if not c.sub(s).push(opts.get('force')):
3880 return False
3880 return False
3881 finally:
3881 finally:
3882 del repo._subtoppath
3882 del repo._subtoppath
3883 result = repo.push(other, opts.get('force'), revs=revs,
3883 result = repo.push(other, opts.get('force'), revs=revs,
3884 newbranch=opts.get('new_branch'))
3884 newbranch=opts.get('new_branch'))
3885
3885
3886 result = (result == 0)
3886 result = (result == 0)
3887
3887
3888 if opts.get('bookmark'):
3888 if opts.get('bookmark'):
3889 rb = other.listkeys('bookmarks')
3889 rb = other.listkeys('bookmarks')
3890 for b in opts['bookmark']:
3890 for b in opts['bookmark']:
3891 # explicit push overrides remote bookmark if any
3891 # explicit push overrides remote bookmark if any
3892 if b in repo._bookmarks:
3892 if b in repo._bookmarks:
3893 ui.status(_("exporting bookmark %s\n") % b)
3893 ui.status(_("exporting bookmark %s\n") % b)
3894 new = repo[b].hex()
3894 new = repo[b].hex()
3895 elif b in rb:
3895 elif b in rb:
3896 ui.status(_("deleting remote bookmark %s\n") % b)
3896 ui.status(_("deleting remote bookmark %s\n") % b)
3897 new = '' # delete
3897 new = '' # delete
3898 else:
3898 else:
3899 ui.warn(_('bookmark %s does not exist on the local '
3899 ui.warn(_('bookmark %s does not exist on the local '
3900 'or remote repository!\n') % b)
3900 'or remote repository!\n') % b)
3901 return 2
3901 return 2
3902 old = rb.get(b, '')
3902 old = rb.get(b, '')
3903 r = other.pushkey('bookmarks', b, old, new)
3903 r = other.pushkey('bookmarks', b, old, new)
3904 if not r:
3904 if not r:
3905 ui.warn(_('updating bookmark %s failed!\n') % b)
3905 ui.warn(_('updating bookmark %s failed!\n') % b)
3906 if not result:
3906 if not result:
3907 result = 2
3907 result = 2
3908
3908
3909 return result
3909 return result
3910
3910
3911 @command('recover', [])
3911 @command('recover', [])
3912 def recover(ui, repo):
3912 def recover(ui, repo):
3913 """roll back an interrupted transaction
3913 """roll back an interrupted transaction
3914
3914
3915 Recover from an interrupted commit or pull.
3915 Recover from an interrupted commit or pull.
3916
3916
3917 This command tries to fix the repository status after an
3917 This command tries to fix the repository status after an
3918 interrupted operation. It should only be necessary when Mercurial
3918 interrupted operation. It should only be necessary when Mercurial
3919 suggests it.
3919 suggests it.
3920
3920
3921 Returns 0 if successful, 1 if nothing to recover or verify fails.
3921 Returns 0 if successful, 1 if nothing to recover or verify fails.
3922 """
3922 """
3923 if repo.recover():
3923 if repo.recover():
3924 return hg.verify(repo)
3924 return hg.verify(repo)
3925 return 1
3925 return 1
3926
3926
3927 @command('^remove|rm',
3927 @command('^remove|rm',
3928 [('A', 'after', None, _('record delete for missing files')),
3928 [('A', 'after', None, _('record delete for missing files')),
3929 ('f', 'force', None,
3929 ('f', 'force', None,
3930 _('remove (and delete) file even if added or modified')),
3930 _('remove (and delete) file even if added or modified')),
3931 ] + walkopts,
3931 ] + walkopts,
3932 _('[OPTION]... FILE...'))
3932 _('[OPTION]... FILE...'))
3933 def remove(ui, repo, *pats, **opts):
3933 def remove(ui, repo, *pats, **opts):
3934 """remove the specified files on the next commit
3934 """remove the specified files on the next commit
3935
3935
3936 Schedule the indicated files for removal from the repository.
3936 Schedule the indicated files for removal from the repository.
3937
3937
3938 This only removes files from the current branch, not from the
3938 This only removes files from the current branch, not from the
3939 entire project history. -A/--after can be used to remove only
3939 entire project history. -A/--after can be used to remove only
3940 files that have already been deleted, -f/--force can be used to
3940 files that have already been deleted, -f/--force can be used to
3941 force deletion, and -Af can be used to remove files from the next
3941 force deletion, and -Af can be used to remove files from the next
3942 revision without deleting them from the working directory.
3942 revision without deleting them from the working directory.
3943
3943
3944 The following table details the behavior of remove for different
3944 The following table details the behavior of remove for different
3945 file states (columns) and option combinations (rows). The file
3945 file states (columns) and option combinations (rows). The file
3946 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
3946 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
3947 reported by :hg:`status`). The actions are Warn, Remove (from
3947 reported by :hg:`status`). The actions are Warn, Remove (from
3948 branch) and Delete (from disk)::
3948 branch) and Delete (from disk)::
3949
3949
3950 A C M !
3950 A C M !
3951 none W RD W R
3951 none W RD W R
3952 -f R RD RD R
3952 -f R RD RD R
3953 -A W W W R
3953 -A W W W R
3954 -Af R R R R
3954 -Af R R R R
3955
3955
3956 Note that remove never deletes files in Added [A] state from the
3956 Note that remove never deletes files in Added [A] state from the
3957 working directory, not even if option --force is specified.
3957 working directory, not even if option --force is specified.
3958
3958
3959 This command schedules the files to be removed at the next commit.
3959 This command schedules the files to be removed at the next commit.
3960 To undo a remove before that, see :hg:`revert`.
3960 To undo a remove before that, see :hg:`revert`.
3961
3961
3962 Returns 0 on success, 1 if any warnings encountered.
3962 Returns 0 on success, 1 if any warnings encountered.
3963 """
3963 """
3964
3964
3965 ret = 0
3965 ret = 0
3966 after, force = opts.get('after'), opts.get('force')
3966 after, force = opts.get('after'), opts.get('force')
3967 if not pats and not after:
3967 if not pats and not after:
3968 raise util.Abort(_('no files specified'))
3968 raise util.Abort(_('no files specified'))
3969
3969
3970 m = scmutil.match(repo[None], pats, opts)
3970 m = scmutil.match(repo[None], pats, opts)
3971 s = repo.status(match=m, clean=True)
3971 s = repo.status(match=m, clean=True)
3972 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
3972 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
3973
3973
3974 for f in m.files():
3974 for f in m.files():
3975 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
3975 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
3976 if os.path.exists(m.rel(f)):
3976 if os.path.exists(m.rel(f)):
3977 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
3977 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
3978 ret = 1
3978 ret = 1
3979
3979
3980 if force:
3980 if force:
3981 list = modified + deleted + clean + added
3981 list = modified + deleted + clean + added
3982 elif after:
3982 elif after:
3983 list = deleted
3983 list = deleted
3984 for f in modified + added + clean:
3984 for f in modified + added + clean:
3985 ui.warn(_('not removing %s: file still exists (use -f'
3985 ui.warn(_('not removing %s: file still exists (use -f'
3986 ' to force removal)\n') % m.rel(f))
3986 ' to force removal)\n') % m.rel(f))
3987 ret = 1
3987 ret = 1
3988 else:
3988 else:
3989 list = deleted + clean
3989 list = deleted + clean
3990 for f in modified:
3990 for f in modified:
3991 ui.warn(_('not removing %s: file is modified (use -f'
3991 ui.warn(_('not removing %s: file is modified (use -f'
3992 ' to force removal)\n') % m.rel(f))
3992 ' to force removal)\n') % m.rel(f))
3993 ret = 1
3993 ret = 1
3994 for f in added:
3994 for f in added:
3995 ui.warn(_('not removing %s: file has been marked for add (use -f'
3995 ui.warn(_('not removing %s: file has been marked for add (use -f'
3996 ' to force removal)\n') % m.rel(f))
3996 ' to force removal)\n') % m.rel(f))
3997 ret = 1
3997 ret = 1
3998
3998
3999 for f in sorted(list):
3999 for f in sorted(list):
4000 if ui.verbose or not m.exact(f):
4000 if ui.verbose or not m.exact(f):
4001 ui.status(_('removing %s\n') % m.rel(f))
4001 ui.status(_('removing %s\n') % m.rel(f))
4002
4002
4003 wlock = repo.wlock()
4003 wlock = repo.wlock()
4004 try:
4004 try:
4005 if not after:
4005 if not after:
4006 for f in list:
4006 for f in list:
4007 if f in added:
4007 if f in added:
4008 continue # we never unlink added files on remove
4008 continue # we never unlink added files on remove
4009 try:
4009 try:
4010 util.unlinkpath(repo.wjoin(f))
4010 util.unlinkpath(repo.wjoin(f))
4011 except OSError, inst:
4011 except OSError, inst:
4012 if inst.errno != errno.ENOENT:
4012 if inst.errno != errno.ENOENT:
4013 raise
4013 raise
4014 repo[None].forget(list)
4014 repo[None].forget(list)
4015 finally:
4015 finally:
4016 wlock.release()
4016 wlock.release()
4017
4017
4018 return ret
4018 return ret
4019
4019
4020 @command('rename|move|mv',
4020 @command('rename|move|mv',
4021 [('A', 'after', None, _('record a rename that has already occurred')),
4021 [('A', 'after', None, _('record a rename that has already occurred')),
4022 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4022 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4023 ] + walkopts + dryrunopts,
4023 ] + walkopts + dryrunopts,
4024 _('[OPTION]... SOURCE... DEST'))
4024 _('[OPTION]... SOURCE... DEST'))
4025 def rename(ui, repo, *pats, **opts):
4025 def rename(ui, repo, *pats, **opts):
4026 """rename files; equivalent of copy + remove
4026 """rename files; equivalent of copy + remove
4027
4027
4028 Mark dest as copies of sources; mark sources for deletion. If dest
4028 Mark dest as copies of sources; mark sources for deletion. If dest
4029 is a directory, copies are put in that directory. If dest is a
4029 is a directory, copies are put in that directory. If dest is a
4030 file, there can only be one source.
4030 file, there can only be one source.
4031
4031
4032 By default, this command copies the contents of files as they
4032 By default, this command copies the contents of files as they
4033 exist in the working directory. If invoked with -A/--after, the
4033 exist in the working directory. If invoked with -A/--after, the
4034 operation is recorded, but no copying is performed.
4034 operation is recorded, but no copying is performed.
4035
4035
4036 This command takes effect at the next commit. To undo a rename
4036 This command takes effect at the next commit. To undo a rename
4037 before that, see :hg:`revert`.
4037 before that, see :hg:`revert`.
4038
4038
4039 Returns 0 on success, 1 if errors are encountered.
4039 Returns 0 on success, 1 if errors are encountered.
4040 """
4040 """
4041 wlock = repo.wlock(False)
4041 wlock = repo.wlock(False)
4042 try:
4042 try:
4043 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4043 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4044 finally:
4044 finally:
4045 wlock.release()
4045 wlock.release()
4046
4046
4047 @command('resolve',
4047 @command('resolve',
4048 [('a', 'all', None, _('select all unresolved files')),
4048 [('a', 'all', None, _('select all unresolved files')),
4049 ('l', 'list', None, _('list state of files needing merge')),
4049 ('l', 'list', None, _('list state of files needing merge')),
4050 ('m', 'mark', None, _('mark files as resolved')),
4050 ('m', 'mark', None, _('mark files as resolved')),
4051 ('u', 'unmark', None, _('mark files as unresolved')),
4051 ('u', 'unmark', None, _('mark files as unresolved')),
4052 ('t', 'tool', '', _('specify merge tool')),
4052 ('t', 'tool', '', _('specify merge tool')),
4053 ('n', 'no-status', None, _('hide status prefix'))]
4053 ('n', 'no-status', None, _('hide status prefix'))]
4054 + walkopts,
4054 + walkopts,
4055 _('[OPTION]... [FILE]...'))
4055 _('[OPTION]... [FILE]...'))
4056 def resolve(ui, repo, *pats, **opts):
4056 def resolve(ui, repo, *pats, **opts):
4057 """redo merges or set/view the merge status of files
4057 """redo merges or set/view the merge status of files
4058
4058
4059 Merges with unresolved conflicts are often the result of
4059 Merges with unresolved conflicts are often the result of
4060 non-interactive merging using the ``internal:merge`` configuration
4060 non-interactive merging using the ``internal:merge`` configuration
4061 setting, or a command-line merge tool like ``diff3``. The resolve
4061 setting, or a command-line merge tool like ``diff3``. The resolve
4062 command is used to manage the files involved in a merge, after
4062 command is used to manage the files involved in a merge, after
4063 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4063 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4064 working directory must have two parents).
4064 working directory must have two parents).
4065
4065
4066 The resolve command can be used in the following ways:
4066 The resolve command can be used in the following ways:
4067
4067
4068 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4068 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4069 files, discarding any previous merge attempts. Re-merging is not
4069 files, discarding any previous merge attempts. Re-merging is not
4070 performed for files already marked as resolved. Use ``--all/-a``
4070 performed for files already marked as resolved. Use ``--all/-a``
4071 to selects all unresolved files. ``--tool`` can be used to specify
4071 to selects all unresolved files. ``--tool`` can be used to specify
4072 the merge tool used for the given files. It overrides the HGMERGE
4072 the merge tool used for the given files. It overrides the HGMERGE
4073 environment variable and your configuration files.
4073 environment variable and your configuration files.
4074
4074
4075 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4075 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4076 (e.g. after having manually fixed-up the files). The default is
4076 (e.g. after having manually fixed-up the files). The default is
4077 to mark all unresolved files.
4077 to mark all unresolved files.
4078
4078
4079 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4079 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4080 default is to mark all resolved files.
4080 default is to mark all resolved files.
4081
4081
4082 - :hg:`resolve -l`: list files which had or still have conflicts.
4082 - :hg:`resolve -l`: list files which had or still have conflicts.
4083 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4083 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4084
4084
4085 Note that Mercurial will not let you commit files with unresolved
4085 Note that Mercurial will not let you commit files with unresolved
4086 merge conflicts. You must use :hg:`resolve -m ...` before you can
4086 merge conflicts. You must use :hg:`resolve -m ...` before you can
4087 commit after a conflicting merge.
4087 commit after a conflicting merge.
4088
4088
4089 Returns 0 on success, 1 if any files fail a resolve attempt.
4089 Returns 0 on success, 1 if any files fail a resolve attempt.
4090 """
4090 """
4091
4091
4092 all, mark, unmark, show, nostatus = \
4092 all, mark, unmark, show, nostatus = \
4093 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
4093 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
4094
4094
4095 if (show and (mark or unmark)) or (mark and unmark):
4095 if (show and (mark or unmark)) or (mark and unmark):
4096 raise util.Abort(_("too many options specified"))
4096 raise util.Abort(_("too many options specified"))
4097 if pats and all:
4097 if pats and all:
4098 raise util.Abort(_("can't specify --all and patterns"))
4098 raise util.Abort(_("can't specify --all and patterns"))
4099 if not (all or pats or show or mark or unmark):
4099 if not (all or pats or show or mark or unmark):
4100 raise util.Abort(_('no files or directories specified; '
4100 raise util.Abort(_('no files or directories specified; '
4101 'use --all to remerge all files'))
4101 'use --all to remerge all files'))
4102
4102
4103 ms = mergemod.mergestate(repo)
4103 ms = mergemod.mergestate(repo)
4104 m = scmutil.match(repo[None], pats, opts)
4104 m = scmutil.match(repo[None], pats, opts)
4105 ret = 0
4105 ret = 0
4106
4106
4107 for f in ms:
4107 for f in ms:
4108 if m(f):
4108 if m(f):
4109 if show:
4109 if show:
4110 if nostatus:
4110 if nostatus:
4111 ui.write("%s\n" % f)
4111 ui.write("%s\n" % f)
4112 else:
4112 else:
4113 ui.write("%s %s\n" % (ms[f].upper(), f),
4113 ui.write("%s %s\n" % (ms[f].upper(), f),
4114 label='resolve.' +
4114 label='resolve.' +
4115 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
4115 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
4116 elif mark:
4116 elif mark:
4117 ms.mark(f, "r")
4117 ms.mark(f, "r")
4118 elif unmark:
4118 elif unmark:
4119 ms.mark(f, "u")
4119 ms.mark(f, "u")
4120 else:
4120 else:
4121 wctx = repo[None]
4121 wctx = repo[None]
4122 mctx = wctx.parents()[-1]
4122 mctx = wctx.parents()[-1]
4123
4123
4124 # backup pre-resolve (merge uses .orig for its own purposes)
4124 # backup pre-resolve (merge uses .orig for its own purposes)
4125 a = repo.wjoin(f)
4125 a = repo.wjoin(f)
4126 util.copyfile(a, a + ".resolve")
4126 util.copyfile(a, a + ".resolve")
4127
4127
4128 try:
4128 try:
4129 # resolve file
4129 # resolve file
4130 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4130 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4131 if ms.resolve(f, wctx, mctx):
4131 if ms.resolve(f, wctx, mctx):
4132 ret = 1
4132 ret = 1
4133 finally:
4133 finally:
4134 ui.setconfig('ui', 'forcemerge', '')
4134 ui.setconfig('ui', 'forcemerge', '')
4135
4135
4136 # replace filemerge's .orig file with our resolve file
4136 # replace filemerge's .orig file with our resolve file
4137 util.rename(a + ".resolve", a + ".orig")
4137 util.rename(a + ".resolve", a + ".orig")
4138
4138
4139 ms.commit()
4139 ms.commit()
4140 return ret
4140 return ret
4141
4141
4142 @command('revert',
4142 @command('revert',
4143 [('a', 'all', None, _('revert all changes when no arguments given')),
4143 [('a', 'all', None, _('revert all changes when no arguments given')),
4144 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4144 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4145 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4145 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4146 ('', 'no-backup', None, _('do not save backup copies of files')),
4146 ('', 'no-backup', None, _('do not save backup copies of files')),
4147 ] + walkopts + dryrunopts,
4147 ] + walkopts + dryrunopts,
4148 _('[OPTION]... [-r REV] [NAME]...'))
4148 _('[OPTION]... [-r REV] [NAME]...'))
4149 def revert(ui, repo, *pats, **opts):
4149 def revert(ui, repo, *pats, **opts):
4150 """restore files to their checkout state
4150 """restore files to their checkout state
4151
4151
4152 .. note::
4152 .. note::
4153 To check out earlier revisions, you should use :hg:`update REV`.
4153 To check out earlier revisions, you should use :hg:`update REV`.
4154 To cancel a merge (and lose your changes), use :hg:`update --clean .`.
4154 To cancel a merge (and lose your changes), use :hg:`update --clean .`.
4155
4155
4156 With no revision specified, revert the specified files or directories
4156 With no revision specified, revert the specified files or directories
4157 to the state they had in the first parent of the working directory.
4157 to the state they had in the first parent of the working directory.
4158 This restores the contents of files to an unmodified
4158 This restores the contents of files to an unmodified
4159 state and unschedules adds, removes, copies, and renames.
4159 state and unschedules adds, removes, copies, and renames.
4160
4160
4161 Using the -r/--rev or -d/--date options, revert the given files or
4161 Using the -r/--rev or -d/--date options, revert the given files or
4162 directories to their states as of a specific revision. Because
4162 directories to their states as of a specific revision. Because
4163 revert does not change the working directory parents, this will
4163 revert does not change the working directory parents, this will
4164 cause these files to appear modified. This can be helpful to "back
4164 cause these files to appear modified. This can be helpful to "back
4165 out" some or all of an earlier change. See :hg:`backout` for a
4165 out" some or all of an earlier change. See :hg:`backout` for a
4166 related method.
4166 related method.
4167
4167
4168 Modified files are saved with a .orig suffix before reverting.
4168 Modified files are saved with a .orig suffix before reverting.
4169 To disable these backups, use --no-backup.
4169 To disable these backups, use --no-backup.
4170
4170
4171 See :hg:`help dates` for a list of formats valid for -d/--date.
4171 See :hg:`help dates` for a list of formats valid for -d/--date.
4172
4172
4173 Returns 0 on success.
4173 Returns 0 on success.
4174 """
4174 """
4175
4175
4176 if opts.get("date"):
4176 if opts.get("date"):
4177 if opts.get("rev"):
4177 if opts.get("rev"):
4178 raise util.Abort(_("you can't specify a revision and a date"))
4178 raise util.Abort(_("you can't specify a revision and a date"))
4179 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4179 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4180
4180
4181 parent, p2 = repo.dirstate.parents()
4181 parent, p2 = repo.dirstate.parents()
4182 ctx = scmutil.revsingle(repo, opts.get('rev'))
4182 ctx = scmutil.revsingle(repo, opts.get('rev'))
4183 node = ctx.node()
4183 node = ctx.node()
4184
4184
4185 if not pats and not opts.get('all'):
4185 if not pats and not opts.get('all'):
4186 msg = _("no files or directories specified")
4186 msg = _("no files or directories specified")
4187 hint = _("use --all to discard all changes")
4188 if p2 != nullid:
4187 if p2 != nullid:
4189 hint = _("uncommitted merge, use --all to discard all changes,"
4188 hint = _("uncommitted merge, use --all to discard all changes,"
4190 " or 'hg update -C .' to abort the merge")
4189 " or 'hg update -C .' to abort the merge")
4191 elif node != parent:
4190 raise util.Abort(msg, hint=hint)
4192 if util.any(repo.status()):
4191 dirty = util.any(repo.status())
4192 if node != parent:
4193 if dirty:
4193 hint = _("uncommitted changes, use --all to discard all"
4194 hint = _("uncommitted changes, use --all to discard all"
4194 " changes, or 'hg update %s' to update") % ctx.rev()
4195 " changes, or 'hg update %s' to update") % ctx.rev()
4195 else:
4196 else:
4196 hint = _("use --all to revert all files,"
4197 hint = _("use --all to revert all files,"
4197 " or 'hg update %s' to update") % ctx.rev()
4198 " or 'hg update %s' to update") % ctx.rev()
4199 elif dirty:
4200 hint = _("uncommitted changes, use --all to discard all changes")
4201 else:
4202 hint = _("use --all to revert all files")
4198 raise util.Abort(msg, hint=hint)
4203 raise util.Abort(msg, hint=hint)
4199
4204
4200 mf = ctx.manifest()
4205 mf = ctx.manifest()
4201 if node == parent:
4206 if node == parent:
4202 pmf = mf
4207 pmf = mf
4203 else:
4208 else:
4204 pmf = None
4209 pmf = None
4205
4210
4206 # need all matching names in dirstate and manifest of target rev,
4211 # need all matching names in dirstate and manifest of target rev,
4207 # so have to walk both. do not print errors if files exist in one
4212 # so have to walk both. do not print errors if files exist in one
4208 # but not other.
4213 # but not other.
4209
4214
4210 names = {}
4215 names = {}
4211
4216
4212 wlock = repo.wlock()
4217 wlock = repo.wlock()
4213 try:
4218 try:
4214 # walk dirstate.
4219 # walk dirstate.
4215
4220
4216 m = scmutil.match(repo[None], pats, opts)
4221 m = scmutil.match(repo[None], pats, opts)
4217 m.bad = lambda x, y: False
4222 m.bad = lambda x, y: False
4218 for abs in repo.walk(m):
4223 for abs in repo.walk(m):
4219 names[abs] = m.rel(abs), m.exact(abs)
4224 names[abs] = m.rel(abs), m.exact(abs)
4220
4225
4221 # walk target manifest.
4226 # walk target manifest.
4222
4227
4223 def badfn(path, msg):
4228 def badfn(path, msg):
4224 if path in names:
4229 if path in names:
4225 return
4230 return
4226 path_ = path + '/'
4231 path_ = path + '/'
4227 for f in names:
4232 for f in names:
4228 if f.startswith(path_):
4233 if f.startswith(path_):
4229 return
4234 return
4230 ui.warn("%s: %s\n" % (m.rel(path), msg))
4235 ui.warn("%s: %s\n" % (m.rel(path), msg))
4231
4236
4232 m = scmutil.match(repo[node], pats, opts)
4237 m = scmutil.match(repo[node], pats, opts)
4233 m.bad = badfn
4238 m.bad = badfn
4234 for abs in repo[node].walk(m):
4239 for abs in repo[node].walk(m):
4235 if abs not in names:
4240 if abs not in names:
4236 names[abs] = m.rel(abs), m.exact(abs)
4241 names[abs] = m.rel(abs), m.exact(abs)
4237
4242
4238 m = scmutil.matchfiles(repo, names)
4243 m = scmutil.matchfiles(repo, names)
4239 changes = repo.status(match=m)[:4]
4244 changes = repo.status(match=m)[:4]
4240 modified, added, removed, deleted = map(set, changes)
4245 modified, added, removed, deleted = map(set, changes)
4241
4246
4242 # if f is a rename, also revert the source
4247 # if f is a rename, also revert the source
4243 cwd = repo.getcwd()
4248 cwd = repo.getcwd()
4244 for f in added:
4249 for f in added:
4245 src = repo.dirstate.copied(f)
4250 src = repo.dirstate.copied(f)
4246 if src and src not in names and repo.dirstate[src] == 'r':
4251 if src and src not in names and repo.dirstate[src] == 'r':
4247 removed.add(src)
4252 removed.add(src)
4248 names[src] = (repo.pathto(src, cwd), True)
4253 names[src] = (repo.pathto(src, cwd), True)
4249
4254
4250 def removeforget(abs):
4255 def removeforget(abs):
4251 if repo.dirstate[abs] == 'a':
4256 if repo.dirstate[abs] == 'a':
4252 return _('forgetting %s\n')
4257 return _('forgetting %s\n')
4253 return _('removing %s\n')
4258 return _('removing %s\n')
4254
4259
4255 revert = ([], _('reverting %s\n'))
4260 revert = ([], _('reverting %s\n'))
4256 add = ([], _('adding %s\n'))
4261 add = ([], _('adding %s\n'))
4257 remove = ([], removeforget)
4262 remove = ([], removeforget)
4258 undelete = ([], _('undeleting %s\n'))
4263 undelete = ([], _('undeleting %s\n'))
4259
4264
4260 disptable = (
4265 disptable = (
4261 # dispatch table:
4266 # dispatch table:
4262 # file state
4267 # file state
4263 # action if in target manifest
4268 # action if in target manifest
4264 # action if not in target manifest
4269 # action if not in target manifest
4265 # make backup if in target manifest
4270 # make backup if in target manifest
4266 # make backup if not in target manifest
4271 # make backup if not in target manifest
4267 (modified, revert, remove, True, True),
4272 (modified, revert, remove, True, True),
4268 (added, revert, remove, True, False),
4273 (added, revert, remove, True, False),
4269 (removed, undelete, None, False, False),
4274 (removed, undelete, None, False, False),
4270 (deleted, revert, remove, False, False),
4275 (deleted, revert, remove, False, False),
4271 )
4276 )
4272
4277
4273 for abs, (rel, exact) in sorted(names.items()):
4278 for abs, (rel, exact) in sorted(names.items()):
4274 mfentry = mf.get(abs)
4279 mfentry = mf.get(abs)
4275 target = repo.wjoin(abs)
4280 target = repo.wjoin(abs)
4276 def handle(xlist, dobackup):
4281 def handle(xlist, dobackup):
4277 xlist[0].append(abs)
4282 xlist[0].append(abs)
4278 if (dobackup and not opts.get('no_backup') and
4283 if (dobackup and not opts.get('no_backup') and
4279 os.path.lexists(target)):
4284 os.path.lexists(target)):
4280 bakname = "%s.orig" % rel
4285 bakname = "%s.orig" % rel
4281 ui.note(_('saving current version of %s as %s\n') %
4286 ui.note(_('saving current version of %s as %s\n') %
4282 (rel, bakname))
4287 (rel, bakname))
4283 if not opts.get('dry_run'):
4288 if not opts.get('dry_run'):
4284 util.rename(target, bakname)
4289 util.rename(target, bakname)
4285 if ui.verbose or not exact:
4290 if ui.verbose or not exact:
4286 msg = xlist[1]
4291 msg = xlist[1]
4287 if not isinstance(msg, basestring):
4292 if not isinstance(msg, basestring):
4288 msg = msg(abs)
4293 msg = msg(abs)
4289 ui.status(msg % rel)
4294 ui.status(msg % rel)
4290 for table, hitlist, misslist, backuphit, backupmiss in disptable:
4295 for table, hitlist, misslist, backuphit, backupmiss in disptable:
4291 if abs not in table:
4296 if abs not in table:
4292 continue
4297 continue
4293 # file has changed in dirstate
4298 # file has changed in dirstate
4294 if mfentry:
4299 if mfentry:
4295 handle(hitlist, backuphit)
4300 handle(hitlist, backuphit)
4296 elif misslist is not None:
4301 elif misslist is not None:
4297 handle(misslist, backupmiss)
4302 handle(misslist, backupmiss)
4298 break
4303 break
4299 else:
4304 else:
4300 if abs not in repo.dirstate:
4305 if abs not in repo.dirstate:
4301 if mfentry:
4306 if mfentry:
4302 handle(add, True)
4307 handle(add, True)
4303 elif exact:
4308 elif exact:
4304 ui.warn(_('file not managed: %s\n') % rel)
4309 ui.warn(_('file not managed: %s\n') % rel)
4305 continue
4310 continue
4306 # file has not changed in dirstate
4311 # file has not changed in dirstate
4307 if node == parent:
4312 if node == parent:
4308 if exact:
4313 if exact:
4309 ui.warn(_('no changes needed to %s\n') % rel)
4314 ui.warn(_('no changes needed to %s\n') % rel)
4310 continue
4315 continue
4311 if pmf is None:
4316 if pmf is None:
4312 # only need parent manifest in this unlikely case,
4317 # only need parent manifest in this unlikely case,
4313 # so do not read by default
4318 # so do not read by default
4314 pmf = repo[parent].manifest()
4319 pmf = repo[parent].manifest()
4315 if abs in pmf:
4320 if abs in pmf:
4316 if mfentry:
4321 if mfentry:
4317 # if version of file is same in parent and target
4322 # if version of file is same in parent and target
4318 # manifests, do nothing
4323 # manifests, do nothing
4319 if (pmf[abs] != mfentry or
4324 if (pmf[abs] != mfentry or
4320 pmf.flags(abs) != mf.flags(abs)):
4325 pmf.flags(abs) != mf.flags(abs)):
4321 handle(revert, False)
4326 handle(revert, False)
4322 else:
4327 else:
4323 handle(remove, False)
4328 handle(remove, False)
4324
4329
4325 if not opts.get('dry_run'):
4330 if not opts.get('dry_run'):
4326 def checkout(f):
4331 def checkout(f):
4327 fc = ctx[f]
4332 fc = ctx[f]
4328 repo.wwrite(f, fc.data(), fc.flags())
4333 repo.wwrite(f, fc.data(), fc.flags())
4329
4334
4330 audit_path = scmutil.pathauditor(repo.root)
4335 audit_path = scmutil.pathauditor(repo.root)
4331 for f in remove[0]:
4336 for f in remove[0]:
4332 if repo.dirstate[f] == 'a':
4337 if repo.dirstate[f] == 'a':
4333 repo.dirstate.drop(f)
4338 repo.dirstate.drop(f)
4334 continue
4339 continue
4335 audit_path(f)
4340 audit_path(f)
4336 try:
4341 try:
4337 util.unlinkpath(repo.wjoin(f))
4342 util.unlinkpath(repo.wjoin(f))
4338 except OSError:
4343 except OSError:
4339 pass
4344 pass
4340 repo.dirstate.remove(f)
4345 repo.dirstate.remove(f)
4341
4346
4342 normal = None
4347 normal = None
4343 if node == parent:
4348 if node == parent:
4344 # We're reverting to our parent. If possible, we'd like status
4349 # We're reverting to our parent. If possible, we'd like status
4345 # to report the file as clean. We have to use normallookup for
4350 # to report the file as clean. We have to use normallookup for
4346 # merges to avoid losing information about merged/dirty files.
4351 # merges to avoid losing information about merged/dirty files.
4347 if p2 != nullid:
4352 if p2 != nullid:
4348 normal = repo.dirstate.normallookup
4353 normal = repo.dirstate.normallookup
4349 else:
4354 else:
4350 normal = repo.dirstate.normal
4355 normal = repo.dirstate.normal
4351 for f in revert[0]:
4356 for f in revert[0]:
4352 checkout(f)
4357 checkout(f)
4353 if normal:
4358 if normal:
4354 normal(f)
4359 normal(f)
4355
4360
4356 for f in add[0]:
4361 for f in add[0]:
4357 checkout(f)
4362 checkout(f)
4358 repo.dirstate.add(f)
4363 repo.dirstate.add(f)
4359
4364
4360 normal = repo.dirstate.normallookup
4365 normal = repo.dirstate.normallookup
4361 if node == parent and p2 == nullid:
4366 if node == parent and p2 == nullid:
4362 normal = repo.dirstate.normal
4367 normal = repo.dirstate.normal
4363 for f in undelete[0]:
4368 for f in undelete[0]:
4364 checkout(f)
4369 checkout(f)
4365 normal(f)
4370 normal(f)
4366
4371
4367 finally:
4372 finally:
4368 wlock.release()
4373 wlock.release()
4369
4374
4370 @command('rollback', dryrunopts)
4375 @command('rollback', dryrunopts)
4371 def rollback(ui, repo, **opts):
4376 def rollback(ui, repo, **opts):
4372 """roll back the last transaction (dangerous)
4377 """roll back the last transaction (dangerous)
4373
4378
4374 This command should be used with care. There is only one level of
4379 This command should be used with care. There is only one level of
4375 rollback, and there is no way to undo a rollback. It will also
4380 rollback, and there is no way to undo a rollback. It will also
4376 restore the dirstate at the time of the last transaction, losing
4381 restore the dirstate at the time of the last transaction, losing
4377 any dirstate changes since that time. This command does not alter
4382 any dirstate changes since that time. This command does not alter
4378 the working directory.
4383 the working directory.
4379
4384
4380 Transactions are used to encapsulate the effects of all commands
4385 Transactions are used to encapsulate the effects of all commands
4381 that create new changesets or propagate existing changesets into a
4386 that create new changesets or propagate existing changesets into a
4382 repository. For example, the following commands are transactional,
4387 repository. For example, the following commands are transactional,
4383 and their effects can be rolled back:
4388 and their effects can be rolled back:
4384
4389
4385 - commit
4390 - commit
4386 - import
4391 - import
4387 - pull
4392 - pull
4388 - push (with this repository as the destination)
4393 - push (with this repository as the destination)
4389 - unbundle
4394 - unbundle
4390
4395
4391 This command is not intended for use on public repositories. Once
4396 This command is not intended for use on public repositories. Once
4392 changes are visible for pull by other users, rolling a transaction
4397 changes are visible for pull by other users, rolling a transaction
4393 back locally is ineffective (someone else may already have pulled
4398 back locally is ineffective (someone else may already have pulled
4394 the changes). Furthermore, a race is possible with readers of the
4399 the changes). Furthermore, a race is possible with readers of the
4395 repository; for example an in-progress pull from the repository
4400 repository; for example an in-progress pull from the repository
4396 may fail if a rollback is performed.
4401 may fail if a rollback is performed.
4397
4402
4398 Returns 0 on success, 1 if no rollback data is available.
4403 Returns 0 on success, 1 if no rollback data is available.
4399 """
4404 """
4400 return repo.rollback(opts.get('dry_run'))
4405 return repo.rollback(opts.get('dry_run'))
4401
4406
4402 @command('root', [])
4407 @command('root', [])
4403 def root(ui, repo):
4408 def root(ui, repo):
4404 """print the root (top) of the current working directory
4409 """print the root (top) of the current working directory
4405
4410
4406 Print the root directory of the current repository.
4411 Print the root directory of the current repository.
4407
4412
4408 Returns 0 on success.
4413 Returns 0 on success.
4409 """
4414 """
4410 ui.write(repo.root + "\n")
4415 ui.write(repo.root + "\n")
4411
4416
4412 @command('^serve',
4417 @command('^serve',
4413 [('A', 'accesslog', '', _('name of access log file to write to'),
4418 [('A', 'accesslog', '', _('name of access log file to write to'),
4414 _('FILE')),
4419 _('FILE')),
4415 ('d', 'daemon', None, _('run server in background')),
4420 ('d', 'daemon', None, _('run server in background')),
4416 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('NUM')),
4421 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('NUM')),
4417 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4422 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4418 # use string type, then we can check if something was passed
4423 # use string type, then we can check if something was passed
4419 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4424 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4420 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4425 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4421 _('ADDR')),
4426 _('ADDR')),
4422 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4427 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4423 _('PREFIX')),
4428 _('PREFIX')),
4424 ('n', 'name', '',
4429 ('n', 'name', '',
4425 _('name to show in web pages (default: working directory)'), _('NAME')),
4430 _('name to show in web pages (default: working directory)'), _('NAME')),
4426 ('', 'web-conf', '',
4431 ('', 'web-conf', '',
4427 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
4432 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
4428 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4433 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4429 _('FILE')),
4434 _('FILE')),
4430 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4435 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4431 ('', 'stdio', None, _('for remote clients')),
4436 ('', 'stdio', None, _('for remote clients')),
4432 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
4437 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
4433 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4438 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4434 ('', 'style', '', _('template style to use'), _('STYLE')),
4439 ('', 'style', '', _('template style to use'), _('STYLE')),
4435 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4440 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4436 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
4441 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
4437 _('[OPTION]...'))
4442 _('[OPTION]...'))
4438 def serve(ui, repo, **opts):
4443 def serve(ui, repo, **opts):
4439 """start stand-alone webserver
4444 """start stand-alone webserver
4440
4445
4441 Start a local HTTP repository browser and pull server. You can use
4446 Start a local HTTP repository browser and pull server. You can use
4442 this for ad-hoc sharing and browsing of repositories. It is
4447 this for ad-hoc sharing and browsing of repositories. It is
4443 recommended to use a real web server to serve a repository for
4448 recommended to use a real web server to serve a repository for
4444 longer periods of time.
4449 longer periods of time.
4445
4450
4446 Please note that the server does not implement access control.
4451 Please note that the server does not implement access control.
4447 This means that, by default, anybody can read from the server and
4452 This means that, by default, anybody can read from the server and
4448 nobody can write to it by default. Set the ``web.allow_push``
4453 nobody can write to it by default. Set the ``web.allow_push``
4449 option to ``*`` to allow everybody to push to the server. You
4454 option to ``*`` to allow everybody to push to the server. You
4450 should use a real web server if you need to authenticate users.
4455 should use a real web server if you need to authenticate users.
4451
4456
4452 By default, the server logs accesses to stdout and errors to
4457 By default, the server logs accesses to stdout and errors to
4453 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4458 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4454 files.
4459 files.
4455
4460
4456 To have the server choose a free port number to listen on, specify
4461 To have the server choose a free port number to listen on, specify
4457 a port number of 0; in this case, the server will print the port
4462 a port number of 0; in this case, the server will print the port
4458 number it uses.
4463 number it uses.
4459
4464
4460 Returns 0 on success.
4465 Returns 0 on success.
4461 """
4466 """
4462
4467
4463 if opts["stdio"] and opts["cmdserver"]:
4468 if opts["stdio"] and opts["cmdserver"]:
4464 raise util.Abort(_("cannot use --stdio with --cmdserver"))
4469 raise util.Abort(_("cannot use --stdio with --cmdserver"))
4465
4470
4466 def checkrepo():
4471 def checkrepo():
4467 if repo is None:
4472 if repo is None:
4468 raise error.RepoError(_("There is no Mercurial repository here"
4473 raise error.RepoError(_("There is no Mercurial repository here"
4469 " (.hg not found)"))
4474 " (.hg not found)"))
4470
4475
4471 if opts["stdio"]:
4476 if opts["stdio"]:
4472 checkrepo()
4477 checkrepo()
4473 s = sshserver.sshserver(ui, repo)
4478 s = sshserver.sshserver(ui, repo)
4474 s.serve_forever()
4479 s.serve_forever()
4475
4480
4476 if opts["cmdserver"]:
4481 if opts["cmdserver"]:
4477 checkrepo()
4482 checkrepo()
4478 s = commandserver.server(ui, repo, opts["cmdserver"])
4483 s = commandserver.server(ui, repo, opts["cmdserver"])
4479 return s.serve()
4484 return s.serve()
4480
4485
4481 # this way we can check if something was given in the command-line
4486 # this way we can check if something was given in the command-line
4482 if opts.get('port'):
4487 if opts.get('port'):
4483 opts['port'] = util.getport(opts.get('port'))
4488 opts['port'] = util.getport(opts.get('port'))
4484
4489
4485 baseui = repo and repo.baseui or ui
4490 baseui = repo and repo.baseui or ui
4486 optlist = ("name templates style address port prefix ipv6"
4491 optlist = ("name templates style address port prefix ipv6"
4487 " accesslog errorlog certificate encoding")
4492 " accesslog errorlog certificate encoding")
4488 for o in optlist.split():
4493 for o in optlist.split():
4489 val = opts.get(o, '')
4494 val = opts.get(o, '')
4490 if val in (None, ''): # should check against default options instead
4495 if val in (None, ''): # should check against default options instead
4491 continue
4496 continue
4492 baseui.setconfig("web", o, val)
4497 baseui.setconfig("web", o, val)
4493 if repo and repo.ui != baseui:
4498 if repo and repo.ui != baseui:
4494 repo.ui.setconfig("web", o, val)
4499 repo.ui.setconfig("web", o, val)
4495
4500
4496 o = opts.get('web_conf') or opts.get('webdir_conf')
4501 o = opts.get('web_conf') or opts.get('webdir_conf')
4497 if not o:
4502 if not o:
4498 if not repo:
4503 if not repo:
4499 raise error.RepoError(_("There is no Mercurial repository"
4504 raise error.RepoError(_("There is no Mercurial repository"
4500 " here (.hg not found)"))
4505 " here (.hg not found)"))
4501 o = repo.root
4506 o = repo.root
4502
4507
4503 app = hgweb.hgweb(o, baseui=ui)
4508 app = hgweb.hgweb(o, baseui=ui)
4504
4509
4505 class service(object):
4510 class service(object):
4506 def init(self):
4511 def init(self):
4507 util.setsignalhandler()
4512 util.setsignalhandler()
4508 self.httpd = hgweb.server.create_server(ui, app)
4513 self.httpd = hgweb.server.create_server(ui, app)
4509
4514
4510 if opts['port'] and not ui.verbose:
4515 if opts['port'] and not ui.verbose:
4511 return
4516 return
4512
4517
4513 if self.httpd.prefix:
4518 if self.httpd.prefix:
4514 prefix = self.httpd.prefix.strip('/') + '/'
4519 prefix = self.httpd.prefix.strip('/') + '/'
4515 else:
4520 else:
4516 prefix = ''
4521 prefix = ''
4517
4522
4518 port = ':%d' % self.httpd.port
4523 port = ':%d' % self.httpd.port
4519 if port == ':80':
4524 if port == ':80':
4520 port = ''
4525 port = ''
4521
4526
4522 bindaddr = self.httpd.addr
4527 bindaddr = self.httpd.addr
4523 if bindaddr == '0.0.0.0':
4528 if bindaddr == '0.0.0.0':
4524 bindaddr = '*'
4529 bindaddr = '*'
4525 elif ':' in bindaddr: # IPv6
4530 elif ':' in bindaddr: # IPv6
4526 bindaddr = '[%s]' % bindaddr
4531 bindaddr = '[%s]' % bindaddr
4527
4532
4528 fqaddr = self.httpd.fqaddr
4533 fqaddr = self.httpd.fqaddr
4529 if ':' in fqaddr:
4534 if ':' in fqaddr:
4530 fqaddr = '[%s]' % fqaddr
4535 fqaddr = '[%s]' % fqaddr
4531 if opts['port']:
4536 if opts['port']:
4532 write = ui.status
4537 write = ui.status
4533 else:
4538 else:
4534 write = ui.write
4539 write = ui.write
4535 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
4540 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
4536 (fqaddr, port, prefix, bindaddr, self.httpd.port))
4541 (fqaddr, port, prefix, bindaddr, self.httpd.port))
4537
4542
4538 def run(self):
4543 def run(self):
4539 self.httpd.serve_forever()
4544 self.httpd.serve_forever()
4540
4545
4541 service = service()
4546 service = service()
4542
4547
4543 cmdutil.service(opts, initfn=service.init, runfn=service.run)
4548 cmdutil.service(opts, initfn=service.init, runfn=service.run)
4544
4549
4545 @command('showconfig|debugconfig',
4550 @command('showconfig|debugconfig',
4546 [('u', 'untrusted', None, _('show untrusted configuration options'))],
4551 [('u', 'untrusted', None, _('show untrusted configuration options'))],
4547 _('[-u] [NAME]...'))
4552 _('[-u] [NAME]...'))
4548 def showconfig(ui, repo, *values, **opts):
4553 def showconfig(ui, repo, *values, **opts):
4549 """show combined config settings from all hgrc files
4554 """show combined config settings from all hgrc files
4550
4555
4551 With no arguments, print names and values of all config items.
4556 With no arguments, print names and values of all config items.
4552
4557
4553 With one argument of the form section.name, print just the value
4558 With one argument of the form section.name, print just the value
4554 of that config item.
4559 of that config item.
4555
4560
4556 With multiple arguments, print names and values of all config
4561 With multiple arguments, print names and values of all config
4557 items with matching section names.
4562 items with matching section names.
4558
4563
4559 With --debug, the source (filename and line number) is printed
4564 With --debug, the source (filename and line number) is printed
4560 for each config item.
4565 for each config item.
4561
4566
4562 Returns 0 on success.
4567 Returns 0 on success.
4563 """
4568 """
4564
4569
4565 for f in scmutil.rcpath():
4570 for f in scmutil.rcpath():
4566 ui.debug('read config from: %s\n' % f)
4571 ui.debug('read config from: %s\n' % f)
4567 untrusted = bool(opts.get('untrusted'))
4572 untrusted = bool(opts.get('untrusted'))
4568 if values:
4573 if values:
4569 sections = [v for v in values if '.' not in v]
4574 sections = [v for v in values if '.' not in v]
4570 items = [v for v in values if '.' in v]
4575 items = [v for v in values if '.' in v]
4571 if len(items) > 1 or items and sections:
4576 if len(items) > 1 or items and sections:
4572 raise util.Abort(_('only one config item permitted'))
4577 raise util.Abort(_('only one config item permitted'))
4573 for section, name, value in ui.walkconfig(untrusted=untrusted):
4578 for section, name, value in ui.walkconfig(untrusted=untrusted):
4574 value = str(value).replace('\n', '\\n')
4579 value = str(value).replace('\n', '\\n')
4575 sectname = section + '.' + name
4580 sectname = section + '.' + name
4576 if values:
4581 if values:
4577 for v in values:
4582 for v in values:
4578 if v == section:
4583 if v == section:
4579 ui.debug('%s: ' %
4584 ui.debug('%s: ' %
4580 ui.configsource(section, name, untrusted))
4585 ui.configsource(section, name, untrusted))
4581 ui.write('%s=%s\n' % (sectname, value))
4586 ui.write('%s=%s\n' % (sectname, value))
4582 elif v == sectname:
4587 elif v == sectname:
4583 ui.debug('%s: ' %
4588 ui.debug('%s: ' %
4584 ui.configsource(section, name, untrusted))
4589 ui.configsource(section, name, untrusted))
4585 ui.write(value, '\n')
4590 ui.write(value, '\n')
4586 else:
4591 else:
4587 ui.debug('%s: ' %
4592 ui.debug('%s: ' %
4588 ui.configsource(section, name, untrusted))
4593 ui.configsource(section, name, untrusted))
4589 ui.write('%s=%s\n' % (sectname, value))
4594 ui.write('%s=%s\n' % (sectname, value))
4590
4595
4591 @command('^status|st',
4596 @command('^status|st',
4592 [('A', 'all', None, _('show status of all files')),
4597 [('A', 'all', None, _('show status of all files')),
4593 ('m', 'modified', None, _('show only modified files')),
4598 ('m', 'modified', None, _('show only modified files')),
4594 ('a', 'added', None, _('show only added files')),
4599 ('a', 'added', None, _('show only added files')),
4595 ('r', 'removed', None, _('show only removed files')),
4600 ('r', 'removed', None, _('show only removed files')),
4596 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4601 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4597 ('c', 'clean', None, _('show only files without changes')),
4602 ('c', 'clean', None, _('show only files without changes')),
4598 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4603 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4599 ('i', 'ignored', None, _('show only ignored files')),
4604 ('i', 'ignored', None, _('show only ignored files')),
4600 ('n', 'no-status', None, _('hide status prefix')),
4605 ('n', 'no-status', None, _('hide status prefix')),
4601 ('C', 'copies', None, _('show source of copied files')),
4606 ('C', 'copies', None, _('show source of copied files')),
4602 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4607 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4603 ('', 'rev', [], _('show difference from revision'), _('REV')),
4608 ('', 'rev', [], _('show difference from revision'), _('REV')),
4604 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
4609 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
4605 ] + walkopts + subrepoopts,
4610 ] + walkopts + subrepoopts,
4606 _('[OPTION]... [FILE]...'))
4611 _('[OPTION]... [FILE]...'))
4607 def status(ui, repo, *pats, **opts):
4612 def status(ui, repo, *pats, **opts):
4608 """show changed files in the working directory
4613 """show changed files in the working directory
4609
4614
4610 Show status of files in the repository. If names are given, only
4615 Show status of files in the repository. If names are given, only
4611 files that match are shown. Files that are clean or ignored or
4616 files that match are shown. Files that are clean or ignored or
4612 the source of a copy/move operation, are not listed unless
4617 the source of a copy/move operation, are not listed unless
4613 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
4618 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
4614 Unless options described with "show only ..." are given, the
4619 Unless options described with "show only ..." are given, the
4615 options -mardu are used.
4620 options -mardu are used.
4616
4621
4617 Option -q/--quiet hides untracked (unknown and ignored) files
4622 Option -q/--quiet hides untracked (unknown and ignored) files
4618 unless explicitly requested with -u/--unknown or -i/--ignored.
4623 unless explicitly requested with -u/--unknown or -i/--ignored.
4619
4624
4620 .. note::
4625 .. note::
4621 status may appear to disagree with diff if permissions have
4626 status may appear to disagree with diff if permissions have
4622 changed or a merge has occurred. The standard diff format does
4627 changed or a merge has occurred. The standard diff format does
4623 not report permission changes and diff only reports changes
4628 not report permission changes and diff only reports changes
4624 relative to one merge parent.
4629 relative to one merge parent.
4625
4630
4626 If one revision is given, it is used as the base revision.
4631 If one revision is given, it is used as the base revision.
4627 If two revisions are given, the differences between them are
4632 If two revisions are given, the differences between them are
4628 shown. The --change option can also be used as a shortcut to list
4633 shown. The --change option can also be used as a shortcut to list
4629 the changed files of a revision from its first parent.
4634 the changed files of a revision from its first parent.
4630
4635
4631 The codes used to show the status of files are::
4636 The codes used to show the status of files are::
4632
4637
4633 M = modified
4638 M = modified
4634 A = added
4639 A = added
4635 R = removed
4640 R = removed
4636 C = clean
4641 C = clean
4637 ! = missing (deleted by non-hg command, but still tracked)
4642 ! = missing (deleted by non-hg command, but still tracked)
4638 ? = not tracked
4643 ? = not tracked
4639 I = ignored
4644 I = ignored
4640 = origin of the previous file listed as A (added)
4645 = origin of the previous file listed as A (added)
4641
4646
4642 Returns 0 on success.
4647 Returns 0 on success.
4643 """
4648 """
4644
4649
4645 revs = opts.get('rev')
4650 revs = opts.get('rev')
4646 change = opts.get('change')
4651 change = opts.get('change')
4647
4652
4648 if revs and change:
4653 if revs and change:
4649 msg = _('cannot specify --rev and --change at the same time')
4654 msg = _('cannot specify --rev and --change at the same time')
4650 raise util.Abort(msg)
4655 raise util.Abort(msg)
4651 elif change:
4656 elif change:
4652 node2 = repo.lookup(change)
4657 node2 = repo.lookup(change)
4653 node1 = repo[node2].p1().node()
4658 node1 = repo[node2].p1().node()
4654 else:
4659 else:
4655 node1, node2 = scmutil.revpair(repo, revs)
4660 node1, node2 = scmutil.revpair(repo, revs)
4656
4661
4657 cwd = (pats and repo.getcwd()) or ''
4662 cwd = (pats and repo.getcwd()) or ''
4658 end = opts.get('print0') and '\0' or '\n'
4663 end = opts.get('print0') and '\0' or '\n'
4659 copy = {}
4664 copy = {}
4660 states = 'modified added removed deleted unknown ignored clean'.split()
4665 states = 'modified added removed deleted unknown ignored clean'.split()
4661 show = [k for k in states if opts.get(k)]
4666 show = [k for k in states if opts.get(k)]
4662 if opts.get('all'):
4667 if opts.get('all'):
4663 show += ui.quiet and (states[:4] + ['clean']) or states
4668 show += ui.quiet and (states[:4] + ['clean']) or states
4664 if not show:
4669 if not show:
4665 show = ui.quiet and states[:4] or states[:5]
4670 show = ui.quiet and states[:4] or states[:5]
4666
4671
4667 stat = repo.status(node1, node2, scmutil.match(repo[node2], pats, opts),
4672 stat = repo.status(node1, node2, scmutil.match(repo[node2], pats, opts),
4668 'ignored' in show, 'clean' in show, 'unknown' in show,
4673 'ignored' in show, 'clean' in show, 'unknown' in show,
4669 opts.get('subrepos'))
4674 opts.get('subrepos'))
4670 changestates = zip(states, 'MAR!?IC', stat)
4675 changestates = zip(states, 'MAR!?IC', stat)
4671
4676
4672 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
4677 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
4673 ctxn = repo[nullid]
4678 ctxn = repo[nullid]
4674 ctx1 = repo[node1]
4679 ctx1 = repo[node1]
4675 ctx2 = repo[node2]
4680 ctx2 = repo[node2]
4676 added = stat[1]
4681 added = stat[1]
4677 if node2 is None:
4682 if node2 is None:
4678 added = stat[0] + stat[1] # merged?
4683 added = stat[0] + stat[1] # merged?
4679
4684
4680 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
4685 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
4681 if k in added:
4686 if k in added:
4682 copy[k] = v
4687 copy[k] = v
4683 elif v in added:
4688 elif v in added:
4684 copy[v] = k
4689 copy[v] = k
4685
4690
4686 for state, char, files in changestates:
4691 for state, char, files in changestates:
4687 if state in show:
4692 if state in show:
4688 format = "%s %%s%s" % (char, end)
4693 format = "%s %%s%s" % (char, end)
4689 if opts.get('no_status'):
4694 if opts.get('no_status'):
4690 format = "%%s%s" % end
4695 format = "%%s%s" % end
4691
4696
4692 for f in files:
4697 for f in files:
4693 ui.write(format % repo.pathto(f, cwd),
4698 ui.write(format % repo.pathto(f, cwd),
4694 label='status.' + state)
4699 label='status.' + state)
4695 if f in copy:
4700 if f in copy:
4696 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
4701 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
4697 label='status.copied')
4702 label='status.copied')
4698
4703
4699 @command('^summary|sum',
4704 @command('^summary|sum',
4700 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
4705 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
4701 def summary(ui, repo, **opts):
4706 def summary(ui, repo, **opts):
4702 """summarize working directory state
4707 """summarize working directory state
4703
4708
4704 This generates a brief summary of the working directory state,
4709 This generates a brief summary of the working directory state,
4705 including parents, branch, commit status, and available updates.
4710 including parents, branch, commit status, and available updates.
4706
4711
4707 With the --remote option, this will check the default paths for
4712 With the --remote option, this will check the default paths for
4708 incoming and outgoing changes. This can be time-consuming.
4713 incoming and outgoing changes. This can be time-consuming.
4709
4714
4710 Returns 0 on success.
4715 Returns 0 on success.
4711 """
4716 """
4712
4717
4713 ctx = repo[None]
4718 ctx = repo[None]
4714 parents = ctx.parents()
4719 parents = ctx.parents()
4715 pnode = parents[0].node()
4720 pnode = parents[0].node()
4716
4721
4717 for p in parents:
4722 for p in parents:
4718 # label with log.changeset (instead of log.parent) since this
4723 # label with log.changeset (instead of log.parent) since this
4719 # shows a working directory parent *changeset*:
4724 # shows a working directory parent *changeset*:
4720 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
4725 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
4721 label='log.changeset')
4726 label='log.changeset')
4722 ui.write(' '.join(p.tags()), label='log.tag')
4727 ui.write(' '.join(p.tags()), label='log.tag')
4723 if p.bookmarks():
4728 if p.bookmarks():
4724 ui.write(' ' + ' '.join(p.bookmarks()), label='log.bookmark')
4729 ui.write(' ' + ' '.join(p.bookmarks()), label='log.bookmark')
4725 if p.rev() == -1:
4730 if p.rev() == -1:
4726 if not len(repo):
4731 if not len(repo):
4727 ui.write(_(' (empty repository)'))
4732 ui.write(_(' (empty repository)'))
4728 else:
4733 else:
4729 ui.write(_(' (no revision checked out)'))
4734 ui.write(_(' (no revision checked out)'))
4730 ui.write('\n')
4735 ui.write('\n')
4731 if p.description():
4736 if p.description():
4732 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
4737 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
4733 label='log.summary')
4738 label='log.summary')
4734
4739
4735 branch = ctx.branch()
4740 branch = ctx.branch()
4736 bheads = repo.branchheads(branch)
4741 bheads = repo.branchheads(branch)
4737 m = _('branch: %s\n') % branch
4742 m = _('branch: %s\n') % branch
4738 if branch != 'default':
4743 if branch != 'default':
4739 ui.write(m, label='log.branch')
4744 ui.write(m, label='log.branch')
4740 else:
4745 else:
4741 ui.status(m, label='log.branch')
4746 ui.status(m, label='log.branch')
4742
4747
4743 st = list(repo.status(unknown=True))[:6]
4748 st = list(repo.status(unknown=True))[:6]
4744
4749
4745 c = repo.dirstate.copies()
4750 c = repo.dirstate.copies()
4746 copied, renamed = [], []
4751 copied, renamed = [], []
4747 for d, s in c.iteritems():
4752 for d, s in c.iteritems():
4748 if s in st[2]:
4753 if s in st[2]:
4749 st[2].remove(s)
4754 st[2].remove(s)
4750 renamed.append(d)
4755 renamed.append(d)
4751 else:
4756 else:
4752 copied.append(d)
4757 copied.append(d)
4753 if d in st[1]:
4758 if d in st[1]:
4754 st[1].remove(d)
4759 st[1].remove(d)
4755 st.insert(3, renamed)
4760 st.insert(3, renamed)
4756 st.insert(4, copied)
4761 st.insert(4, copied)
4757
4762
4758 ms = mergemod.mergestate(repo)
4763 ms = mergemod.mergestate(repo)
4759 st.append([f for f in ms if ms[f] == 'u'])
4764 st.append([f for f in ms if ms[f] == 'u'])
4760
4765
4761 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
4766 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
4762 st.append(subs)
4767 st.append(subs)
4763
4768
4764 labels = [ui.label(_('%d modified'), 'status.modified'),
4769 labels = [ui.label(_('%d modified'), 'status.modified'),
4765 ui.label(_('%d added'), 'status.added'),
4770 ui.label(_('%d added'), 'status.added'),
4766 ui.label(_('%d removed'), 'status.removed'),
4771 ui.label(_('%d removed'), 'status.removed'),
4767 ui.label(_('%d renamed'), 'status.copied'),
4772 ui.label(_('%d renamed'), 'status.copied'),
4768 ui.label(_('%d copied'), 'status.copied'),
4773 ui.label(_('%d copied'), 'status.copied'),
4769 ui.label(_('%d deleted'), 'status.deleted'),
4774 ui.label(_('%d deleted'), 'status.deleted'),
4770 ui.label(_('%d unknown'), 'status.unknown'),
4775 ui.label(_('%d unknown'), 'status.unknown'),
4771 ui.label(_('%d ignored'), 'status.ignored'),
4776 ui.label(_('%d ignored'), 'status.ignored'),
4772 ui.label(_('%d unresolved'), 'resolve.unresolved'),
4777 ui.label(_('%d unresolved'), 'resolve.unresolved'),
4773 ui.label(_('%d subrepos'), 'status.modified')]
4778 ui.label(_('%d subrepos'), 'status.modified')]
4774 t = []
4779 t = []
4775 for s, l in zip(st, labels):
4780 for s, l in zip(st, labels):
4776 if s:
4781 if s:
4777 t.append(l % len(s))
4782 t.append(l % len(s))
4778
4783
4779 t = ', '.join(t)
4784 t = ', '.join(t)
4780 cleanworkdir = False
4785 cleanworkdir = False
4781
4786
4782 if len(parents) > 1:
4787 if len(parents) > 1:
4783 t += _(' (merge)')
4788 t += _(' (merge)')
4784 elif branch != parents[0].branch():
4789 elif branch != parents[0].branch():
4785 t += _(' (new branch)')
4790 t += _(' (new branch)')
4786 elif (parents[0].extra().get('close') and
4791 elif (parents[0].extra().get('close') and
4787 pnode in repo.branchheads(branch, closed=True)):
4792 pnode in repo.branchheads(branch, closed=True)):
4788 t += _(' (head closed)')
4793 t += _(' (head closed)')
4789 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
4794 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
4790 t += _(' (clean)')
4795 t += _(' (clean)')
4791 cleanworkdir = True
4796 cleanworkdir = True
4792 elif pnode not in bheads:
4797 elif pnode not in bheads:
4793 t += _(' (new branch head)')
4798 t += _(' (new branch head)')
4794
4799
4795 if cleanworkdir:
4800 if cleanworkdir:
4796 ui.status(_('commit: %s\n') % t.strip())
4801 ui.status(_('commit: %s\n') % t.strip())
4797 else:
4802 else:
4798 ui.write(_('commit: %s\n') % t.strip())
4803 ui.write(_('commit: %s\n') % t.strip())
4799
4804
4800 # all ancestors of branch heads - all ancestors of parent = new csets
4805 # all ancestors of branch heads - all ancestors of parent = new csets
4801 new = [0] * len(repo)
4806 new = [0] * len(repo)
4802 cl = repo.changelog
4807 cl = repo.changelog
4803 for a in [cl.rev(n) for n in bheads]:
4808 for a in [cl.rev(n) for n in bheads]:
4804 new[a] = 1
4809 new[a] = 1
4805 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
4810 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
4806 new[a] = 1
4811 new[a] = 1
4807 for a in [p.rev() for p in parents]:
4812 for a in [p.rev() for p in parents]:
4808 if a >= 0:
4813 if a >= 0:
4809 new[a] = 0
4814 new[a] = 0
4810 for a in cl.ancestors(*[p.rev() for p in parents]):
4815 for a in cl.ancestors(*[p.rev() for p in parents]):
4811 new[a] = 0
4816 new[a] = 0
4812 new = sum(new)
4817 new = sum(new)
4813
4818
4814 if new == 0:
4819 if new == 0:
4815 ui.status(_('update: (current)\n'))
4820 ui.status(_('update: (current)\n'))
4816 elif pnode not in bheads:
4821 elif pnode not in bheads:
4817 ui.write(_('update: %d new changesets (update)\n') % new)
4822 ui.write(_('update: %d new changesets (update)\n') % new)
4818 else:
4823 else:
4819 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
4824 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
4820 (new, len(bheads)))
4825 (new, len(bheads)))
4821
4826
4822 if opts.get('remote'):
4827 if opts.get('remote'):
4823 t = []
4828 t = []
4824 source, branches = hg.parseurl(ui.expandpath('default'))
4829 source, branches = hg.parseurl(ui.expandpath('default'))
4825 other = hg.peer(repo, {}, source)
4830 other = hg.peer(repo, {}, source)
4826 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
4831 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
4827 ui.debug('comparing with %s\n' % util.hidepassword(source))
4832 ui.debug('comparing with %s\n' % util.hidepassword(source))
4828 repo.ui.pushbuffer()
4833 repo.ui.pushbuffer()
4829 commoninc = discovery.findcommonincoming(repo, other)
4834 commoninc = discovery.findcommonincoming(repo, other)
4830 _common, incoming, _rheads = commoninc
4835 _common, incoming, _rheads = commoninc
4831 repo.ui.popbuffer()
4836 repo.ui.popbuffer()
4832 if incoming:
4837 if incoming:
4833 t.append(_('1 or more incoming'))
4838 t.append(_('1 or more incoming'))
4834
4839
4835 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
4840 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
4836 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
4841 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
4837 if source != dest:
4842 if source != dest:
4838 other = hg.peer(repo, {}, dest)
4843 other = hg.peer(repo, {}, dest)
4839 commoninc = None
4844 commoninc = None
4840 ui.debug('comparing with %s\n' % util.hidepassword(dest))
4845 ui.debug('comparing with %s\n' % util.hidepassword(dest))
4841 repo.ui.pushbuffer()
4846 repo.ui.pushbuffer()
4842 common, outheads = discovery.findcommonoutgoing(repo, other,
4847 common, outheads = discovery.findcommonoutgoing(repo, other,
4843 commoninc=commoninc)
4848 commoninc=commoninc)
4844 repo.ui.popbuffer()
4849 repo.ui.popbuffer()
4845 o = repo.changelog.findmissing(common=common, heads=outheads)
4850 o = repo.changelog.findmissing(common=common, heads=outheads)
4846 if o:
4851 if o:
4847 t.append(_('%d outgoing') % len(o))
4852 t.append(_('%d outgoing') % len(o))
4848 if 'bookmarks' in other.listkeys('namespaces'):
4853 if 'bookmarks' in other.listkeys('namespaces'):
4849 lmarks = repo.listkeys('bookmarks')
4854 lmarks = repo.listkeys('bookmarks')
4850 rmarks = other.listkeys('bookmarks')
4855 rmarks = other.listkeys('bookmarks')
4851 diff = set(rmarks) - set(lmarks)
4856 diff = set(rmarks) - set(lmarks)
4852 if len(diff) > 0:
4857 if len(diff) > 0:
4853 t.append(_('%d incoming bookmarks') % len(diff))
4858 t.append(_('%d incoming bookmarks') % len(diff))
4854 diff = set(lmarks) - set(rmarks)
4859 diff = set(lmarks) - set(rmarks)
4855 if len(diff) > 0:
4860 if len(diff) > 0:
4856 t.append(_('%d outgoing bookmarks') % len(diff))
4861 t.append(_('%d outgoing bookmarks') % len(diff))
4857
4862
4858 if t:
4863 if t:
4859 ui.write(_('remote: %s\n') % (', '.join(t)))
4864 ui.write(_('remote: %s\n') % (', '.join(t)))
4860 else:
4865 else:
4861 ui.status(_('remote: (synced)\n'))
4866 ui.status(_('remote: (synced)\n'))
4862
4867
4863 @command('tag',
4868 @command('tag',
4864 [('f', 'force', None, _('force tag')),
4869 [('f', 'force', None, _('force tag')),
4865 ('l', 'local', None, _('make the tag local')),
4870 ('l', 'local', None, _('make the tag local')),
4866 ('r', 'rev', '', _('revision to tag'), _('REV')),
4871 ('r', 'rev', '', _('revision to tag'), _('REV')),
4867 ('', 'remove', None, _('remove a tag')),
4872 ('', 'remove', None, _('remove a tag')),
4868 # -l/--local is already there, commitopts cannot be used
4873 # -l/--local is already there, commitopts cannot be used
4869 ('e', 'edit', None, _('edit commit message')),
4874 ('e', 'edit', None, _('edit commit message')),
4870 ('m', 'message', '', _('use <text> as commit message'), _('TEXT')),
4875 ('m', 'message', '', _('use <text> as commit message'), _('TEXT')),
4871 ] + commitopts2,
4876 ] + commitopts2,
4872 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
4877 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
4873 def tag(ui, repo, name1, *names, **opts):
4878 def tag(ui, repo, name1, *names, **opts):
4874 """add one or more tags for the current or given revision
4879 """add one or more tags for the current or given revision
4875
4880
4876 Name a particular revision using <name>.
4881 Name a particular revision using <name>.
4877
4882
4878 Tags are used to name particular revisions of the repository and are
4883 Tags are used to name particular revisions of the repository and are
4879 very useful to compare different revisions, to go back to significant
4884 very useful to compare different revisions, to go back to significant
4880 earlier versions or to mark branch points as releases, etc. Changing
4885 earlier versions or to mark branch points as releases, etc. Changing
4881 an existing tag is normally disallowed; use -f/--force to override.
4886 an existing tag is normally disallowed; use -f/--force to override.
4882
4887
4883 If no revision is given, the parent of the working directory is
4888 If no revision is given, the parent of the working directory is
4884 used, or tip if no revision is checked out.
4889 used, or tip if no revision is checked out.
4885
4890
4886 To facilitate version control, distribution, and merging of tags,
4891 To facilitate version control, distribution, and merging of tags,
4887 they are stored as a file named ".hgtags" which is managed similarly
4892 they are stored as a file named ".hgtags" which is managed similarly
4888 to other project files and can be hand-edited if necessary. This
4893 to other project files and can be hand-edited if necessary. This
4889 also means that tagging creates a new commit. The file
4894 also means that tagging creates a new commit. The file
4890 ".hg/localtags" is used for local tags (not shared among
4895 ".hg/localtags" is used for local tags (not shared among
4891 repositories).
4896 repositories).
4892
4897
4893 Tag commits are usually made at the head of a branch. If the parent
4898 Tag commits are usually made at the head of a branch. If the parent
4894 of the working directory is not a branch head, :hg:`tag` aborts; use
4899 of the working directory is not a branch head, :hg:`tag` aborts; use
4895 -f/--force to force the tag commit to be based on a non-head
4900 -f/--force to force the tag commit to be based on a non-head
4896 changeset.
4901 changeset.
4897
4902
4898 See :hg:`help dates` for a list of formats valid for -d/--date.
4903 See :hg:`help dates` for a list of formats valid for -d/--date.
4899
4904
4900 Since tag names have priority over branch names during revision
4905 Since tag names have priority over branch names during revision
4901 lookup, using an existing branch name as a tag name is discouraged.
4906 lookup, using an existing branch name as a tag name is discouraged.
4902
4907
4903 Returns 0 on success.
4908 Returns 0 on success.
4904 """
4909 """
4905
4910
4906 rev_ = "."
4911 rev_ = "."
4907 names = [t.strip() for t in (name1,) + names]
4912 names = [t.strip() for t in (name1,) + names]
4908 if len(names) != len(set(names)):
4913 if len(names) != len(set(names)):
4909 raise util.Abort(_('tag names must be unique'))
4914 raise util.Abort(_('tag names must be unique'))
4910 for n in names:
4915 for n in names:
4911 if n in ['tip', '.', 'null']:
4916 if n in ['tip', '.', 'null']:
4912 raise util.Abort(_("the name '%s' is reserved") % n)
4917 raise util.Abort(_("the name '%s' is reserved") % n)
4913 if not n:
4918 if not n:
4914 raise util.Abort(_('tag names cannot consist entirely of whitespace'))
4919 raise util.Abort(_('tag names cannot consist entirely of whitespace'))
4915 if opts.get('rev') and opts.get('remove'):
4920 if opts.get('rev') and opts.get('remove'):
4916 raise util.Abort(_("--rev and --remove are incompatible"))
4921 raise util.Abort(_("--rev and --remove are incompatible"))
4917 if opts.get('rev'):
4922 if opts.get('rev'):
4918 rev_ = opts['rev']
4923 rev_ = opts['rev']
4919 message = opts.get('message')
4924 message = opts.get('message')
4920 if opts.get('remove'):
4925 if opts.get('remove'):
4921 expectedtype = opts.get('local') and 'local' or 'global'
4926 expectedtype = opts.get('local') and 'local' or 'global'
4922 for n in names:
4927 for n in names:
4923 if not repo.tagtype(n):
4928 if not repo.tagtype(n):
4924 raise util.Abort(_("tag '%s' does not exist") % n)
4929 raise util.Abort(_("tag '%s' does not exist") % n)
4925 if repo.tagtype(n) != expectedtype:
4930 if repo.tagtype(n) != expectedtype:
4926 if expectedtype == 'global':
4931 if expectedtype == 'global':
4927 raise util.Abort(_("tag '%s' is not a global tag") % n)
4932 raise util.Abort(_("tag '%s' is not a global tag") % n)
4928 else:
4933 else:
4929 raise util.Abort(_("tag '%s' is not a local tag") % n)
4934 raise util.Abort(_("tag '%s' is not a local tag") % n)
4930 rev_ = nullid
4935 rev_ = nullid
4931 if not message:
4936 if not message:
4932 # we don't translate commit messages
4937 # we don't translate commit messages
4933 message = 'Removed tag %s' % ', '.join(names)
4938 message = 'Removed tag %s' % ', '.join(names)
4934 elif not opts.get('force'):
4939 elif not opts.get('force'):
4935 for n in names:
4940 for n in names:
4936 if n in repo.tags():
4941 if n in repo.tags():
4937 raise util.Abort(_("tag '%s' already exists "
4942 raise util.Abort(_("tag '%s' already exists "
4938 "(use -f to force)") % n)
4943 "(use -f to force)") % n)
4939 if not opts.get('local'):
4944 if not opts.get('local'):
4940 p1, p2 = repo.dirstate.parents()
4945 p1, p2 = repo.dirstate.parents()
4941 if p2 != nullid:
4946 if p2 != nullid:
4942 raise util.Abort(_('uncommitted merge'))
4947 raise util.Abort(_('uncommitted merge'))
4943 bheads = repo.branchheads()
4948 bheads = repo.branchheads()
4944 if not opts.get('force') and bheads and p1 not in bheads:
4949 if not opts.get('force') and bheads and p1 not in bheads:
4945 raise util.Abort(_('not at a branch head (use -f to force)'))
4950 raise util.Abort(_('not at a branch head (use -f to force)'))
4946 r = scmutil.revsingle(repo, rev_).node()
4951 r = scmutil.revsingle(repo, rev_).node()
4947
4952
4948 if not message:
4953 if not message:
4949 # we don't translate commit messages
4954 # we don't translate commit messages
4950 message = ('Added tag %s for changeset %s' %
4955 message = ('Added tag %s for changeset %s' %
4951 (', '.join(names), short(r)))
4956 (', '.join(names), short(r)))
4952
4957
4953 date = opts.get('date')
4958 date = opts.get('date')
4954 if date:
4959 if date:
4955 date = util.parsedate(date)
4960 date = util.parsedate(date)
4956
4961
4957 if opts.get('edit'):
4962 if opts.get('edit'):
4958 message = ui.edit(message, ui.username())
4963 message = ui.edit(message, ui.username())
4959
4964
4960 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
4965 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
4961
4966
4962 @command('tags', [], '')
4967 @command('tags', [], '')
4963 def tags(ui, repo):
4968 def tags(ui, repo):
4964 """list repository tags
4969 """list repository tags
4965
4970
4966 This lists both regular and local tags. When the -v/--verbose
4971 This lists both regular and local tags. When the -v/--verbose
4967 switch is used, a third column "local" is printed for local tags.
4972 switch is used, a third column "local" is printed for local tags.
4968
4973
4969 Returns 0 on success.
4974 Returns 0 on success.
4970 """
4975 """
4971
4976
4972 hexfunc = ui.debugflag and hex or short
4977 hexfunc = ui.debugflag and hex or short
4973 tagtype = ""
4978 tagtype = ""
4974
4979
4975 for t, n in reversed(repo.tagslist()):
4980 for t, n in reversed(repo.tagslist()):
4976 if ui.quiet:
4981 if ui.quiet:
4977 ui.write("%s\n" % t)
4982 ui.write("%s\n" % t)
4978 continue
4983 continue
4979
4984
4980 hn = hexfunc(n)
4985 hn = hexfunc(n)
4981 r = "%5d:%s" % (repo.changelog.rev(n), hn)
4986 r = "%5d:%s" % (repo.changelog.rev(n), hn)
4982 spaces = " " * (30 - encoding.colwidth(t))
4987 spaces = " " * (30 - encoding.colwidth(t))
4983
4988
4984 if ui.verbose:
4989 if ui.verbose:
4985 if repo.tagtype(t) == 'local':
4990 if repo.tagtype(t) == 'local':
4986 tagtype = " local"
4991 tagtype = " local"
4987 else:
4992 else:
4988 tagtype = ""
4993 tagtype = ""
4989 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
4994 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
4990
4995
4991 @command('tip',
4996 @command('tip',
4992 [('p', 'patch', None, _('show patch')),
4997 [('p', 'patch', None, _('show patch')),
4993 ('g', 'git', None, _('use git extended diff format')),
4998 ('g', 'git', None, _('use git extended diff format')),
4994 ] + templateopts,
4999 ] + templateopts,
4995 _('[-p] [-g]'))
5000 _('[-p] [-g]'))
4996 def tip(ui, repo, **opts):
5001 def tip(ui, repo, **opts):
4997 """show the tip revision
5002 """show the tip revision
4998
5003
4999 The tip revision (usually just called the tip) is the changeset
5004 The tip revision (usually just called the tip) is the changeset
5000 most recently added to the repository (and therefore the most
5005 most recently added to the repository (and therefore the most
5001 recently changed head).
5006 recently changed head).
5002
5007
5003 If you have just made a commit, that commit will be the tip. If
5008 If you have just made a commit, that commit will be the tip. If
5004 you have just pulled changes from another repository, the tip of
5009 you have just pulled changes from another repository, the tip of
5005 that repository becomes the current tip. The "tip" tag is special
5010 that repository becomes the current tip. The "tip" tag is special
5006 and cannot be renamed or assigned to a different changeset.
5011 and cannot be renamed or assigned to a different changeset.
5007
5012
5008 Returns 0 on success.
5013 Returns 0 on success.
5009 """
5014 """
5010 displayer = cmdutil.show_changeset(ui, repo, opts)
5015 displayer = cmdutil.show_changeset(ui, repo, opts)
5011 displayer.show(repo[len(repo) - 1])
5016 displayer.show(repo[len(repo) - 1])
5012 displayer.close()
5017 displayer.close()
5013
5018
5014 @command('unbundle',
5019 @command('unbundle',
5015 [('u', 'update', None,
5020 [('u', 'update', None,
5016 _('update to new branch head if changesets were unbundled'))],
5021 _('update to new branch head if changesets were unbundled'))],
5017 _('[-u] FILE...'))
5022 _('[-u] FILE...'))
5018 def unbundle(ui, repo, fname1, *fnames, **opts):
5023 def unbundle(ui, repo, fname1, *fnames, **opts):
5019 """apply one or more changegroup files
5024 """apply one or more changegroup files
5020
5025
5021 Apply one or more compressed changegroup files generated by the
5026 Apply one or more compressed changegroup files generated by the
5022 bundle command.
5027 bundle command.
5023
5028
5024 Returns 0 on success, 1 if an update has unresolved files.
5029 Returns 0 on success, 1 if an update has unresolved files.
5025 """
5030 """
5026 fnames = (fname1,) + fnames
5031 fnames = (fname1,) + fnames
5027
5032
5028 lock = repo.lock()
5033 lock = repo.lock()
5029 wc = repo['.']
5034 wc = repo['.']
5030 try:
5035 try:
5031 for fname in fnames:
5036 for fname in fnames:
5032 f = url.open(ui, fname)
5037 f = url.open(ui, fname)
5033 gen = changegroup.readbundle(f, fname)
5038 gen = changegroup.readbundle(f, fname)
5034 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname,
5039 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname,
5035 lock=lock)
5040 lock=lock)
5036 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
5041 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
5037 finally:
5042 finally:
5038 lock.release()
5043 lock.release()
5039 return postincoming(ui, repo, modheads, opts.get('update'), None)
5044 return postincoming(ui, repo, modheads, opts.get('update'), None)
5040
5045
5041 @command('^update|up|checkout|co',
5046 @command('^update|up|checkout|co',
5042 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5047 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5043 ('c', 'check', None,
5048 ('c', 'check', None,
5044 _('update across branches if no uncommitted changes')),
5049 _('update across branches if no uncommitted changes')),
5045 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5050 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5046 ('r', 'rev', '', _('revision'), _('REV'))],
5051 ('r', 'rev', '', _('revision'), _('REV'))],
5047 _('[-c] [-C] [-d DATE] [[-r] REV]'))
5052 _('[-c] [-C] [-d DATE] [[-r] REV]'))
5048 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
5053 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
5049 """update working directory (or switch revisions)
5054 """update working directory (or switch revisions)
5050
5055
5051 Update the repository's working directory to the specified
5056 Update the repository's working directory to the specified
5052 changeset. If no changeset is specified, update to the tip of the
5057 changeset. If no changeset is specified, update to the tip of the
5053 current named branch.
5058 current named branch.
5054
5059
5055 If the changeset is not a descendant of the working directory's
5060 If the changeset is not a descendant of the working directory's
5056 parent, the update is aborted. With the -c/--check option, the
5061 parent, the update is aborted. With the -c/--check option, the
5057 working directory is checked for uncommitted changes; if none are
5062 working directory is checked for uncommitted changes; if none are
5058 found, the working directory is updated to the specified
5063 found, the working directory is updated to the specified
5059 changeset.
5064 changeset.
5060
5065
5061 Update sets the working directory's parent revison to the specified
5066 Update sets the working directory's parent revison to the specified
5062 changeset (see :hg:`help parents`).
5067 changeset (see :hg:`help parents`).
5063
5068
5064 The following rules apply when the working directory contains
5069 The following rules apply when the working directory contains
5065 uncommitted changes:
5070 uncommitted changes:
5066
5071
5067 1. If neither -c/--check nor -C/--clean is specified, and if
5072 1. If neither -c/--check nor -C/--clean is specified, and if
5068 the requested changeset is an ancestor or descendant of
5073 the requested changeset is an ancestor or descendant of
5069 the working directory's parent, the uncommitted changes
5074 the working directory's parent, the uncommitted changes
5070 are merged into the requested changeset and the merged
5075 are merged into the requested changeset and the merged
5071 result is left uncommitted. If the requested changeset is
5076 result is left uncommitted. If the requested changeset is
5072 not an ancestor or descendant (that is, it is on another
5077 not an ancestor or descendant (that is, it is on another
5073 branch), the update is aborted and the uncommitted changes
5078 branch), the update is aborted and the uncommitted changes
5074 are preserved.
5079 are preserved.
5075
5080
5076 2. With the -c/--check option, the update is aborted and the
5081 2. With the -c/--check option, the update is aborted and the
5077 uncommitted changes are preserved.
5082 uncommitted changes are preserved.
5078
5083
5079 3. With the -C/--clean option, uncommitted changes are discarded and
5084 3. With the -C/--clean option, uncommitted changes are discarded and
5080 the working directory is updated to the requested changeset.
5085 the working directory is updated to the requested changeset.
5081
5086
5082 Use null as the changeset to remove the working directory (like
5087 Use null as the changeset to remove the working directory (like
5083 :hg:`clone -U`).
5088 :hg:`clone -U`).
5084
5089
5085 If you want to revert just one file to an older revision, use
5090 If you want to revert just one file to an older revision, use
5086 :hg:`revert [-r REV] NAME`.
5091 :hg:`revert [-r REV] NAME`.
5087
5092
5088 See :hg:`help dates` for a list of formats valid for -d/--date.
5093 See :hg:`help dates` for a list of formats valid for -d/--date.
5089
5094
5090 Returns 0 on success, 1 if there are unresolved files.
5095 Returns 0 on success, 1 if there are unresolved files.
5091 """
5096 """
5092 if rev and node:
5097 if rev and node:
5093 raise util.Abort(_("please specify just one revision"))
5098 raise util.Abort(_("please specify just one revision"))
5094
5099
5095 if rev is None or rev == '':
5100 if rev is None or rev == '':
5096 rev = node
5101 rev = node
5097
5102
5098 # if we defined a bookmark, we have to remember the original bookmark name
5103 # if we defined a bookmark, we have to remember the original bookmark name
5099 brev = rev
5104 brev = rev
5100 rev = scmutil.revsingle(repo, rev, rev).rev()
5105 rev = scmutil.revsingle(repo, rev, rev).rev()
5101
5106
5102 if check and clean:
5107 if check and clean:
5103 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
5108 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
5104
5109
5105 if check:
5110 if check:
5106 # we could use dirty() but we can ignore merge and branch trivia
5111 # we could use dirty() but we can ignore merge and branch trivia
5107 c = repo[None]
5112 c = repo[None]
5108 if c.modified() or c.added() or c.removed():
5113 if c.modified() or c.added() or c.removed():
5109 raise util.Abort(_("uncommitted local changes"))
5114 raise util.Abort(_("uncommitted local changes"))
5110
5115
5111 if date:
5116 if date:
5112 if rev is not None:
5117 if rev is not None:
5113 raise util.Abort(_("you can't specify a revision and a date"))
5118 raise util.Abort(_("you can't specify a revision and a date"))
5114 rev = cmdutil.finddate(ui, repo, date)
5119 rev = cmdutil.finddate(ui, repo, date)
5115
5120
5116 if clean or check:
5121 if clean or check:
5117 ret = hg.clean(repo, rev)
5122 ret = hg.clean(repo, rev)
5118 else:
5123 else:
5119 ret = hg.update(repo, rev)
5124 ret = hg.update(repo, rev)
5120
5125
5121 if brev in repo._bookmarks:
5126 if brev in repo._bookmarks:
5122 bookmarks.setcurrent(repo, brev)
5127 bookmarks.setcurrent(repo, brev)
5123
5128
5124 return ret
5129 return ret
5125
5130
5126 @command('verify', [])
5131 @command('verify', [])
5127 def verify(ui, repo):
5132 def verify(ui, repo):
5128 """verify the integrity of the repository
5133 """verify the integrity of the repository
5129
5134
5130 Verify the integrity of the current repository.
5135 Verify the integrity of the current repository.
5131
5136
5132 This will perform an extensive check of the repository's
5137 This will perform an extensive check of the repository's
5133 integrity, validating the hashes and checksums of each entry in
5138 integrity, validating the hashes and checksums of each entry in
5134 the changelog, manifest, and tracked files, as well as the
5139 the changelog, manifest, and tracked files, as well as the
5135 integrity of their crosslinks and indices.
5140 integrity of their crosslinks and indices.
5136
5141
5137 Returns 0 on success, 1 if errors are encountered.
5142 Returns 0 on success, 1 if errors are encountered.
5138 """
5143 """
5139 return hg.verify(repo)
5144 return hg.verify(repo)
5140
5145
5141 @command('version', [])
5146 @command('version', [])
5142 def version_(ui):
5147 def version_(ui):
5143 """output version and copyright information"""
5148 """output version and copyright information"""
5144 ui.write(_("Mercurial Distributed SCM (version %s)\n")
5149 ui.write(_("Mercurial Distributed SCM (version %s)\n")
5145 % util.version())
5150 % util.version())
5146 ui.status(_(
5151 ui.status(_(
5147 "(see http://mercurial.selenic.com for more information)\n"
5152 "(see http://mercurial.selenic.com for more information)\n"
5148 "\nCopyright (C) 2005-2011 Matt Mackall and others\n"
5153 "\nCopyright (C) 2005-2011 Matt Mackall and others\n"
5149 "This is free software; see the source for copying conditions. "
5154 "This is free software; see the source for copying conditions. "
5150 "There is NO\nwarranty; "
5155 "There is NO\nwarranty; "
5151 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5156 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5152 ))
5157 ))
5153
5158
5154 norepo = ("clone init version help debugcommands debugcomplete"
5159 norepo = ("clone init version help debugcommands debugcomplete"
5155 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
5160 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
5156 " debugknown debuggetbundle debugbundle")
5161 " debugknown debuggetbundle debugbundle")
5157 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
5162 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
5158 " debugdata debugindex debugindexdot debugrevlog")
5163 " debugdata debugindex debugindexdot debugrevlog")
@@ -1,223 +1,229 b''
1 # commandserver.py - communicate with Mercurial's API over a pipe
1 # commandserver.py - communicate with Mercurial's API over a pipe
2 #
2 #
3 # Copyright Matt Mackall <mpm@selenic.com>
3 # Copyright 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 i18n import _
8 from i18n import _
9 import struct
9 import struct
10 import sys
10 import sys
11 import dispatch, encoding, util
11 import dispatch, encoding, util
12
12
13 logfile = None
13 logfile = None
14
14
15 def log(*args):
15 def log(*args):
16 if not logfile:
16 if not logfile:
17 return
17 return
18
18
19 for a in args:
19 for a in args:
20 logfile.write(str(a))
20 logfile.write(str(a))
21
21
22 logfile.flush()
22 logfile.flush()
23
23
24 class channeledoutput(object):
24 class channeledoutput(object):
25 """
25 """
26 Write data from in_ to out in the following format:
26 Write data from in_ to out in the following format:
27
27
28 data length (unsigned int),
28 data length (unsigned int),
29 data
29 data
30 """
30 """
31 def __init__(self, in_, out, channel):
31 def __init__(self, in_, out, channel):
32 self.in_ = in_
32 self.in_ = in_
33 self.out = out
33 self.out = out
34 self.channel = channel
34 self.channel = channel
35
35
36 def write(self, data):
36 def write(self, data):
37 if not data:
37 if not data:
38 return
38 return
39 self.out.write(struct.pack('>cI', self.channel, len(data)))
39 self.out.write(struct.pack('>cI', self.channel, len(data)))
40 self.out.write(data)
40 self.out.write(data)
41 self.out.flush()
41 self.out.flush()
42
42
43 def __getattr__(self, attr):
43 def __getattr__(self, attr):
44 if attr in ('isatty', 'fileno'):
44 if attr in ('isatty', 'fileno'):
45 raise AttributeError, attr
45 raise AttributeError, attr
46 return getattr(self.in_, attr)
46 return getattr(self.in_, attr)
47
47
48 class channeledinput(object):
48 class channeledinput(object):
49 """
49 """
50 Read data from in_.
50 Read data from in_.
51
51
52 Requests for input are written to out in the following format:
52 Requests for input are written to out in the following format:
53 channel identifier - 'I' for plain input, 'L' line based (1 byte)
53 channel identifier - 'I' for plain input, 'L' line based (1 byte)
54 how many bytes to send at most (unsigned int),
54 how many bytes to send at most (unsigned int),
55
55
56 The client replies with:
56 The client replies with:
57 data length (unsigned int), 0 meaning EOF
57 data length (unsigned int), 0 meaning EOF
58 data
58 data
59 """
59 """
60
60
61 maxchunksize = 4 * 1024
61 maxchunksize = 4 * 1024
62
62
63 def __init__(self, in_, out, channel):
63 def __init__(self, in_, out, channel):
64 self.in_ = in_
64 self.in_ = in_
65 self.out = out
65 self.out = out
66 self.channel = channel
66 self.channel = channel
67
67
68 def read(self, size=-1):
68 def read(self, size=-1):
69 if size < 0:
69 if size < 0:
70 # if we need to consume all the clients input, ask for 4k chunks
70 # if we need to consume all the clients input, ask for 4k chunks
71 # so the pipe doesn't fill up risking a deadlock
71 # so the pipe doesn't fill up risking a deadlock
72 size = self.maxchunksize
72 size = self.maxchunksize
73 s = self._read(size, self.channel)
73 s = self._read(size, self.channel)
74 buf = s
74 buf = s
75 while s:
75 while s:
76 s = self._read(size, self.channel)
76 s = self._read(size, self.channel)
77 buf += s
77 buf += s
78
78
79 return buf
79 return buf
80 else:
80 else:
81 return self._read(size, self.channel)
81 return self._read(size, self.channel)
82
82
83 def _read(self, size, channel):
83 def _read(self, size, channel):
84 if not size:
84 if not size:
85 return ''
85 return ''
86 assert size > 0
86 assert size > 0
87
87
88 # tell the client we need at most size bytes
88 # tell the client we need at most size bytes
89 self.out.write(struct.pack('>cI', channel, size))
89 self.out.write(struct.pack('>cI', channel, size))
90 self.out.flush()
90 self.out.flush()
91
91
92 length = self.in_.read(4)
92 length = self.in_.read(4)
93 length = struct.unpack('>I', length)[0]
93 length = struct.unpack('>I', length)[0]
94 if not length:
94 if not length:
95 return ''
95 return ''
96 else:
96 else:
97 return self.in_.read(length)
97 return self.in_.read(length)
98
98
99 def readline(self, size=-1):
99 def readline(self, size=-1):
100 if size < 0:
100 if size < 0:
101 size = self.maxchunksize
101 size = self.maxchunksize
102 s = self._read(size, 'L')
102 s = self._read(size, 'L')
103 buf = s
103 buf = s
104 # keep asking for more until there's either no more or
104 # keep asking for more until there's either no more or
105 # we got a full line
105 # we got a full line
106 while s and s[-1] != '\n':
106 while s and s[-1] != '\n':
107 s = self._read(size, 'L')
107 s = self._read(size, 'L')
108 buf += s
108 buf += s
109
109
110 return buf
110 return buf
111 else:
111 else:
112 return self._read(size, 'L')
112 return self._read(size, 'L')
113
113
114 def __iter__(self):
114 def __iter__(self):
115 return self
115 return self
116
116
117 def next(self):
117 def next(self):
118 l = self.readline()
118 l = self.readline()
119 if not l:
119 if not l:
120 raise StopIteration
120 raise StopIteration
121 return l
121 return l
122
122
123 def __getattr__(self, attr):
123 def __getattr__(self, attr):
124 if attr in ('isatty', 'fileno'):
124 if attr in ('isatty', 'fileno'):
125 raise AttributeError, attr
125 raise AttributeError, attr
126 return getattr(self.in_, attr)
126 return getattr(self.in_, attr)
127
127
128 class server(object):
128 class server(object):
129 """
129 """
130 Listens for commands on stdin, runs them and writes the output on a channel
130 Listens for commands on stdin, runs them and writes the output on a channel
131 based stream to stdout.
131 based stream to stdout.
132 """
132 """
133 def __init__(self, ui, repo, mode):
133 def __init__(self, ui, repo, mode):
134 self.ui = ui
134 self.ui = ui
135
135
136 logpath = ui.config("cmdserver", "log", None)
136 logpath = ui.config("cmdserver", "log", None)
137 if logpath:
137 if logpath:
138 global logfile
138 global logfile
139 if logpath == '-':
139 if logpath == '-':
140 # write log on a special 'd'ebug channel
140 # write log on a special 'd'ebug channel
141 logfile = channeledoutput(sys.stdout, sys.stdout, 'd')
141 logfile = channeledoutput(sys.stdout, sys.stdout, 'd')
142 else:
142 else:
143 logfile = open(logpath, 'a')
143 logfile = open(logpath, 'a')
144
144
145 self.repo = repo
145 self.repo = repo
146 self.repoui = repo.ui
146
147
147 if mode == 'pipe':
148 if mode == 'pipe':
148 self.cerr = channeledoutput(sys.stderr, sys.stdout, 'e')
149 self.cerr = channeledoutput(sys.stderr, sys.stdout, 'e')
149 self.cout = channeledoutput(sys.stdout, sys.stdout, 'o')
150 self.cout = channeledoutput(sys.stdout, sys.stdout, 'o')
150 self.cin = channeledinput(sys.stdin, sys.stdout, 'I')
151 self.cin = channeledinput(sys.stdin, sys.stdout, 'I')
151 self.cresult = channeledoutput(sys.stdout, sys.stdout, 'r')
152 self.cresult = channeledoutput(sys.stdout, sys.stdout, 'r')
152
153
153 self.client = sys.stdin
154 self.client = sys.stdin
154 else:
155 else:
155 raise util.Abort(_('unknown mode %s') % mode)
156 raise util.Abort(_('unknown mode %s') % mode)
156
157
157 def _read(self, size):
158 def _read(self, size):
158 if not size:
159 if not size:
159 return ''
160 return ''
160
161
161 data = self.client.read(size)
162 data = self.client.read(size)
162
163
163 # is the other end closed?
164 # is the other end closed?
164 if not data:
165 if not data:
165 raise EOFError()
166 raise EOFError()
166
167
167 return data
168 return data
168
169
169 def runcommand(self):
170 def runcommand(self):
170 """ reads a list of \0 terminated arguments, executes
171 """ reads a list of \0 terminated arguments, executes
171 and writes the return code to the result channel """
172 and writes the return code to the result channel """
172
173
173 length = struct.unpack('>I', self._read(4))[0]
174 length = struct.unpack('>I', self._read(4))[0]
174 if not length:
175 if not length:
175 args = []
176 args = []
176 else:
177 else:
177 args = self._read(length).split('\0')
178 args = self._read(length).split('\0')
178
179
179 # copy the ui so changes to it don't persist between requests
180 # copy the uis so changes (e.g. --config or --verbose) don't
180 req = dispatch.request(args, self.ui.copy(), self.repo, self.cin,
181 # persist between requests
182 copiedui = self.ui.copy()
183 self.repo.baseui = copiedui
184 self.repo.ui = self.repo.dirstate._ui = self.repoui.copy()
185
186 req = dispatch.request(args, copiedui, self.repo, self.cin,
181 self.cout, self.cerr)
187 self.cout, self.cerr)
182
188
183 ret = dispatch.dispatch(req) or 0 # might return None
189 ret = dispatch.dispatch(req) or 0 # might return None
184
190
185 self.cresult.write(struct.pack('>i', int(ret)))
191 self.cresult.write(struct.pack('>i', int(ret)))
186
192
187 def getencoding(self):
193 def getencoding(self):
188 """ writes the current encoding to the result channel """
194 """ writes the current encoding to the result channel """
189 self.cresult.write(encoding.encoding)
195 self.cresult.write(encoding.encoding)
190
196
191 def serveone(self):
197 def serveone(self):
192 cmd = self.client.readline()[:-1]
198 cmd = self.client.readline()[:-1]
193 if cmd:
199 if cmd:
194 handler = self.capabilities.get(cmd)
200 handler = self.capabilities.get(cmd)
195 if handler:
201 if handler:
196 handler(self)
202 handler(self)
197 else:
203 else:
198 # clients are expected to check what commands are supported by
204 # clients are expected to check what commands are supported by
199 # looking at the servers capabilities
205 # looking at the servers capabilities
200 raise util.Abort(_('unknown command %s') % cmd)
206 raise util.Abort(_('unknown command %s') % cmd)
201
207
202 return cmd != ''
208 return cmd != ''
203
209
204 capabilities = {'runcommand' : runcommand,
210 capabilities = {'runcommand' : runcommand,
205 'getencoding' : getencoding}
211 'getencoding' : getencoding}
206
212
207 def serve(self):
213 def serve(self):
208 hellomsg = 'capabilities: ' + ' '.join(self.capabilities.keys())
214 hellomsg = 'capabilities: ' + ' '.join(self.capabilities.keys())
209 hellomsg += '\n'
215 hellomsg += '\n'
210 hellomsg += 'encoding: ' + encoding.encoding
216 hellomsg += 'encoding: ' + encoding.encoding
211
217
212 # write the hello msg in -one- chunk
218 # write the hello msg in -one- chunk
213 self.cout.write(hellomsg)
219 self.cout.write(hellomsg)
214
220
215 try:
221 try:
216 while self.serveone():
222 while self.serveone():
217 pass
223 pass
218 except EOFError:
224 except EOFError:
219 # we'll get here if the client disconnected while we were reading
225 # we'll get here if the client disconnected while we were reading
220 # its request
226 # its request
221 return 1
227 return 1
222
228
223 return 0
229 return 0
@@ -1,715 +1,727 b''
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 i18n import _
8 from i18n import _
9 import os, sys, atexit, signal, pdb, socket, errno, shlex, time, traceback, re
9 import os, sys, atexit, signal, pdb, socket, errno, shlex, time, traceback, re
10 import util, commands, hg, fancyopts, extensions, hook, error
10 import util, commands, hg, fancyopts, extensions, hook, error
11 import cmdutil, encoding
11 import cmdutil, encoding
12 import ui as uimod
12 import ui as uimod
13
13
14 class request(object):
14 class request(object):
15 def __init__(self, args, ui=None, repo=None, fin=None, fout=None, ferr=None):
15 def __init__(self, args, ui=None, repo=None, fin=None, fout=None, ferr=None):
16 self.args = args
16 self.args = args
17 self.ui = ui
17 self.ui = ui
18 self.repo = repo
18 self.repo = repo
19
19
20 # input/output/error streams
20 # input/output/error streams
21 self.fin = fin
21 self.fin = fin
22 self.fout = fout
22 self.fout = fout
23 self.ferr = ferr
23 self.ferr = ferr
24
24
25 def run():
25 def run():
26 "run the command in sys.argv"
26 "run the command in sys.argv"
27 sys.exit(dispatch(request(sys.argv[1:])))
27 sys.exit(dispatch(request(sys.argv[1:])))
28
28
29 def dispatch(req):
29 def dispatch(req):
30 "run the command specified in req.args"
30 "run the command specified in req.args"
31 if req.ferr:
31 if req.ferr:
32 ferr = req.ferr
32 ferr = req.ferr
33 elif req.ui:
33 elif req.ui:
34 ferr = req.ui.ferr
34 ferr = req.ui.ferr
35 else:
35 else:
36 ferr = sys.stderr
36 ferr = sys.stderr
37
37
38 try:
38 try:
39 if not req.ui:
39 if not req.ui:
40 req.ui = uimod.ui()
40 req.ui = uimod.ui()
41 if '--traceback' in req.args:
41 if '--traceback' in req.args:
42 req.ui.setconfig('ui', 'traceback', 'on')
42 req.ui.setconfig('ui', 'traceback', 'on')
43
43
44 # set ui streams from the request
44 # set ui streams from the request
45 if req.fin:
45 if req.fin:
46 req.ui.fin = req.fin
46 req.ui.fin = req.fin
47 if req.fout:
47 if req.fout:
48 req.ui.fout = req.fout
48 req.ui.fout = req.fout
49 if req.ferr:
49 if req.ferr:
50 req.ui.ferr = req.ferr
50 req.ui.ferr = req.ferr
51 except util.Abort, inst:
51 except util.Abort, inst:
52 ferr.write(_("abort: %s\n") % inst)
52 ferr.write(_("abort: %s\n") % inst)
53 if inst.hint:
53 if inst.hint:
54 ferr.write(_("(%s)\n") % inst.hint)
54 ferr.write(_("(%s)\n") % inst.hint)
55 return -1
55 return -1
56 except error.ParseError, inst:
56 except error.ParseError, inst:
57 if len(inst.args) > 1:
57 if len(inst.args) > 1:
58 ferr.write(_("hg: parse error at %s: %s\n") %
58 ferr.write(_("hg: parse error at %s: %s\n") %
59 (inst.args[1], inst.args[0]))
59 (inst.args[1], inst.args[0]))
60 else:
60 else:
61 ferr.write(_("hg: parse error: %s\n") % inst.args[0])
61 ferr.write(_("hg: parse error: %s\n") % inst.args[0])
62 return -1
62 return -1
63
63
64 return _runcatch(req)
64 return _runcatch(req)
65
65
66 def _runcatch(req):
66 def _runcatch(req):
67 def catchterm(*args):
67 def catchterm(*args):
68 raise error.SignalInterrupt
68 raise error.SignalInterrupt
69
69
70 ui = req.ui
70 ui = req.ui
71 try:
71 try:
72 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
72 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
73 num = getattr(signal, name, None)
73 num = getattr(signal, name, None)
74 if num:
74 if num:
75 signal.signal(num, catchterm)
75 signal.signal(num, catchterm)
76 except ValueError:
76 except ValueError:
77 pass # happens if called in a thread
77 pass # happens if called in a thread
78
78
79 try:
79 try:
80 try:
80 try:
81 # enter the debugger before command execution
81 # enter the debugger before command execution
82 if '--debugger' in req.args:
82 if '--debugger' in req.args:
83 ui.warn(_("entering debugger - "
83 ui.warn(_("entering debugger - "
84 "type c to continue starting hg or h for help\n"))
84 "type c to continue starting hg or h for help\n"))
85 pdb.set_trace()
85 pdb.set_trace()
86 try:
86 try:
87 return _dispatch(req)
87 return _dispatch(req)
88 finally:
88 finally:
89 ui.flush()
89 ui.flush()
90 except:
90 except:
91 # enter the debugger when we hit an exception
91 # enter the debugger when we hit an exception
92 if '--debugger' in req.args:
92 if '--debugger' in req.args:
93 traceback.print_exc()
93 traceback.print_exc()
94 pdb.post_mortem(sys.exc_info()[2])
94 pdb.post_mortem(sys.exc_info()[2])
95 ui.traceback()
95 ui.traceback()
96 raise
96 raise
97
97
98 # Global exception handling, alphabetically
98 # Global exception handling, alphabetically
99 # Mercurial-specific first, followed by built-in and library exceptions
99 # Mercurial-specific first, followed by built-in and library exceptions
100 except error.AmbiguousCommand, inst:
100 except error.AmbiguousCommand, inst:
101 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
101 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
102 (inst.args[0], " ".join(inst.args[1])))
102 (inst.args[0], " ".join(inst.args[1])))
103 except error.ParseError, inst:
103 except error.ParseError, inst:
104 if len(inst.args) > 1:
104 if len(inst.args) > 1:
105 ui.warn(_("hg: parse error at %s: %s\n") %
105 ui.warn(_("hg: parse error at %s: %s\n") %
106 (inst.args[1], inst.args[0]))
106 (inst.args[1], inst.args[0]))
107 else:
107 else:
108 ui.warn(_("hg: parse error: %s\n") % inst.args[0])
108 ui.warn(_("hg: parse error: %s\n") % inst.args[0])
109 return -1
109 return -1
110 except error.LockHeld, inst:
110 except error.LockHeld, inst:
111 if inst.errno == errno.ETIMEDOUT:
111 if inst.errno == errno.ETIMEDOUT:
112 reason = _('timed out waiting for lock held by %s') % inst.locker
112 reason = _('timed out waiting for lock held by %s') % inst.locker
113 else:
113 else:
114 reason = _('lock held by %s') % inst.locker
114 reason = _('lock held by %s') % inst.locker
115 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
115 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
116 except error.LockUnavailable, inst:
116 except error.LockUnavailable, inst:
117 ui.warn(_("abort: could not lock %s: %s\n") %
117 ui.warn(_("abort: could not lock %s: %s\n") %
118 (inst.desc or inst.filename, inst.strerror))
118 (inst.desc or inst.filename, inst.strerror))
119 except error.CommandError, inst:
119 except error.CommandError, inst:
120 if inst.args[0]:
120 if inst.args[0]:
121 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
121 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
122 commands.help_(ui, inst.args[0], full=False, command=True)
122 commands.help_(ui, inst.args[0], full=False, command=True)
123 else:
123 else:
124 ui.warn(_("hg: %s\n") % inst.args[1])
124 ui.warn(_("hg: %s\n") % inst.args[1])
125 commands.help_(ui, 'shortlist')
125 commands.help_(ui, 'shortlist')
126 except error.RepoError, inst:
126 except error.RepoError, inst:
127 ui.warn(_("abort: %s!\n") % inst)
127 ui.warn(_("abort: %s!\n") % inst)
128 except error.ResponseError, inst:
128 except error.ResponseError, inst:
129 ui.warn(_("abort: %s") % inst.args[0])
129 ui.warn(_("abort: %s") % inst.args[0])
130 if not isinstance(inst.args[1], basestring):
130 if not isinstance(inst.args[1], basestring):
131 ui.warn(" %r\n" % (inst.args[1],))
131 ui.warn(" %r\n" % (inst.args[1],))
132 elif not inst.args[1]:
132 elif not inst.args[1]:
133 ui.warn(_(" empty string\n"))
133 ui.warn(_(" empty string\n"))
134 else:
134 else:
135 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
135 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
136 except error.RevlogError, inst:
136 except error.RevlogError, inst:
137 ui.warn(_("abort: %s!\n") % inst)
137 ui.warn(_("abort: %s!\n") % inst)
138 except error.SignalInterrupt:
138 except error.SignalInterrupt:
139 ui.warn(_("killed!\n"))
139 ui.warn(_("killed!\n"))
140 except error.UnknownCommand, inst:
140 except error.UnknownCommand, inst:
141 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
141 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
142 try:
142 try:
143 # check if the command is in a disabled extension
143 # check if the command is in a disabled extension
144 # (but don't check for extensions themselves)
144 # (but don't check for extensions themselves)
145 commands.help_(ui, inst.args[0], unknowncmd=True)
145 commands.help_(ui, inst.args[0], unknowncmd=True)
146 except error.UnknownCommand:
146 except error.UnknownCommand:
147 commands.help_(ui, 'shortlist')
147 commands.help_(ui, 'shortlist')
148 except util.Abort, inst:
148 except util.Abort, inst:
149 ui.warn(_("abort: %s\n") % inst)
149 ui.warn(_("abort: %s\n") % inst)
150 if inst.hint:
150 if inst.hint:
151 ui.warn(_("(%s)\n") % inst.hint)
151 ui.warn(_("(%s)\n") % inst.hint)
152 except ImportError, inst:
152 except ImportError, inst:
153 ui.warn(_("abort: %s!\n") % inst)
153 ui.warn(_("abort: %s!\n") % inst)
154 m = str(inst).split()[-1]
154 m = str(inst).split()[-1]
155 if m in "mpatch bdiff".split():
155 if m in "mpatch bdiff".split():
156 ui.warn(_("(did you forget to compile extensions?)\n"))
156 ui.warn(_("(did you forget to compile extensions?)\n"))
157 elif m in "zlib".split():
157 elif m in "zlib".split():
158 ui.warn(_("(is your Python install correct?)\n"))
158 ui.warn(_("(is your Python install correct?)\n"))
159 except IOError, inst:
159 except IOError, inst:
160 if hasattr(inst, "code"):
160 if hasattr(inst, "code"):
161 ui.warn(_("abort: %s\n") % inst)
161 ui.warn(_("abort: %s\n") % inst)
162 elif hasattr(inst, "reason"):
162 elif hasattr(inst, "reason"):
163 try: # usually it is in the form (errno, strerror)
163 try: # usually it is in the form (errno, strerror)
164 reason = inst.reason.args[1]
164 reason = inst.reason.args[1]
165 except (AttributeError, IndexError):
165 except (AttributeError, IndexError):
166 # it might be anything, for example a string
166 # it might be anything, for example a string
167 reason = inst.reason
167 reason = inst.reason
168 ui.warn(_("abort: error: %s\n") % reason)
168 ui.warn(_("abort: error: %s\n") % reason)
169 elif hasattr(inst, "args") and inst.args[0] == errno.EPIPE:
169 elif hasattr(inst, "args") and inst.args[0] == errno.EPIPE:
170 if ui.debugflag:
170 if ui.debugflag:
171 ui.warn(_("broken pipe\n"))
171 ui.warn(_("broken pipe\n"))
172 elif getattr(inst, "strerror", None):
172 elif getattr(inst, "strerror", None):
173 if getattr(inst, "filename", None):
173 if getattr(inst, "filename", None):
174 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
174 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
175 else:
175 else:
176 ui.warn(_("abort: %s\n") % inst.strerror)
176 ui.warn(_("abort: %s\n") % inst.strerror)
177 else:
177 else:
178 raise
178 raise
179 except OSError, inst:
179 except OSError, inst:
180 if getattr(inst, "filename", None):
180 if getattr(inst, "filename", None):
181 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
181 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
182 else:
182 else:
183 ui.warn(_("abort: %s\n") % inst.strerror)
183 ui.warn(_("abort: %s\n") % inst.strerror)
184 except KeyboardInterrupt:
184 except KeyboardInterrupt:
185 try:
185 try:
186 ui.warn(_("interrupted!\n"))
186 ui.warn(_("interrupted!\n"))
187 except IOError, inst:
187 except IOError, inst:
188 if inst.errno == errno.EPIPE:
188 if inst.errno == errno.EPIPE:
189 if ui.debugflag:
189 if ui.debugflag:
190 ui.warn(_("\nbroken pipe\n"))
190 ui.warn(_("\nbroken pipe\n"))
191 else:
191 else:
192 raise
192 raise
193 except MemoryError:
193 except MemoryError:
194 ui.warn(_("abort: out of memory\n"))
194 ui.warn(_("abort: out of memory\n"))
195 except SystemExit, inst:
195 except SystemExit, inst:
196 # Commands shouldn't sys.exit directly, but give a return code.
196 # Commands shouldn't sys.exit directly, but give a return code.
197 # Just in case catch this and and pass exit code to caller.
197 # Just in case catch this and and pass exit code to caller.
198 return inst.code
198 return inst.code
199 except socket.error, inst:
199 except socket.error, inst:
200 ui.warn(_("abort: %s\n") % inst.args[-1])
200 ui.warn(_("abort: %s\n") % inst.args[-1])
201 except:
201 except:
202 ui.warn(_("** unknown exception encountered,"
202 ui.warn(_("** unknown exception encountered,"
203 " please report by visiting\n"))
203 " please report by visiting\n"))
204 ui.warn(_("** http://mercurial.selenic.com/wiki/BugTracker\n"))
204 ui.warn(_("** http://mercurial.selenic.com/wiki/BugTracker\n"))
205 ui.warn(_("** Python %s\n") % sys.version.replace('\n', ''))
205 ui.warn(_("** Python %s\n") % sys.version.replace('\n', ''))
206 ui.warn(_("** Mercurial Distributed SCM (version %s)\n")
206 ui.warn(_("** Mercurial Distributed SCM (version %s)\n")
207 % util.version())
207 % util.version())
208 ui.warn(_("** Extensions loaded: %s\n")
208 ui.warn(_("** Extensions loaded: %s\n")
209 % ", ".join([x[0] for x in extensions.extensions()]))
209 % ", ".join([x[0] for x in extensions.extensions()]))
210 raise
210 raise
211
211
212 return -1
212 return -1
213
213
214 def aliasargs(fn, givenargs):
214 def aliasargs(fn, givenargs):
215 args = getattr(fn, 'args', [])
215 args = getattr(fn, 'args', [])
216 if args and givenargs:
216 if args and givenargs:
217 cmd = ' '.join(map(util.shellquote, args))
217 cmd = ' '.join(map(util.shellquote, args))
218
218
219 nums = []
219 nums = []
220 def replacer(m):
220 def replacer(m):
221 num = int(m.group(1)) - 1
221 num = int(m.group(1)) - 1
222 nums.append(num)
222 nums.append(num)
223 return givenargs[num]
223 return givenargs[num]
224 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
224 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
225 givenargs = [x for i, x in enumerate(givenargs)
225 givenargs = [x for i, x in enumerate(givenargs)
226 if i not in nums]
226 if i not in nums]
227 args = shlex.split(cmd)
227 args = shlex.split(cmd)
228 return args + givenargs
228 return args + givenargs
229
229
230 class cmdalias(object):
230 class cmdalias(object):
231 def __init__(self, name, definition, cmdtable):
231 def __init__(self, name, definition, cmdtable):
232 self.name = self.cmd = name
232 self.name = self.cmd = name
233 self.cmdname = ''
233 self.cmdname = ''
234 self.definition = definition
234 self.definition = definition
235 self.args = []
235 self.args = []
236 self.opts = []
236 self.opts = []
237 self.help = ''
237 self.help = ''
238 self.norepo = True
238 self.norepo = True
239 self.badalias = False
239 self.badalias = False
240
240
241 try:
241 try:
242 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
242 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
243 for alias, e in cmdtable.iteritems():
243 for alias, e in cmdtable.iteritems():
244 if e is entry:
244 if e is entry:
245 self.cmd = alias
245 self.cmd = alias
246 break
246 break
247 self.shadows = True
247 self.shadows = True
248 except error.UnknownCommand:
248 except error.UnknownCommand:
249 self.shadows = False
249 self.shadows = False
250
250
251 if not self.definition:
251 if not self.definition:
252 def fn(ui, *args):
252 def fn(ui, *args):
253 ui.warn(_("no definition for alias '%s'\n") % self.name)
253 ui.warn(_("no definition for alias '%s'\n") % self.name)
254 return 1
254 return 1
255 self.fn = fn
255 self.fn = fn
256 self.badalias = True
256 self.badalias = True
257
257
258 return
258 return
259
259
260 if self.definition.startswith('!'):
260 if self.definition.startswith('!'):
261 self.shell = True
261 self.shell = True
262 def fn(ui, *args):
262 def fn(ui, *args):
263 env = {'HG_ARGS': ' '.join((self.name,) + args)}
263 env = {'HG_ARGS': ' '.join((self.name,) + args)}
264 def _checkvar(m):
264 def _checkvar(m):
265 if m.groups()[0] == '$':
265 if m.groups()[0] == '$':
266 return m.group()
266 return m.group()
267 elif int(m.groups()[0]) <= len(args):
267 elif int(m.groups()[0]) <= len(args):
268 return m.group()
268 return m.group()
269 else:
269 else:
270 ui.debug("No argument found for substitution "
270 ui.debug("No argument found for substitution "
271 "of %i variable in alias '%s' definition."
271 "of %i variable in alias '%s' definition."
272 % (int(m.groups()[0]), self.name))
272 % (int(m.groups()[0]), self.name))
273 return ''
273 return ''
274 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
274 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
275 replace = dict((str(i + 1), arg) for i, arg in enumerate(args))
275 replace = dict((str(i + 1), arg) for i, arg in enumerate(args))
276 replace['0'] = self.name
276 replace['0'] = self.name
277 replace['@'] = ' '.join(args)
277 replace['@'] = ' '.join(args)
278 cmd = util.interpolate(r'\$', replace, cmd, escape_prefix=True)
278 cmd = util.interpolate(r'\$', replace, cmd, escape_prefix=True)
279 return util.system(cmd, environ=env, out=ui.fout)
279 return util.system(cmd, environ=env, out=ui.fout)
280 self.fn = fn
280 self.fn = fn
281 return
281 return
282
282
283 args = shlex.split(self.definition)
283 args = shlex.split(self.definition)
284 self.cmdname = cmd = args.pop(0)
284 self.cmdname = cmd = args.pop(0)
285 args = map(util.expandpath, args)
285 args = map(util.expandpath, args)
286
286
287 for invalidarg in ("--cwd", "-R", "--repository", "--repo"):
287 for invalidarg in ("--cwd", "-R", "--repository", "--repo"):
288 if _earlygetopt([invalidarg], args):
288 if _earlygetopt([invalidarg], args):
289 def fn(ui, *args):
289 def fn(ui, *args):
290 ui.warn(_("error in definition for alias '%s': %s may only "
290 ui.warn(_("error in definition for alias '%s': %s may only "
291 "be given on the command line\n")
291 "be given on the command line\n")
292 % (self.name, invalidarg))
292 % (self.name, invalidarg))
293 return 1
293 return 1
294
294
295 self.fn = fn
295 self.fn = fn
296 self.badalias = True
296 self.badalias = True
297 return
297 return
298
298
299 try:
299 try:
300 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
300 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
301 if len(tableentry) > 2:
301 if len(tableentry) > 2:
302 self.fn, self.opts, self.help = tableentry
302 self.fn, self.opts, self.help = tableentry
303 else:
303 else:
304 self.fn, self.opts = tableentry
304 self.fn, self.opts = tableentry
305
305
306 self.args = aliasargs(self.fn, args)
306 self.args = aliasargs(self.fn, args)
307 if cmd not in commands.norepo.split(' '):
307 if cmd not in commands.norepo.split(' '):
308 self.norepo = False
308 self.norepo = False
309 if self.help.startswith("hg " + cmd):
309 if self.help.startswith("hg " + cmd):
310 # drop prefix in old-style help lines so hg shows the alias
310 # drop prefix in old-style help lines so hg shows the alias
311 self.help = self.help[4 + len(cmd):]
311 self.help = self.help[4 + len(cmd):]
312 self.__doc__ = self.fn.__doc__
312 self.__doc__ = self.fn.__doc__
313
313
314 except error.UnknownCommand:
314 except error.UnknownCommand:
315 def fn(ui, *args):
315 def fn(ui, *args):
316 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
316 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
317 % (self.name, cmd))
317 % (self.name, cmd))
318 try:
318 try:
319 # check if the command is in a disabled extension
319 # check if the command is in a disabled extension
320 commands.help_(ui, cmd, unknowncmd=True)
320 commands.help_(ui, cmd, unknowncmd=True)
321 except error.UnknownCommand:
321 except error.UnknownCommand:
322 pass
322 pass
323 return 1
323 return 1
324 self.fn = fn
324 self.fn = fn
325 self.badalias = True
325 self.badalias = True
326 except error.AmbiguousCommand:
326 except error.AmbiguousCommand:
327 def fn(ui, *args):
327 def fn(ui, *args):
328 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
328 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
329 % (self.name, cmd))
329 % (self.name, cmd))
330 return 1
330 return 1
331 self.fn = fn
331 self.fn = fn
332 self.badalias = True
332 self.badalias = True
333
333
334 def __call__(self, ui, *args, **opts):
334 def __call__(self, ui, *args, **opts):
335 if self.shadows:
335 if self.shadows:
336 ui.debug("alias '%s' shadows command '%s'\n" %
336 ui.debug("alias '%s' shadows command '%s'\n" %
337 (self.name, self.cmdname))
337 (self.name, self.cmdname))
338
338
339 if hasattr(self, 'shell'):
339 if hasattr(self, 'shell'):
340 return self.fn(ui, *args, **opts)
340 return self.fn(ui, *args, **opts)
341 else:
341 else:
342 try:
342 try:
343 util.checksignature(self.fn)(ui, *args, **opts)
343 util.checksignature(self.fn)(ui, *args, **opts)
344 except error.SignatureError:
344 except error.SignatureError:
345 args = ' '.join([self.cmdname] + self.args)
345 args = ' '.join([self.cmdname] + self.args)
346 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
346 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
347 raise
347 raise
348
348
349 def addaliases(ui, cmdtable):
349 def addaliases(ui, cmdtable):
350 # aliases are processed after extensions have been loaded, so they
350 # aliases are processed after extensions have been loaded, so they
351 # may use extension commands. Aliases can also use other alias definitions,
351 # may use extension commands. Aliases can also use other alias definitions,
352 # but only if they have been defined prior to the current definition.
352 # but only if they have been defined prior to the current definition.
353 for alias, definition in ui.configitems('alias'):
353 for alias, definition in ui.configitems('alias'):
354 aliasdef = cmdalias(alias, definition, cmdtable)
354 aliasdef = cmdalias(alias, definition, cmdtable)
355 cmdtable[aliasdef.cmd] = (aliasdef, aliasdef.opts, aliasdef.help)
355 cmdtable[aliasdef.cmd] = (aliasdef, aliasdef.opts, aliasdef.help)
356 if aliasdef.norepo:
356 if aliasdef.norepo:
357 commands.norepo += ' %s' % alias
357 commands.norepo += ' %s' % alias
358
358
359 def _parse(ui, args):
359 def _parse(ui, args):
360 options = {}
360 options = {}
361 cmdoptions = {}
361 cmdoptions = {}
362
362
363 try:
363 try:
364 args = fancyopts.fancyopts(args, commands.globalopts, options)
364 args = fancyopts.fancyopts(args, commands.globalopts, options)
365 except fancyopts.getopt.GetoptError, inst:
365 except fancyopts.getopt.GetoptError, inst:
366 raise error.CommandError(None, inst)
366 raise error.CommandError(None, inst)
367
367
368 if args:
368 if args:
369 cmd, args = args[0], args[1:]
369 cmd, args = args[0], args[1:]
370 aliases, entry = cmdutil.findcmd(cmd, commands.table,
370 aliases, entry = cmdutil.findcmd(cmd, commands.table,
371 ui.config("ui", "strict"))
371 ui.config("ui", "strict"))
372 cmd = aliases[0]
372 cmd = aliases[0]
373 args = aliasargs(entry[0], args)
373 args = aliasargs(entry[0], args)
374 defaults = ui.config("defaults", cmd)
374 defaults = ui.config("defaults", cmd)
375 if defaults:
375 if defaults:
376 args = map(util.expandpath, shlex.split(defaults)) + args
376 args = map(util.expandpath, shlex.split(defaults)) + args
377 c = list(entry[1])
377 c = list(entry[1])
378 else:
378 else:
379 cmd = None
379 cmd = None
380 c = []
380 c = []
381
381
382 # combine global options into local
382 # combine global options into local
383 for o in commands.globalopts:
383 for o in commands.globalopts:
384 c.append((o[0], o[1], options[o[1]], o[3]))
384 c.append((o[0], o[1], options[o[1]], o[3]))
385
385
386 try:
386 try:
387 args = fancyopts.fancyopts(args, c, cmdoptions, True)
387 args = fancyopts.fancyopts(args, c, cmdoptions, True)
388 except fancyopts.getopt.GetoptError, inst:
388 except fancyopts.getopt.GetoptError, inst:
389 raise error.CommandError(cmd, inst)
389 raise error.CommandError(cmd, inst)
390
390
391 # separate global options back out
391 # separate global options back out
392 for o in commands.globalopts:
392 for o in commands.globalopts:
393 n = o[1]
393 n = o[1]
394 options[n] = cmdoptions[n]
394 options[n] = cmdoptions[n]
395 del cmdoptions[n]
395 del cmdoptions[n]
396
396
397 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
397 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
398
398
399 def _parseconfig(ui, config):
399 def _parseconfig(ui, config):
400 """parse the --config options from the command line"""
400 """parse the --config options from the command line"""
401 configs = []
402
401 for cfg in config:
403 for cfg in config:
402 try:
404 try:
403 name, value = cfg.split('=', 1)
405 name, value = cfg.split('=', 1)
404 section, name = name.split('.', 1)
406 section, name = name.split('.', 1)
405 if not section or not name:
407 if not section or not name:
406 raise IndexError
408 raise IndexError
407 ui.setconfig(section, name, value)
409 ui.setconfig(section, name, value)
410 configs.append((section, name, value))
408 except (IndexError, ValueError):
411 except (IndexError, ValueError):
409 raise util.Abort(_('malformed --config option: %r '
412 raise util.Abort(_('malformed --config option: %r '
410 '(use --config section.name=value)') % cfg)
413 '(use --config section.name=value)') % cfg)
411
414
415 return configs
416
412 def _earlygetopt(aliases, args):
417 def _earlygetopt(aliases, args):
413 """Return list of values for an option (or aliases).
418 """Return list of values for an option (or aliases).
414
419
415 The values are listed in the order they appear in args.
420 The values are listed in the order they appear in args.
416 The options and values are removed from args.
421 The options and values are removed from args.
417 """
422 """
418 try:
423 try:
419 argcount = args.index("--")
424 argcount = args.index("--")
420 except ValueError:
425 except ValueError:
421 argcount = len(args)
426 argcount = len(args)
422 shortopts = [opt for opt in aliases if len(opt) == 2]
427 shortopts = [opt for opt in aliases if len(opt) == 2]
423 values = []
428 values = []
424 pos = 0
429 pos = 0
425 while pos < argcount:
430 while pos < argcount:
426 if args[pos] in aliases:
431 if args[pos] in aliases:
427 if pos + 1 >= argcount:
432 if pos + 1 >= argcount:
428 # ignore and let getopt report an error if there is no value
433 # ignore and let getopt report an error if there is no value
429 break
434 break
430 del args[pos]
435 del args[pos]
431 values.append(args.pop(pos))
436 values.append(args.pop(pos))
432 argcount -= 2
437 argcount -= 2
433 elif args[pos][:2] in shortopts:
438 elif args[pos][:2] in shortopts:
434 # short option can have no following space, e.g. hg log -Rfoo
439 # short option can have no following space, e.g. hg log -Rfoo
435 values.append(args.pop(pos)[2:])
440 values.append(args.pop(pos)[2:])
436 argcount -= 1
441 argcount -= 1
437 else:
442 else:
438 pos += 1
443 pos += 1
439 return values
444 return values
440
445
441 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
446 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
442 # run pre-hook, and abort if it fails
447 # run pre-hook, and abort if it fails
443 ret = hook.hook(lui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs),
448 ret = hook.hook(lui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs),
444 pats=cmdpats, opts=cmdoptions)
449 pats=cmdpats, opts=cmdoptions)
445 if ret:
450 if ret:
446 return ret
451 return ret
447 ret = _runcommand(ui, options, cmd, d)
452 ret = _runcommand(ui, options, cmd, d)
448 # run post-hook, passing command result
453 # run post-hook, passing command result
449 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
454 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
450 result=ret, pats=cmdpats, opts=cmdoptions)
455 result=ret, pats=cmdpats, opts=cmdoptions)
451 return ret
456 return ret
452
457
453 def _getlocal(ui, rpath):
458 def _getlocal(ui, rpath):
454 """Return (path, local ui object) for the given target path.
459 """Return (path, local ui object) for the given target path.
455
460
456 Takes paths in [cwd]/.hg/hgrc into account."
461 Takes paths in [cwd]/.hg/hgrc into account."
457 """
462 """
458 try:
463 try:
459 wd = os.getcwd()
464 wd = os.getcwd()
460 except OSError, e:
465 except OSError, e:
461 raise util.Abort(_("error getting current working directory: %s") %
466 raise util.Abort(_("error getting current working directory: %s") %
462 e.strerror)
467 e.strerror)
463 path = cmdutil.findrepo(wd) or ""
468 path = cmdutil.findrepo(wd) or ""
464 if not path:
469 if not path:
465 lui = ui
470 lui = ui
466 else:
471 else:
467 lui = ui.copy()
472 lui = ui.copy()
468 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
473 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
469
474
470 if rpath:
475 if rpath:
471 path = lui.expandpath(rpath[-1])
476 path = lui.expandpath(rpath[-1])
472 lui = ui.copy()
477 lui = ui.copy()
473 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
478 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
474
479
475 return path, lui
480 return path, lui
476
481
477 def _checkshellalias(ui, args):
482 def _checkshellalias(ui, args):
478 cwd = os.getcwd()
483 cwd = os.getcwd()
479 norepo = commands.norepo
484 norepo = commands.norepo
480 options = {}
485 options = {}
481
486
482 try:
487 try:
483 args = fancyopts.fancyopts(args, commands.globalopts, options)
488 args = fancyopts.fancyopts(args, commands.globalopts, options)
484 except fancyopts.getopt.GetoptError:
489 except fancyopts.getopt.GetoptError:
485 return
490 return
486
491
487 if not args:
492 if not args:
488 return
493 return
489
494
490 _parseconfig(ui, options['config'])
495 _parseconfig(ui, options['config'])
491 if options['cwd']:
496 if options['cwd']:
492 os.chdir(options['cwd'])
497 os.chdir(options['cwd'])
493
498
494 path, lui = _getlocal(ui, [options['repository']])
499 path, lui = _getlocal(ui, [options['repository']])
495
500
496 cmdtable = commands.table.copy()
501 cmdtable = commands.table.copy()
497 addaliases(lui, cmdtable)
502 addaliases(lui, cmdtable)
498
503
499 cmd = args[0]
504 cmd = args[0]
500 try:
505 try:
501 aliases, entry = cmdutil.findcmd(cmd, cmdtable, lui.config("ui", "strict"))
506 aliases, entry = cmdutil.findcmd(cmd, cmdtable, lui.config("ui", "strict"))
502 except (error.AmbiguousCommand, error.UnknownCommand):
507 except (error.AmbiguousCommand, error.UnknownCommand):
503 commands.norepo = norepo
508 commands.norepo = norepo
504 os.chdir(cwd)
509 os.chdir(cwd)
505 return
510 return
506
511
507 cmd = aliases[0]
512 cmd = aliases[0]
508 fn = entry[0]
513 fn = entry[0]
509
514
510 if cmd and hasattr(fn, 'shell'):
515 if cmd and hasattr(fn, 'shell'):
511 d = lambda: fn(ui, *args[1:])
516 d = lambda: fn(ui, *args[1:])
512 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d, [], {})
517 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d, [], {})
513
518
514 commands.norepo = norepo
519 commands.norepo = norepo
515 os.chdir(cwd)
520 os.chdir(cwd)
516
521
517 _loaded = set()
522 _loaded = set()
518 def _dispatch(req):
523 def _dispatch(req):
519 args = req.args
524 args = req.args
520 ui = req.ui
525 ui = req.ui
521
526
522 shellaliasfn = _checkshellalias(ui, args)
527 shellaliasfn = _checkshellalias(ui, args)
523 if shellaliasfn:
528 if shellaliasfn:
524 return shellaliasfn()
529 return shellaliasfn()
525
530
526 # read --config before doing anything else
531 # read --config before doing anything else
527 # (e.g. to change trust settings for reading .hg/hgrc)
532 # (e.g. to change trust settings for reading .hg/hgrc)
528 _parseconfig(ui, _earlygetopt(['--config'], args))
533 cfgs = _parseconfig(ui, _earlygetopt(['--config'], args))
529
534
530 # check for cwd
535 # check for cwd
531 cwd = _earlygetopt(['--cwd'], args)
536 cwd = _earlygetopt(['--cwd'], args)
532 if cwd:
537 if cwd:
533 os.chdir(cwd[-1])
538 os.chdir(cwd[-1])
534
539
535 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
540 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
536 path, lui = _getlocal(ui, rpath)
541 path, lui = _getlocal(ui, rpath)
537
542
538 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
543 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
539 # reposetup. Programs like TortoiseHg will call _dispatch several
544 # reposetup. Programs like TortoiseHg will call _dispatch several
540 # times so we keep track of configured extensions in _loaded.
545 # times so we keep track of configured extensions in _loaded.
541 extensions.loadall(lui)
546 extensions.loadall(lui)
542 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
547 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
543 # Propagate any changes to lui.__class__ by extensions
548 # Propagate any changes to lui.__class__ by extensions
544 ui.__class__ = lui.__class__
549 ui.__class__ = lui.__class__
545
550
546 # (uisetup and extsetup are handled in extensions.loadall)
551 # (uisetup and extsetup are handled in extensions.loadall)
547
552
548 for name, module in exts:
553 for name, module in exts:
549 cmdtable = getattr(module, 'cmdtable', {})
554 cmdtable = getattr(module, 'cmdtable', {})
550 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
555 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
551 if overrides:
556 if overrides:
552 ui.warn(_("extension '%s' overrides commands: %s\n")
557 ui.warn(_("extension '%s' overrides commands: %s\n")
553 % (name, " ".join(overrides)))
558 % (name, " ".join(overrides)))
554 commands.table.update(cmdtable)
559 commands.table.update(cmdtable)
555 _loaded.add(name)
560 _loaded.add(name)
556
561
557 # (reposetup is handled in hg.repository)
562 # (reposetup is handled in hg.repository)
558
563
559 addaliases(lui, commands.table)
564 addaliases(lui, commands.table)
560
565
561 # check for fallback encoding
566 # check for fallback encoding
562 fallback = lui.config('ui', 'fallbackencoding')
567 fallback = lui.config('ui', 'fallbackencoding')
563 if fallback:
568 if fallback:
564 encoding.fallbackencoding = fallback
569 encoding.fallbackencoding = fallback
565
570
566 fullargs = args
571 fullargs = args
567 cmd, func, args, options, cmdoptions = _parse(lui, args)
572 cmd, func, args, options, cmdoptions = _parse(lui, args)
568
573
569 if options["config"]:
574 if options["config"]:
570 raise util.Abort(_("option --config may not be abbreviated!"))
575 raise util.Abort(_("option --config may not be abbreviated!"))
571 if options["cwd"]:
576 if options["cwd"]:
572 raise util.Abort(_("option --cwd may not be abbreviated!"))
577 raise util.Abort(_("option --cwd may not be abbreviated!"))
573 if options["repository"]:
578 if options["repository"]:
574 raise util.Abort(_(
579 raise util.Abort(_(
575 "Option -R has to be separated from other options (e.g. not -qR) "
580 "Option -R has to be separated from other options (e.g. not -qR) "
576 "and --repository may only be abbreviated as --repo!"))
581 "and --repository may only be abbreviated as --repo!"))
577
582
578 if options["encoding"]:
583 if options["encoding"]:
579 encoding.encoding = options["encoding"]
584 encoding.encoding = options["encoding"]
580 if options["encodingmode"]:
585 if options["encodingmode"]:
581 encoding.encodingmode = options["encodingmode"]
586 encoding.encodingmode = options["encodingmode"]
582 if options["time"]:
587 if options["time"]:
583 def get_times():
588 def get_times():
584 t = os.times()
589 t = os.times()
585 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
590 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
586 t = (t[0], t[1], t[2], t[3], time.clock())
591 t = (t[0], t[1], t[2], t[3], time.clock())
587 return t
592 return t
588 s = get_times()
593 s = get_times()
589 def print_time():
594 def print_time():
590 t = get_times()
595 t = get_times()
591 ui.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
596 ui.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
592 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
597 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
593 atexit.register(print_time)
598 atexit.register(print_time)
594
599
595 if options['verbose'] or options['debug'] or options['quiet']:
600 uis = set([ui, lui])
596 for ui_ in (ui, lui):
601
597 ui_.setconfig('ui', 'verbose', str(bool(options['verbose'])))
602 if req.repo:
598 ui_.setconfig('ui', 'debug', str(bool(options['debug'])))
603 uis.add(req.repo.ui)
599 ui_.setconfig('ui', 'quiet', str(bool(options['quiet'])))
604
600 if options['traceback']:
605 # copy configs that were passed on the cmdline (--config) to the repo ui
601 for ui_ in (ui, lui):
606 for cfg in cfgs:
602 ui_.setconfig('ui', 'traceback', 'on')
607 req.repo.ui.setconfig(*cfg)
608
609 for opt in ('verbose', 'debug', 'quiet', 'traceback'):
610 val = bool(options[opt])
611 if val:
612 for ui_ in uis:
613 ui_.setconfig('ui', opt, str(val))
614
603 if options['noninteractive']:
615 if options['noninteractive']:
604 for ui_ in (ui, lui):
616 for ui_ in uis:
605 ui_.setconfig('ui', 'interactive', 'off')
617 ui_.setconfig('ui', 'interactive', 'off')
606
618
607 if cmdoptions.get('insecure', False):
619 if cmdoptions.get('insecure', False):
608 for ui_ in (ui, lui):
620 for ui_ in uis:
609 ui_.setconfig('web', 'cacerts', '')
621 ui_.setconfig('web', 'cacerts', '')
610
622
611 if options['help']:
623 if options['help']:
612 return commands.help_(ui, cmd, options['version'])
624 return commands.help_(ui, cmd, options['version'])
613 elif options['version']:
625 elif options['version']:
614 return commands.version_(ui)
626 return commands.version_(ui)
615 elif not cmd:
627 elif not cmd:
616 return commands.help_(ui, 'shortlist')
628 return commands.help_(ui, 'shortlist')
617
629
618 repo = None
630 repo = None
619 cmdpats = args[:]
631 cmdpats = args[:]
620 if cmd not in commands.norepo.split():
632 if cmd not in commands.norepo.split():
621 # use the repo from the request only if we don't have -R
633 # use the repo from the request only if we don't have -R
622 if not rpath:
634 if not rpath:
623 repo = req.repo
635 repo = req.repo
624
636
625 if repo:
637 if repo:
626 # set the descriptors of the repo ui to those of ui
638 # set the descriptors of the repo ui to those of ui
627 repo.ui.fin = ui.fin
639 repo.ui.fin = ui.fin
628 repo.ui.fout = ui.fout
640 repo.ui.fout = ui.fout
629 repo.ui.ferr = ui.ferr
641 repo.ui.ferr = ui.ferr
630 else:
642 else:
631 try:
643 try:
632 repo = hg.repository(ui, path=path)
644 repo = hg.repository(ui, path=path)
633 if not repo.local():
645 if not repo.local():
634 raise util.Abort(_("repository '%s' is not local") % path)
646 raise util.Abort(_("repository '%s' is not local") % path)
635 repo.ui.setconfig("bundle", "mainreporoot", repo.root)
647 repo.ui.setconfig("bundle", "mainreporoot", repo.root)
636 except error.RequirementError:
648 except error.RequirementError:
637 raise
649 raise
638 except error.RepoError:
650 except error.RepoError:
639 if cmd not in commands.optionalrepo.split():
651 if cmd not in commands.optionalrepo.split():
640 if args and not path: # try to infer -R from command args
652 if args and not path: # try to infer -R from command args
641 repos = map(cmdutil.findrepo, args)
653 repos = map(cmdutil.findrepo, args)
642 guess = repos[0]
654 guess = repos[0]
643 if guess and repos.count(guess) == len(repos):
655 if guess and repos.count(guess) == len(repos):
644 req.args = ['--repository', guess] + fullargs
656 req.args = ['--repository', guess] + fullargs
645 return _dispatch(req)
657 return _dispatch(req)
646 if not path:
658 if not path:
647 raise error.RepoError(_("no repository found in %r"
659 raise error.RepoError(_("no repository found in %r"
648 " (.hg not found)") % os.getcwd())
660 " (.hg not found)") % os.getcwd())
649 raise
661 raise
650 if repo:
662 if repo:
651 ui = repo.ui
663 ui = repo.ui
652 args.insert(0, repo)
664 args.insert(0, repo)
653 elif rpath:
665 elif rpath:
654 ui.warn(_("warning: --repository ignored\n"))
666 ui.warn(_("warning: --repository ignored\n"))
655
667
656 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
668 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
657 ui.log("command", msg + "\n")
669 ui.log("command", msg + "\n")
658 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
670 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
659 try:
671 try:
660 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
672 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
661 cmdpats, cmdoptions)
673 cmdpats, cmdoptions)
662 finally:
674 finally:
663 if repo and repo != req.repo:
675 if repo and repo != req.repo:
664 repo.close()
676 repo.close()
665
677
666 def _runcommand(ui, options, cmd, cmdfunc):
678 def _runcommand(ui, options, cmd, cmdfunc):
667 def checkargs():
679 def checkargs():
668 try:
680 try:
669 return cmdfunc()
681 return cmdfunc()
670 except error.SignatureError:
682 except error.SignatureError:
671 raise error.CommandError(cmd, _("invalid arguments"))
683 raise error.CommandError(cmd, _("invalid arguments"))
672
684
673 if options['profile']:
685 if options['profile']:
674 format = ui.config('profiling', 'format', default='text')
686 format = ui.config('profiling', 'format', default='text')
675
687
676 if not format in ['text', 'kcachegrind']:
688 if not format in ['text', 'kcachegrind']:
677 ui.warn(_("unrecognized profiling format '%s'"
689 ui.warn(_("unrecognized profiling format '%s'"
678 " - Ignored\n") % format)
690 " - Ignored\n") % format)
679 format = 'text'
691 format = 'text'
680
692
681 output = ui.config('profiling', 'output')
693 output = ui.config('profiling', 'output')
682
694
683 if output:
695 if output:
684 path = ui.expandpath(output)
696 path = ui.expandpath(output)
685 ostream = open(path, 'wb')
697 ostream = open(path, 'wb')
686 else:
698 else:
687 ostream = sys.stderr
699 ostream = sys.stderr
688
700
689 try:
701 try:
690 from mercurial import lsprof
702 from mercurial import lsprof
691 except ImportError:
703 except ImportError:
692 raise util.Abort(_(
704 raise util.Abort(_(
693 'lsprof not available - install from '
705 'lsprof not available - install from '
694 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
706 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
695 p = lsprof.Profiler()
707 p = lsprof.Profiler()
696 p.enable(subcalls=True)
708 p.enable(subcalls=True)
697 try:
709 try:
698 return checkargs()
710 return checkargs()
699 finally:
711 finally:
700 p.disable()
712 p.disable()
701
713
702 if format == 'kcachegrind':
714 if format == 'kcachegrind':
703 import lsprofcalltree
715 import lsprofcalltree
704 calltree = lsprofcalltree.KCacheGrind(p)
716 calltree = lsprofcalltree.KCacheGrind(p)
705 calltree.output(ostream)
717 calltree.output(ostream)
706 else:
718 else:
707 # format == 'text'
719 # format == 'text'
708 stats = lsprof.Stats(p.getstats())
720 stats = lsprof.Stats(p.getstats())
709 stats.sort()
721 stats.sort()
710 stats.pprint(top=10, file=ostream, climit=5)
722 stats.pprint(top=10, file=ostream, climit=5)
711
723
712 if output:
724 if output:
713 ostream.close()
725 ostream.close()
714 else:
726 else:
715 return checkargs()
727 return checkargs()
@@ -1,269 +1,270 b''
1 # filemerge.py - file-level merge handling for Mercurial
1 # filemerge.py - file-level merge handling for Mercurial
2 #
2 #
3 # Copyright 2006, 2007, 2008 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006, 2007, 2008 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 node import short
8 from node import short
9 from i18n import _
9 from i18n import _
10 import util, simplemerge, match, error
10 import util, simplemerge, match, error
11 import os, tempfile, re, filecmp
11 import os, tempfile, re, filecmp
12
12
13 def _toolstr(ui, tool, part, default=""):
13 def _toolstr(ui, tool, part, default=""):
14 return ui.config("merge-tools", tool + "." + part, default)
14 return ui.config("merge-tools", tool + "." + part, default)
15
15
16 def _toolbool(ui, tool, part, default=False):
16 def _toolbool(ui, tool, part, default=False):
17 return ui.configbool("merge-tools", tool + "." + part, default)
17 return ui.configbool("merge-tools", tool + "." + part, default)
18
18
19 def _toollist(ui, tool, part, default=[]):
19 def _toollist(ui, tool, part, default=[]):
20 return ui.configlist("merge-tools", tool + "." + part, default)
20 return ui.configlist("merge-tools", tool + "." + part, default)
21
21
22 _internal = ['internal:' + s
22 _internal = ['internal:' + s
23 for s in 'fail local other merge prompt dump'.split()]
23 for s in 'fail local other merge prompt dump'.split()]
24
24
25 def _findtool(ui, tool):
25 def _findtool(ui, tool):
26 if tool in _internal:
26 if tool in _internal:
27 return tool
27 return tool
28 for kn in ("regkey", "regkeyalt"):
28 for kn in ("regkey", "regkeyalt"):
29 k = _toolstr(ui, tool, kn)
29 k = _toolstr(ui, tool, kn)
30 if not k:
30 if not k:
31 continue
31 continue
32 p = util.lookupreg(k, _toolstr(ui, tool, "regname"))
32 p = util.lookupreg(k, _toolstr(ui, tool, "regname"))
33 if p:
33 if p:
34 p = util.findexe(p + _toolstr(ui, tool, "regappend"))
34 p = util.findexe(p + _toolstr(ui, tool, "regappend"))
35 if p:
35 if p:
36 return p
36 return p
37 return util.findexe(_toolstr(ui, tool, "executable", tool))
37 return util.findexe(_toolstr(ui, tool, "executable", tool))
38
38
39 def _picktool(repo, ui, path, binary, symlink):
39 def _picktool(repo, ui, path, binary, symlink):
40 def check(tool, pat, symlink, binary):
40 def check(tool, pat, symlink, binary):
41 tmsg = tool
41 tmsg = tool
42 if pat:
42 if pat:
43 tmsg += " specified for " + pat
43 tmsg += " specified for " + pat
44 if not _findtool(ui, tool):
44 if not _findtool(ui, tool):
45 if pat: # explicitly requested tool deserves a warning
45 if pat: # explicitly requested tool deserves a warning
46 ui.warn(_("couldn't find merge tool %s\n") % tmsg)
46 ui.warn(_("couldn't find merge tool %s\n") % tmsg)
47 else: # configured but non-existing tools are more silent
47 else: # configured but non-existing tools are more silent
48 ui.note(_("couldn't find merge tool %s\n") % tmsg)
48 ui.note(_("couldn't find merge tool %s\n") % tmsg)
49 elif symlink and not _toolbool(ui, tool, "symlink"):
49 elif symlink and not _toolbool(ui, tool, "symlink"):
50 ui.warn(_("tool %s can't handle symlinks\n") % tmsg)
50 ui.warn(_("tool %s can't handle symlinks\n") % tmsg)
51 elif binary and not _toolbool(ui, tool, "binary"):
51 elif binary and not _toolbool(ui, tool, "binary"):
52 ui.warn(_("tool %s can't handle binary\n") % tmsg)
52 ui.warn(_("tool %s can't handle binary\n") % tmsg)
53 elif not util.gui() and _toolbool(ui, tool, "gui"):
53 elif not util.gui() and _toolbool(ui, tool, "gui"):
54 ui.warn(_("tool %s requires a GUI\n") % tmsg)
54 ui.warn(_("tool %s requires a GUI\n") % tmsg)
55 else:
55 else:
56 return True
56 return True
57 return False
57 return False
58
58
59 # forcemerge comes from command line arguments, highest priority
59 # forcemerge comes from command line arguments, highest priority
60 force = ui.config('ui', 'forcemerge')
60 force = ui.config('ui', 'forcemerge')
61 if force:
61 if force:
62 toolpath = _findtool(ui, force)
62 toolpath = _findtool(ui, force)
63 if toolpath:
63 if toolpath:
64 return (force, '"' + toolpath + '"')
64 return (force, '"' + toolpath + '"')
65 else:
65 else:
66 # mimic HGMERGE if given tool not found
66 # mimic HGMERGE if given tool not found
67 return (force, force)
67 return (force, force)
68
68
69 # HGMERGE takes next precedence
69 # HGMERGE takes next precedence
70 hgmerge = os.environ.get("HGMERGE")
70 hgmerge = os.environ.get("HGMERGE")
71 if hgmerge:
71 if hgmerge:
72 return (hgmerge, hgmerge)
72 return (hgmerge, hgmerge)
73
73
74 # then patterns
74 # then patterns
75 for pat, tool in ui.configitems("merge-patterns"):
75 for pat, tool in ui.configitems("merge-patterns"):
76 mf = match.match(repo.root, '', [pat])
76 mf = match.match(repo.root, '', [pat])
77 if mf(path) and check(tool, pat, symlink, False):
77 if mf(path) and check(tool, pat, symlink, False):
78 toolpath = _findtool(ui, tool)
78 toolpath = _findtool(ui, tool)
79 return (tool, '"' + toolpath + '"')
79 return (tool, '"' + toolpath + '"')
80
80
81 # then merge tools
81 # then merge tools
82 tools = {}
82 tools = {}
83 for k, v in ui.configitems("merge-tools"):
83 for k, v in ui.configitems("merge-tools"):
84 t = k.split('.')[0]
84 t = k.split('.')[0]
85 if t not in tools:
85 if t not in tools:
86 tools[t] = int(_toolstr(ui, t, "priority", "0"))
86 tools[t] = int(_toolstr(ui, t, "priority", "0"))
87 names = tools.keys()
87 names = tools.keys()
88 tools = sorted([(-p, t) for t, p in tools.items()])
88 tools = sorted([(-p, t) for t, p in tools.items()])
89 uimerge = ui.config("ui", "merge")
89 uimerge = ui.config("ui", "merge")
90 if uimerge:
90 if uimerge:
91 if uimerge not in names:
91 if uimerge not in names:
92 return (uimerge, uimerge)
92 return (uimerge, uimerge)
93 tools.insert(0, (None, uimerge)) # highest priority
93 tools.insert(0, (None, uimerge)) # highest priority
94 tools.append((None, "hgmerge")) # the old default, if found
94 tools.append((None, "hgmerge")) # the old default, if found
95 for p, t in tools:
95 for p, t in tools:
96 if check(t, None, symlink, binary):
96 if check(t, None, symlink, binary):
97 toolpath = _findtool(ui, t)
97 toolpath = _findtool(ui, t)
98 return (t, '"' + toolpath + '"')
98 return (t, '"' + toolpath + '"')
99 # internal merge as last resort
99 # internal merge as last resort
100 return (not (symlink or binary) and "internal:merge" or None, None)
100 return (not (symlink or binary) and "internal:merge" or None, None)
101
101
102 def _eoltype(data):
102 def _eoltype(data):
103 "Guess the EOL type of a file"
103 "Guess the EOL type of a file"
104 if '\0' in data: # binary
104 if '\0' in data: # binary
105 return None
105 return None
106 if '\r\n' in data: # Windows
106 if '\r\n' in data: # Windows
107 return '\r\n'
107 return '\r\n'
108 if '\r' in data: # Old Mac
108 if '\r' in data: # Old Mac
109 return '\r'
109 return '\r'
110 if '\n' in data: # UNIX
110 if '\n' in data: # UNIX
111 return '\n'
111 return '\n'
112 return None # unknown
112 return None # unknown
113
113
114 def _matcheol(file, origfile):
114 def _matcheol(file, origfile):
115 "Convert EOL markers in a file to match origfile"
115 "Convert EOL markers in a file to match origfile"
116 tostyle = _eoltype(util.readfile(origfile))
116 tostyle = _eoltype(util.readfile(origfile))
117 if tostyle:
117 if tostyle:
118 data = util.readfile(file)
118 data = util.readfile(file)
119 style = _eoltype(data)
119 style = _eoltype(data)
120 if style:
120 if style:
121 newdata = data.replace(style, tostyle)
121 newdata = data.replace(style, tostyle)
122 if newdata != data:
122 if newdata != data:
123 util.writefile(file, newdata)
123 util.writefile(file, newdata)
124
124
125 def filemerge(repo, mynode, orig, fcd, fco, fca):
125 def filemerge(repo, mynode, orig, fcd, fco, fca):
126 """perform a 3-way merge in the working directory
126 """perform a 3-way merge in the working directory
127
127
128 mynode = parent node before merge
128 mynode = parent node before merge
129 orig = original local filename before merge
129 orig = original local filename before merge
130 fco = other file context
130 fco = other file context
131 fca = ancestor file context
131 fca = ancestor file context
132 fcd = local file context for current/destination file
132 fcd = local file context for current/destination file
133 """
133 """
134
134
135 def temp(prefix, ctx):
135 def temp(prefix, ctx):
136 pre = "%s~%s." % (os.path.basename(ctx.path()), prefix)
136 pre = "%s~%s." % (os.path.basename(ctx.path()), prefix)
137 (fd, name) = tempfile.mkstemp(prefix=pre)
137 (fd, name) = tempfile.mkstemp(prefix=pre)
138 data = repo.wwritedata(ctx.path(), ctx.data())
138 data = repo.wwritedata(ctx.path(), ctx.data())
139 f = os.fdopen(fd, "wb")
139 f = os.fdopen(fd, "wb")
140 f.write(data)
140 f.write(data)
141 f.close()
141 f.close()
142 return name
142 return name
143
143
144 def isbin(ctx):
144 def isbin(ctx):
145 try:
145 try:
146 return util.binary(ctx.data())
146 return util.binary(ctx.data())
147 except IOError:
147 except IOError:
148 return False
148 return False
149
149
150 if not fco.cmp(fcd): # files identical?
150 if not fco.cmp(fcd): # files identical?
151 return None
151 return None
152
152
153 ui = repo.ui
153 ui = repo.ui
154 fd = fcd.path()
154 fd = fcd.path()
155 binary = isbin(fcd) or isbin(fco) or isbin(fca)
155 binary = isbin(fcd) or isbin(fco) or isbin(fca)
156 symlink = 'l' in fcd.flags() + fco.flags()
156 symlink = 'l' in fcd.flags() + fco.flags()
157 tool, toolpath = _picktool(repo, ui, fd, binary, symlink)
157 tool, toolpath = _picktool(repo, ui, fd, binary, symlink)
158 ui.debug("picked tool '%s' for %s (binary %s symlink %s)\n" %
158 ui.debug("picked tool '%s' for %s (binary %s symlink %s)\n" %
159 (tool, fd, binary, symlink))
159 (tool, fd, binary, symlink))
160
160
161 if not tool or tool == 'internal:prompt':
161 if not tool or tool == 'internal:prompt':
162 tool = "internal:local"
162 tool = "internal:local"
163 if ui.promptchoice(_(" no tool found to merge %s\n"
163 if ui.promptchoice(_(" no tool found to merge %s\n"
164 "keep (l)ocal or take (o)ther?") % fd,
164 "keep (l)ocal or take (o)ther?") % fd,
165 (_("&Local"), _("&Other")), 0):
165 (_("&Local"), _("&Other")), 0):
166 tool = "internal:other"
166 tool = "internal:other"
167 if tool == "internal:local":
167 if tool == "internal:local":
168 return 0
168 return 0
169 if tool == "internal:other":
169 if tool == "internal:other":
170 repo.wwrite(fd, fco.data(), fco.flags())
170 repo.wwrite(fd, fco.data(), fco.flags())
171 return 0
171 return 0
172 if tool == "internal:fail":
172 if tool == "internal:fail":
173 return 1
173 return 1
174
174
175 # do the actual merge
175 # do the actual merge
176 a = repo.wjoin(fd)
176 a = repo.wjoin(fd)
177 b = temp("base", fca)
177 b = temp("base", fca)
178 c = temp("other", fco)
178 c = temp("other", fco)
179 out = ""
179 out = ""
180 back = a + ".orig"
180 back = a + ".orig"
181 util.copyfile(a, back)
181 util.copyfile(a, back)
182
182
183 if orig != fco.path():
183 if orig != fco.path():
184 ui.status(_("merging %s and %s to %s\n") % (orig, fco.path(), fd))
184 ui.status(_("merging %s and %s to %s\n") % (orig, fco.path(), fd))
185 else:
185 else:
186 ui.status(_("merging %s\n") % fd)
186 ui.status(_("merging %s\n") % fd)
187
187
188 ui.debug("my %s other %s ancestor %s\n" % (fcd, fco, fca))
188 ui.debug("my %s other %s ancestor %s\n" % (fcd, fco, fca))
189
189
190 # do we attempt to simplemerge first?
190 # do we attempt to simplemerge first?
191 try:
191 try:
192 premerge = _toolbool(ui, tool, "premerge", not (binary or symlink))
192 premerge = _toolbool(ui, tool, "premerge", not (binary or symlink))
193 except error.ConfigError:
193 except error.ConfigError:
194 premerge = _toolstr(ui, tool, "premerge").lower()
194 premerge = _toolstr(ui, tool, "premerge").lower()
195 valid = 'keep'.split()
195 valid = 'keep'.split()
196 if premerge not in valid:
196 if premerge not in valid:
197 _valid = ', '.join(["'" + v + "'" for v in valid])
197 _valid = ', '.join(["'" + v + "'" for v in valid])
198 raise error.ConfigError(_("%s.premerge not valid "
198 raise error.ConfigError(_("%s.premerge not valid "
199 "('%s' is neither boolean nor %s)") %
199 "('%s' is neither boolean nor %s)") %
200 (tool, premerge, _valid))
200 (tool, premerge, _valid))
201
201
202 if premerge:
202 if premerge:
203 r = simplemerge.simplemerge(ui, a, b, c, quiet=True)
203 r = simplemerge.simplemerge(ui, a, b, c, quiet=True)
204 if not r:
204 if not r:
205 ui.debug(" premerge successful\n")
205 ui.debug(" premerge successful\n")
206 os.unlink(back)
206 os.unlink(back)
207 os.unlink(b)
207 os.unlink(b)
208 os.unlink(c)
208 os.unlink(c)
209 return 0
209 return 0
210 if premerge != 'keep':
210 if premerge != 'keep':
211 util.copyfile(back, a) # restore from backup and try again
211 util.copyfile(back, a) # restore from backup and try again
212
212
213 env = dict(HG_FILE=fd,
213 env = dict(HG_FILE=fd,
214 HG_MY_NODE=short(mynode),
214 HG_MY_NODE=short(mynode),
215 HG_OTHER_NODE=str(fco.changectx()),
215 HG_OTHER_NODE=str(fco.changectx()),
216 HG_BASE_NODE=str(fca.changectx()),
216 HG_BASE_NODE=str(fca.changectx()),
217 HG_MY_ISLINK='l' in fcd.flags(),
217 HG_MY_ISLINK='l' in fcd.flags(),
218 HG_OTHER_ISLINK='l' in fco.flags(),
218 HG_OTHER_ISLINK='l' in fco.flags(),
219 HG_BASE_ISLINK='l' in fca.flags())
219 HG_BASE_ISLINK='l' in fca.flags())
220
220
221 if tool == "internal:merge":
221 if tool == "internal:merge":
222 r = simplemerge.simplemerge(ui, a, b, c, label=['local', 'other'])
222 r = simplemerge.simplemerge(ui, a, b, c, label=['local', 'other'])
223 elif tool == 'internal:dump':
223 elif tool == 'internal:dump':
224 a = repo.wjoin(fd)
224 a = repo.wjoin(fd)
225 util.copyfile(a, a + ".local")
225 util.copyfile(a, a + ".local")
226 repo.wwrite(fd + ".other", fco.data(), fco.flags())
226 repo.wwrite(fd + ".other", fco.data(), fco.flags())
227 repo.wwrite(fd + ".base", fca.data(), fca.flags())
227 repo.wwrite(fd + ".base", fca.data(), fca.flags())
228 return 1 # unresolved
228 return 1 # unresolved
229 else:
229 else:
230 args = _toolstr(ui, tool, "args", '$local $base $other')
230 args = _toolstr(ui, tool, "args", '$local $base $other')
231 if "$output" in args:
231 if "$output" in args:
232 out, a = a, back # read input from backup, write to original
232 out, a = a, back # read input from backup, write to original
233 replace = dict(local=a, base=b, other=c, output=out)
233 replace = dict(local=a, base=b, other=c, output=out)
234 args = util.interpolate(r'\$', replace, args,
234 args = util.interpolate(r'\$', replace, args,
235 lambda s: '"%s"' % util.localpath(s))
235 lambda s: '"%s"' % util.localpath(s))
236 r = util.system(toolpath + ' ' + args, cwd=repo.root, environ=env)
236 r = util.system(toolpath + ' ' + args, cwd=repo.root, environ=env,
237 out=ui.fout)
237
238
238 if not r and (_toolbool(ui, tool, "checkconflicts") or
239 if not r and (_toolbool(ui, tool, "checkconflicts") or
239 'conflicts' in _toollist(ui, tool, "check")):
240 'conflicts' in _toollist(ui, tool, "check")):
240 if re.search("^(<<<<<<< .*|=======|>>>>>>> .*)$", fcd.data(),
241 if re.search("^(<<<<<<< .*|=======|>>>>>>> .*)$", fcd.data(),
241 re.MULTILINE):
242 re.MULTILINE):
242 r = 1
243 r = 1
243
244
244 checked = False
245 checked = False
245 if 'prompt' in _toollist(ui, tool, "check"):
246 if 'prompt' in _toollist(ui, tool, "check"):
246 checked = True
247 checked = True
247 if ui.promptchoice(_("was merge of '%s' successful (yn)?") % fd,
248 if ui.promptchoice(_("was merge of '%s' successful (yn)?") % fd,
248 (_("&Yes"), _("&No")), 1):
249 (_("&Yes"), _("&No")), 1):
249 r = 1
250 r = 1
250
251
251 if not r and not checked and (_toolbool(ui, tool, "checkchanged") or
252 if not r and not checked and (_toolbool(ui, tool, "checkchanged") or
252 'changed' in _toollist(ui, tool, "check")):
253 'changed' in _toollist(ui, tool, "check")):
253 if filecmp.cmp(repo.wjoin(fd), back):
254 if filecmp.cmp(repo.wjoin(fd), back):
254 if ui.promptchoice(_(" output file %s appears unchanged\n"
255 if ui.promptchoice(_(" output file %s appears unchanged\n"
255 "was merge successful (yn)?") % fd,
256 "was merge successful (yn)?") % fd,
256 (_("&Yes"), _("&No")), 1):
257 (_("&Yes"), _("&No")), 1):
257 r = 1
258 r = 1
258
259
259 if _toolbool(ui, tool, "fixeol"):
260 if _toolbool(ui, tool, "fixeol"):
260 _matcheol(repo.wjoin(fd), back)
261 _matcheol(repo.wjoin(fd), back)
261
262
262 if r:
263 if r:
263 ui.warn(_("merging %s failed!\n") % fd)
264 ui.warn(_("merging %s failed!\n") % fd)
264 else:
265 else:
265 os.unlink(back)
266 os.unlink(back)
266
267
267 os.unlink(b)
268 os.unlink(b)
268 os.unlink(c)
269 os.unlink(c)
269 return r
270 return r
@@ -1,1991 +1,1991 b''
1 # localrepo.py - read/write repository class for mercurial
1 # localrepo.py - read/write repository class 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 node import bin, hex, nullid, nullrev, short
8 from node import bin, hex, nullid, nullrev, short
9 from i18n import _
9 from i18n import _
10 import repo, changegroup, subrepo, discovery, pushkey
10 import repo, changegroup, subrepo, discovery, pushkey
11 import changelog, dirstate, filelog, manifest, context, bookmarks
11 import changelog, dirstate, filelog, manifest, context, bookmarks
12 import lock, transaction, store, encoding
12 import lock, transaction, store, encoding
13 import scmutil, util, extensions, hook, error
13 import scmutil, util, extensions, hook, error
14 import match as matchmod
14 import match as matchmod
15 import merge as mergemod
15 import merge as mergemod
16 import tags as tagsmod
16 import tags as tagsmod
17 from lock import release
17 from lock import release
18 import weakref, errno, os, time, inspect
18 import weakref, errno, os, time, inspect
19 propertycache = util.propertycache
19 propertycache = util.propertycache
20
20
21 class localrepository(repo.repository):
21 class localrepository(repo.repository):
22 capabilities = set(('lookup', 'changegroupsubset', 'branchmap', 'pushkey',
22 capabilities = set(('lookup', 'changegroupsubset', 'branchmap', 'pushkey',
23 'known', 'getbundle'))
23 'known', 'getbundle'))
24 supportedformats = set(('revlogv1', 'generaldelta'))
24 supportedformats = set(('revlogv1', 'generaldelta'))
25 supported = supportedformats | set(('store', 'fncache', 'shared',
25 supported = supportedformats | set(('store', 'fncache', 'shared',
26 'dotencode'))
26 'dotencode'))
27
27
28 def __init__(self, baseui, path=None, create=False):
28 def __init__(self, baseui, path=None, create=False):
29 repo.repository.__init__(self)
29 repo.repository.__init__(self)
30 self.root = os.path.realpath(util.expandpath(path))
30 self.root = os.path.realpath(util.expandpath(path))
31 self.path = os.path.join(self.root, ".hg")
31 self.path = os.path.join(self.root, ".hg")
32 self.origroot = path
32 self.origroot = path
33 self.auditor = scmutil.pathauditor(self.root, self._checknested)
33 self.auditor = scmutil.pathauditor(self.root, self._checknested)
34 self.opener = scmutil.opener(self.path)
34 self.opener = scmutil.opener(self.path)
35 self.wopener = scmutil.opener(self.root)
35 self.wopener = scmutil.opener(self.root)
36 self.baseui = baseui
36 self.baseui = baseui
37 self.ui = baseui.copy()
37 self.ui = baseui.copy()
38
38
39 try:
39 try:
40 self.ui.readconfig(self.join("hgrc"), self.root)
40 self.ui.readconfig(self.join("hgrc"), self.root)
41 extensions.loadall(self.ui)
41 extensions.loadall(self.ui)
42 except IOError:
42 except IOError:
43 pass
43 pass
44
44
45 if not os.path.isdir(self.path):
45 if not os.path.isdir(self.path):
46 if create:
46 if create:
47 if not os.path.exists(path):
47 if not os.path.exists(path):
48 util.makedirs(path)
48 util.makedirs(path)
49 util.makedir(self.path, notindexed=True)
49 util.makedir(self.path, notindexed=True)
50 requirements = ["revlogv1"]
50 requirements = ["revlogv1"]
51 if self.ui.configbool('format', 'usestore', True):
51 if self.ui.configbool('format', 'usestore', True):
52 os.mkdir(os.path.join(self.path, "store"))
52 os.mkdir(os.path.join(self.path, "store"))
53 requirements.append("store")
53 requirements.append("store")
54 if self.ui.configbool('format', 'usefncache', True):
54 if self.ui.configbool('format', 'usefncache', True):
55 requirements.append("fncache")
55 requirements.append("fncache")
56 if self.ui.configbool('format', 'dotencode', True):
56 if self.ui.configbool('format', 'dotencode', True):
57 requirements.append('dotencode')
57 requirements.append('dotencode')
58 # create an invalid changelog
58 # create an invalid changelog
59 self.opener.append(
59 self.opener.append(
60 "00changelog.i",
60 "00changelog.i",
61 '\0\0\0\2' # represents revlogv2
61 '\0\0\0\2' # represents revlogv2
62 ' dummy changelog to prevent using the old repo layout'
62 ' dummy changelog to prevent using the old repo layout'
63 )
63 )
64 if self.ui.configbool('format', 'generaldelta', False):
64 if self.ui.configbool('format', 'generaldelta', False):
65 requirements.append("generaldelta")
65 requirements.append("generaldelta")
66 else:
66 else:
67 raise error.RepoError(_("repository %s not found") % path)
67 raise error.RepoError(_("repository %s not found") % path)
68 elif create:
68 elif create:
69 raise error.RepoError(_("repository %s already exists") % path)
69 raise error.RepoError(_("repository %s already exists") % path)
70 else:
70 else:
71 try:
71 try:
72 requirements = scmutil.readrequires(self.opener, self.supported)
72 requirements = scmutil.readrequires(self.opener, self.supported)
73 except IOError, inst:
73 except IOError, inst:
74 if inst.errno != errno.ENOENT:
74 if inst.errno != errno.ENOENT:
75 raise
75 raise
76 requirements = set()
76 requirements = set()
77
77
78 self.sharedpath = self.path
78 self.sharedpath = self.path
79 try:
79 try:
80 s = os.path.realpath(self.opener.read("sharedpath"))
80 s = os.path.realpath(self.opener.read("sharedpath"))
81 if not os.path.exists(s):
81 if not os.path.exists(s):
82 raise error.RepoError(
82 raise error.RepoError(
83 _('.hg/sharedpath points to nonexistent directory %s') % s)
83 _('.hg/sharedpath points to nonexistent directory %s') % s)
84 self.sharedpath = s
84 self.sharedpath = s
85 except IOError, inst:
85 except IOError, inst:
86 if inst.errno != errno.ENOENT:
86 if inst.errno != errno.ENOENT:
87 raise
87 raise
88
88
89 self.store = store.store(requirements, self.sharedpath, scmutil.opener)
89 self.store = store.store(requirements, self.sharedpath, scmutil.opener)
90 self.spath = self.store.path
90 self.spath = self.store.path
91 self.sopener = self.store.opener
91 self.sopener = self.store.opener
92 self.sjoin = self.store.join
92 self.sjoin = self.store.join
93 self.opener.createmode = self.store.createmode
93 self.opener.createmode = self.store.createmode
94 self._applyrequirements(requirements)
94 self._applyrequirements(requirements)
95 if create:
95 if create:
96 self._writerequirements()
96 self._writerequirements()
97
97
98 # These two define the set of tags for this repository. _tags
98 # These two define the set of tags for this repository. _tags
99 # maps tag name to node; _tagtypes maps tag name to 'global' or
99 # maps tag name to node; _tagtypes maps tag name to 'global' or
100 # 'local'. (Global tags are defined by .hgtags across all
100 # 'local'. (Global tags are defined by .hgtags across all
101 # heads, and local tags are defined in .hg/localtags.) They
101 # heads, and local tags are defined in .hg/localtags.) They
102 # constitute the in-memory cache of tags.
102 # constitute the in-memory cache of tags.
103 self._tags = None
103 self._tags = None
104 self._tagtypes = None
104 self._tagtypes = None
105
105
106 self._branchcache = None
106 self._branchcache = None
107 self._branchcachetip = None
107 self._branchcachetip = None
108 self.nodetagscache = None
108 self.nodetagscache = None
109 self.filterpats = {}
109 self.filterpats = {}
110 self._datafilters = {}
110 self._datafilters = {}
111 self._transref = self._lockref = self._wlockref = None
111 self._transref = self._lockref = self._wlockref = None
112
112
113 def _applyrequirements(self, requirements):
113 def _applyrequirements(self, requirements):
114 self.requirements = requirements
114 self.requirements = requirements
115 openerreqs = set(('revlogv1', 'generaldelta'))
115 openerreqs = set(('revlogv1', 'generaldelta'))
116 self.sopener.options = dict((r, 1) for r in requirements
116 self.sopener.options = dict((r, 1) for r in requirements
117 if r in openerreqs)
117 if r in openerreqs)
118
118
119 def _writerequirements(self):
119 def _writerequirements(self):
120 reqfile = self.opener("requires", "w")
120 reqfile = self.opener("requires", "w")
121 for r in self.requirements:
121 for r in self.requirements:
122 reqfile.write("%s\n" % r)
122 reqfile.write("%s\n" % r)
123 reqfile.close()
123 reqfile.close()
124
124
125 def _checknested(self, path):
125 def _checknested(self, path):
126 """Determine if path is a legal nested repository."""
126 """Determine if path is a legal nested repository."""
127 if not path.startswith(self.root):
127 if not path.startswith(self.root):
128 return False
128 return False
129 subpath = path[len(self.root) + 1:]
129 subpath = path[len(self.root) + 1:]
130
130
131 # XXX: Checking against the current working copy is wrong in
131 # XXX: Checking against the current working copy is wrong in
132 # the sense that it can reject things like
132 # the sense that it can reject things like
133 #
133 #
134 # $ hg cat -r 10 sub/x.txt
134 # $ hg cat -r 10 sub/x.txt
135 #
135 #
136 # if sub/ is no longer a subrepository in the working copy
136 # if sub/ is no longer a subrepository in the working copy
137 # parent revision.
137 # parent revision.
138 #
138 #
139 # However, it can of course also allow things that would have
139 # However, it can of course also allow things that would have
140 # been rejected before, such as the above cat command if sub/
140 # been rejected before, such as the above cat command if sub/
141 # is a subrepository now, but was a normal directory before.
141 # is a subrepository now, but was a normal directory before.
142 # The old path auditor would have rejected by mistake since it
142 # The old path auditor would have rejected by mistake since it
143 # panics when it sees sub/.hg/.
143 # panics when it sees sub/.hg/.
144 #
144 #
145 # All in all, checking against the working copy seems sensible
145 # All in all, checking against the working copy seems sensible
146 # since we want to prevent access to nested repositories on
146 # since we want to prevent access to nested repositories on
147 # the filesystem *now*.
147 # the filesystem *now*.
148 ctx = self[None]
148 ctx = self[None]
149 parts = util.splitpath(subpath)
149 parts = util.splitpath(subpath)
150 while parts:
150 while parts:
151 prefix = os.sep.join(parts)
151 prefix = os.sep.join(parts)
152 if prefix in ctx.substate:
152 if prefix in ctx.substate:
153 if prefix == subpath:
153 if prefix == subpath:
154 return True
154 return True
155 else:
155 else:
156 sub = ctx.sub(prefix)
156 sub = ctx.sub(prefix)
157 return sub.checknested(subpath[len(prefix) + 1:])
157 return sub.checknested(subpath[len(prefix) + 1:])
158 else:
158 else:
159 parts.pop()
159 parts.pop()
160 return False
160 return False
161
161
162 @util.propertycache
162 @util.propertycache
163 def _bookmarks(self):
163 def _bookmarks(self):
164 return bookmarks.read(self)
164 return bookmarks.read(self)
165
165
166 @util.propertycache
166 @util.propertycache
167 def _bookmarkcurrent(self):
167 def _bookmarkcurrent(self):
168 return bookmarks.readcurrent(self)
168 return bookmarks.readcurrent(self)
169
169
170 @propertycache
170 @propertycache
171 def changelog(self):
171 def changelog(self):
172 c = changelog.changelog(self.sopener)
172 c = changelog.changelog(self.sopener)
173 if 'HG_PENDING' in os.environ:
173 if 'HG_PENDING' in os.environ:
174 p = os.environ['HG_PENDING']
174 p = os.environ['HG_PENDING']
175 if p.startswith(self.root):
175 if p.startswith(self.root):
176 c.readpending('00changelog.i.a')
176 c.readpending('00changelog.i.a')
177 return c
177 return c
178
178
179 @propertycache
179 @propertycache
180 def manifest(self):
180 def manifest(self):
181 return manifest.manifest(self.sopener)
181 return manifest.manifest(self.sopener)
182
182
183 @propertycache
183 @propertycache
184 def dirstate(self):
184 def dirstate(self):
185 warned = [0]
185 warned = [0]
186 def validate(node):
186 def validate(node):
187 try:
187 try:
188 self.changelog.rev(node)
188 self.changelog.rev(node)
189 return node
189 return node
190 except error.LookupError:
190 except error.LookupError:
191 if not warned[0]:
191 if not warned[0]:
192 warned[0] = True
192 warned[0] = True
193 self.ui.warn(_("warning: ignoring unknown"
193 self.ui.warn(_("warning: ignoring unknown"
194 " working parent %s!\n") % short(node))
194 " working parent %s!\n") % short(node))
195 return nullid
195 return nullid
196
196
197 return dirstate.dirstate(self.opener, self.ui, self.root, validate)
197 return dirstate.dirstate(self.opener, self.ui, self.root, validate)
198
198
199 def __getitem__(self, changeid):
199 def __getitem__(self, changeid):
200 if changeid is None:
200 if changeid is None:
201 return context.workingctx(self)
201 return context.workingctx(self)
202 return context.changectx(self, changeid)
202 return context.changectx(self, changeid)
203
203
204 def __contains__(self, changeid):
204 def __contains__(self, changeid):
205 try:
205 try:
206 return bool(self.lookup(changeid))
206 return bool(self.lookup(changeid))
207 except error.RepoLookupError:
207 except error.RepoLookupError:
208 return False
208 return False
209
209
210 def __nonzero__(self):
210 def __nonzero__(self):
211 return True
211 return True
212
212
213 def __len__(self):
213 def __len__(self):
214 return len(self.changelog)
214 return len(self.changelog)
215
215
216 def __iter__(self):
216 def __iter__(self):
217 for i in xrange(len(self)):
217 for i in xrange(len(self)):
218 yield i
218 yield i
219
219
220 def url(self):
220 def url(self):
221 return 'file:' + self.root
221 return 'file:' + self.root
222
222
223 def hook(self, name, throw=False, **args):
223 def hook(self, name, throw=False, **args):
224 return hook.hook(self.ui, self, name, throw, **args)
224 return hook.hook(self.ui, self, name, throw, **args)
225
225
226 tag_disallowed = ':\r\n'
226 tag_disallowed = ':\r\n'
227
227
228 def _tag(self, names, node, message, local, user, date, extra={}):
228 def _tag(self, names, node, message, local, user, date, extra={}):
229 if isinstance(names, str):
229 if isinstance(names, str):
230 allchars = names
230 allchars = names
231 names = (names,)
231 names = (names,)
232 else:
232 else:
233 allchars = ''.join(names)
233 allchars = ''.join(names)
234 for c in self.tag_disallowed:
234 for c in self.tag_disallowed:
235 if c in allchars:
235 if c in allchars:
236 raise util.Abort(_('%r cannot be used in a tag name') % c)
236 raise util.Abort(_('%r cannot be used in a tag name') % c)
237
237
238 branches = self.branchmap()
238 branches = self.branchmap()
239 for name in names:
239 for name in names:
240 self.hook('pretag', throw=True, node=hex(node), tag=name,
240 self.hook('pretag', throw=True, node=hex(node), tag=name,
241 local=local)
241 local=local)
242 if name in branches:
242 if name in branches:
243 self.ui.warn(_("warning: tag %s conflicts with existing"
243 self.ui.warn(_("warning: tag %s conflicts with existing"
244 " branch name\n") % name)
244 " branch name\n") % name)
245
245
246 def writetags(fp, names, munge, prevtags):
246 def writetags(fp, names, munge, prevtags):
247 fp.seek(0, 2)
247 fp.seek(0, 2)
248 if prevtags and prevtags[-1] != '\n':
248 if prevtags and prevtags[-1] != '\n':
249 fp.write('\n')
249 fp.write('\n')
250 for name in names:
250 for name in names:
251 m = munge and munge(name) or name
251 m = munge and munge(name) or name
252 if self._tagtypes and name in self._tagtypes:
252 if self._tagtypes and name in self._tagtypes:
253 old = self._tags.get(name, nullid)
253 old = self._tags.get(name, nullid)
254 fp.write('%s %s\n' % (hex(old), m))
254 fp.write('%s %s\n' % (hex(old), m))
255 fp.write('%s %s\n' % (hex(node), m))
255 fp.write('%s %s\n' % (hex(node), m))
256 fp.close()
256 fp.close()
257
257
258 prevtags = ''
258 prevtags = ''
259 if local:
259 if local:
260 try:
260 try:
261 fp = self.opener('localtags', 'r+')
261 fp = self.opener('localtags', 'r+')
262 except IOError:
262 except IOError:
263 fp = self.opener('localtags', 'a')
263 fp = self.opener('localtags', 'a')
264 else:
264 else:
265 prevtags = fp.read()
265 prevtags = fp.read()
266
266
267 # local tags are stored in the current charset
267 # local tags are stored in the current charset
268 writetags(fp, names, None, prevtags)
268 writetags(fp, names, None, prevtags)
269 for name in names:
269 for name in names:
270 self.hook('tag', node=hex(node), tag=name, local=local)
270 self.hook('tag', node=hex(node), tag=name, local=local)
271 return
271 return
272
272
273 try:
273 try:
274 fp = self.wfile('.hgtags', 'rb+')
274 fp = self.wfile('.hgtags', 'rb+')
275 except IOError, e:
275 except IOError, e:
276 if e.errno != errno.ENOENT:
276 if e.errno != errno.ENOENT:
277 raise
277 raise
278 fp = self.wfile('.hgtags', 'ab')
278 fp = self.wfile('.hgtags', 'ab')
279 else:
279 else:
280 prevtags = fp.read()
280 prevtags = fp.read()
281
281
282 # committed tags are stored in UTF-8
282 # committed tags are stored in UTF-8
283 writetags(fp, names, encoding.fromlocal, prevtags)
283 writetags(fp, names, encoding.fromlocal, prevtags)
284
284
285 fp.close()
285 fp.close()
286
286
287 if '.hgtags' not in self.dirstate:
287 if '.hgtags' not in self.dirstate:
288 self[None].add(['.hgtags'])
288 self[None].add(['.hgtags'])
289
289
290 m = matchmod.exact(self.root, '', ['.hgtags'])
290 m = matchmod.exact(self.root, '', ['.hgtags'])
291 tagnode = self.commit(message, user, date, extra=extra, match=m)
291 tagnode = self.commit(message, user, date, extra=extra, match=m)
292
292
293 for name in names:
293 for name in names:
294 self.hook('tag', node=hex(node), tag=name, local=local)
294 self.hook('tag', node=hex(node), tag=name, local=local)
295
295
296 return tagnode
296 return tagnode
297
297
298 def tag(self, names, node, message, local, user, date):
298 def tag(self, names, node, message, local, user, date):
299 '''tag a revision with one or more symbolic names.
299 '''tag a revision with one or more symbolic names.
300
300
301 names is a list of strings or, when adding a single tag, names may be a
301 names is a list of strings or, when adding a single tag, names may be a
302 string.
302 string.
303
303
304 if local is True, the tags are stored in a per-repository file.
304 if local is True, the tags are stored in a per-repository file.
305 otherwise, they are stored in the .hgtags file, and a new
305 otherwise, they are stored in the .hgtags file, and a new
306 changeset is committed with the change.
306 changeset is committed with the change.
307
307
308 keyword arguments:
308 keyword arguments:
309
309
310 local: whether to store tags in non-version-controlled file
310 local: whether to store tags in non-version-controlled file
311 (default False)
311 (default False)
312
312
313 message: commit message to use if committing
313 message: commit message to use if committing
314
314
315 user: name of user to use if committing
315 user: name of user to use if committing
316
316
317 date: date tuple to use if committing'''
317 date: date tuple to use if committing'''
318
318
319 if not local:
319 if not local:
320 for x in self.status()[:5]:
320 for x in self.status()[:5]:
321 if '.hgtags' in x:
321 if '.hgtags' in x:
322 raise util.Abort(_('working copy of .hgtags is changed '
322 raise util.Abort(_('working copy of .hgtags is changed '
323 '(please commit .hgtags manually)'))
323 '(please commit .hgtags manually)'))
324
324
325 self.tags() # instantiate the cache
325 self.tags() # instantiate the cache
326 self._tag(names, node, message, local, user, date)
326 self._tag(names, node, message, local, user, date)
327
327
328 def tags(self):
328 def tags(self):
329 '''return a mapping of tag to node'''
329 '''return a mapping of tag to node'''
330 if self._tags is None:
330 if self._tags is None:
331 (self._tags, self._tagtypes) = self._findtags()
331 (self._tags, self._tagtypes) = self._findtags()
332
332
333 return self._tags
333 return self._tags
334
334
335 def _findtags(self):
335 def _findtags(self):
336 '''Do the hard work of finding tags. Return a pair of dicts
336 '''Do the hard work of finding tags. Return a pair of dicts
337 (tags, tagtypes) where tags maps tag name to node, and tagtypes
337 (tags, tagtypes) where tags maps tag name to node, and tagtypes
338 maps tag name to a string like \'global\' or \'local\'.
338 maps tag name to a string like \'global\' or \'local\'.
339 Subclasses or extensions are free to add their own tags, but
339 Subclasses or extensions are free to add their own tags, but
340 should be aware that the returned dicts will be retained for the
340 should be aware that the returned dicts will be retained for the
341 duration of the localrepo object.'''
341 duration of the localrepo object.'''
342
342
343 # XXX what tagtype should subclasses/extensions use? Currently
343 # XXX what tagtype should subclasses/extensions use? Currently
344 # mq and bookmarks add tags, but do not set the tagtype at all.
344 # mq and bookmarks add tags, but do not set the tagtype at all.
345 # Should each extension invent its own tag type? Should there
345 # Should each extension invent its own tag type? Should there
346 # be one tagtype for all such "virtual" tags? Or is the status
346 # be one tagtype for all such "virtual" tags? Or is the status
347 # quo fine?
347 # quo fine?
348
348
349 alltags = {} # map tag name to (node, hist)
349 alltags = {} # map tag name to (node, hist)
350 tagtypes = {}
350 tagtypes = {}
351
351
352 tagsmod.findglobaltags(self.ui, self, alltags, tagtypes)
352 tagsmod.findglobaltags(self.ui, self, alltags, tagtypes)
353 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
353 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
354
354
355 # Build the return dicts. Have to re-encode tag names because
355 # Build the return dicts. Have to re-encode tag names because
356 # the tags module always uses UTF-8 (in order not to lose info
356 # the tags module always uses UTF-8 (in order not to lose info
357 # writing to the cache), but the rest of Mercurial wants them in
357 # writing to the cache), but the rest of Mercurial wants them in
358 # local encoding.
358 # local encoding.
359 tags = {}
359 tags = {}
360 for (name, (node, hist)) in alltags.iteritems():
360 for (name, (node, hist)) in alltags.iteritems():
361 if node != nullid:
361 if node != nullid:
362 try:
362 try:
363 # ignore tags to unknown nodes
363 # ignore tags to unknown nodes
364 self.changelog.lookup(node)
364 self.changelog.lookup(node)
365 tags[encoding.tolocal(name)] = node
365 tags[encoding.tolocal(name)] = node
366 except error.LookupError:
366 except error.LookupError:
367 pass
367 pass
368 tags['tip'] = self.changelog.tip()
368 tags['tip'] = self.changelog.tip()
369 tagtypes = dict([(encoding.tolocal(name), value)
369 tagtypes = dict([(encoding.tolocal(name), value)
370 for (name, value) in tagtypes.iteritems()])
370 for (name, value) in tagtypes.iteritems()])
371 return (tags, tagtypes)
371 return (tags, tagtypes)
372
372
373 def tagtype(self, tagname):
373 def tagtype(self, tagname):
374 '''
374 '''
375 return the type of the given tag. result can be:
375 return the type of the given tag. result can be:
376
376
377 'local' : a local tag
377 'local' : a local tag
378 'global' : a global tag
378 'global' : a global tag
379 None : tag does not exist
379 None : tag does not exist
380 '''
380 '''
381
381
382 self.tags()
382 self.tags()
383
383
384 return self._tagtypes.get(tagname)
384 return self._tagtypes.get(tagname)
385
385
386 def tagslist(self):
386 def tagslist(self):
387 '''return a list of tags ordered by revision'''
387 '''return a list of tags ordered by revision'''
388 l = []
388 l = []
389 for t, n in self.tags().iteritems():
389 for t, n in self.tags().iteritems():
390 r = self.changelog.rev(n)
390 r = self.changelog.rev(n)
391 l.append((r, t, n))
391 l.append((r, t, n))
392 return [(t, n) for r, t, n in sorted(l)]
392 return [(t, n) for r, t, n in sorted(l)]
393
393
394 def nodetags(self, node):
394 def nodetags(self, node):
395 '''return the tags associated with a node'''
395 '''return the tags associated with a node'''
396 if not self.nodetagscache:
396 if not self.nodetagscache:
397 self.nodetagscache = {}
397 self.nodetagscache = {}
398 for t, n in self.tags().iteritems():
398 for t, n in self.tags().iteritems():
399 self.nodetagscache.setdefault(n, []).append(t)
399 self.nodetagscache.setdefault(n, []).append(t)
400 for tags in self.nodetagscache.itervalues():
400 for tags in self.nodetagscache.itervalues():
401 tags.sort()
401 tags.sort()
402 return self.nodetagscache.get(node, [])
402 return self.nodetagscache.get(node, [])
403
403
404 def nodebookmarks(self, node):
404 def nodebookmarks(self, node):
405 marks = []
405 marks = []
406 for bookmark, n in self._bookmarks.iteritems():
406 for bookmark, n in self._bookmarks.iteritems():
407 if n == node:
407 if n == node:
408 marks.append(bookmark)
408 marks.append(bookmark)
409 return sorted(marks)
409 return sorted(marks)
410
410
411 def _branchtags(self, partial, lrev):
411 def _branchtags(self, partial, lrev):
412 # TODO: rename this function?
412 # TODO: rename this function?
413 tiprev = len(self) - 1
413 tiprev = len(self) - 1
414 if lrev != tiprev:
414 if lrev != tiprev:
415 ctxgen = (self[r] for r in xrange(lrev + 1, tiprev + 1))
415 ctxgen = (self[r] for r in xrange(lrev + 1, tiprev + 1))
416 self._updatebranchcache(partial, ctxgen)
416 self._updatebranchcache(partial, ctxgen)
417 self._writebranchcache(partial, self.changelog.tip(), tiprev)
417 self._writebranchcache(partial, self.changelog.tip(), tiprev)
418
418
419 return partial
419 return partial
420
420
421 def updatebranchcache(self):
421 def updatebranchcache(self):
422 tip = self.changelog.tip()
422 tip = self.changelog.tip()
423 if self._branchcache is not None and self._branchcachetip == tip:
423 if self._branchcache is not None and self._branchcachetip == tip:
424 return self._branchcache
424 return self._branchcache
425
425
426 oldtip = self._branchcachetip
426 oldtip = self._branchcachetip
427 self._branchcachetip = tip
427 self._branchcachetip = tip
428 if oldtip is None or oldtip not in self.changelog.nodemap:
428 if oldtip is None or oldtip not in self.changelog.nodemap:
429 partial, last, lrev = self._readbranchcache()
429 partial, last, lrev = self._readbranchcache()
430 else:
430 else:
431 lrev = self.changelog.rev(oldtip)
431 lrev = self.changelog.rev(oldtip)
432 partial = self._branchcache
432 partial = self._branchcache
433
433
434 self._branchtags(partial, lrev)
434 self._branchtags(partial, lrev)
435 # this private cache holds all heads (not just tips)
435 # this private cache holds all heads (not just tips)
436 self._branchcache = partial
436 self._branchcache = partial
437
437
438 def branchmap(self):
438 def branchmap(self):
439 '''returns a dictionary {branch: [branchheads]}'''
439 '''returns a dictionary {branch: [branchheads]}'''
440 self.updatebranchcache()
440 self.updatebranchcache()
441 return self._branchcache
441 return self._branchcache
442
442
443 def branchtags(self):
443 def branchtags(self):
444 '''return a dict where branch names map to the tipmost head of
444 '''return a dict where branch names map to the tipmost head of
445 the branch, open heads come before closed'''
445 the branch, open heads come before closed'''
446 bt = {}
446 bt = {}
447 for bn, heads in self.branchmap().iteritems():
447 for bn, heads in self.branchmap().iteritems():
448 tip = heads[-1]
448 tip = heads[-1]
449 for h in reversed(heads):
449 for h in reversed(heads):
450 if 'close' not in self.changelog.read(h)[5]:
450 if 'close' not in self.changelog.read(h)[5]:
451 tip = h
451 tip = h
452 break
452 break
453 bt[bn] = tip
453 bt[bn] = tip
454 return bt
454 return bt
455
455
456 def _readbranchcache(self):
456 def _readbranchcache(self):
457 partial = {}
457 partial = {}
458 try:
458 try:
459 f = self.opener("cache/branchheads")
459 f = self.opener("cache/branchheads")
460 lines = f.read().split('\n')
460 lines = f.read().split('\n')
461 f.close()
461 f.close()
462 except (IOError, OSError):
462 except (IOError, OSError):
463 return {}, nullid, nullrev
463 return {}, nullid, nullrev
464
464
465 try:
465 try:
466 last, lrev = lines.pop(0).split(" ", 1)
466 last, lrev = lines.pop(0).split(" ", 1)
467 last, lrev = bin(last), int(lrev)
467 last, lrev = bin(last), int(lrev)
468 if lrev >= len(self) or self[lrev].node() != last:
468 if lrev >= len(self) or self[lrev].node() != last:
469 # invalidate the cache
469 # invalidate the cache
470 raise ValueError('invalidating branch cache (tip differs)')
470 raise ValueError('invalidating branch cache (tip differs)')
471 for l in lines:
471 for l in lines:
472 if not l:
472 if not l:
473 continue
473 continue
474 node, label = l.split(" ", 1)
474 node, label = l.split(" ", 1)
475 label = encoding.tolocal(label.strip())
475 label = encoding.tolocal(label.strip())
476 partial.setdefault(label, []).append(bin(node))
476 partial.setdefault(label, []).append(bin(node))
477 except KeyboardInterrupt:
477 except KeyboardInterrupt:
478 raise
478 raise
479 except Exception, inst:
479 except Exception, inst:
480 if self.ui.debugflag:
480 if self.ui.debugflag:
481 self.ui.warn(str(inst), '\n')
481 self.ui.warn(str(inst), '\n')
482 partial, last, lrev = {}, nullid, nullrev
482 partial, last, lrev = {}, nullid, nullrev
483 return partial, last, lrev
483 return partial, last, lrev
484
484
485 def _writebranchcache(self, branches, tip, tiprev):
485 def _writebranchcache(self, branches, tip, tiprev):
486 try:
486 try:
487 f = self.opener("cache/branchheads", "w", atomictemp=True)
487 f = self.opener("cache/branchheads", "w", atomictemp=True)
488 f.write("%s %s\n" % (hex(tip), tiprev))
488 f.write("%s %s\n" % (hex(tip), tiprev))
489 for label, nodes in branches.iteritems():
489 for label, nodes in branches.iteritems():
490 for node in nodes:
490 for node in nodes:
491 f.write("%s %s\n" % (hex(node), encoding.fromlocal(label)))
491 f.write("%s %s\n" % (hex(node), encoding.fromlocal(label)))
492 f.rename()
492 f.rename()
493 except (IOError, OSError):
493 except (IOError, OSError):
494 pass
494 pass
495
495
496 def _updatebranchcache(self, partial, ctxgen):
496 def _updatebranchcache(self, partial, ctxgen):
497 # collect new branch entries
497 # collect new branch entries
498 newbranches = {}
498 newbranches = {}
499 for c in ctxgen:
499 for c in ctxgen:
500 newbranches.setdefault(c.branch(), []).append(c.node())
500 newbranches.setdefault(c.branch(), []).append(c.node())
501 # if older branchheads are reachable from new ones, they aren't
501 # if older branchheads are reachable from new ones, they aren't
502 # really branchheads. Note checking parents is insufficient:
502 # really branchheads. Note checking parents is insufficient:
503 # 1 (branch a) -> 2 (branch b) -> 3 (branch a)
503 # 1 (branch a) -> 2 (branch b) -> 3 (branch a)
504 for branch, newnodes in newbranches.iteritems():
504 for branch, newnodes in newbranches.iteritems():
505 bheads = partial.setdefault(branch, [])
505 bheads = partial.setdefault(branch, [])
506 bheads.extend(newnodes)
506 bheads.extend(newnodes)
507 if len(bheads) <= 1:
507 if len(bheads) <= 1:
508 continue
508 continue
509 bheads = sorted(bheads, key=lambda x: self[x].rev())
509 bheads = sorted(bheads, key=lambda x: self[x].rev())
510 # starting from tip means fewer passes over reachable
510 # starting from tip means fewer passes over reachable
511 while newnodes:
511 while newnodes:
512 latest = newnodes.pop()
512 latest = newnodes.pop()
513 if latest not in bheads:
513 if latest not in bheads:
514 continue
514 continue
515 minbhrev = self[bheads[0]].node()
515 minbhrev = self[bheads[0]].node()
516 reachable = self.changelog.reachable(latest, minbhrev)
516 reachable = self.changelog.reachable(latest, minbhrev)
517 reachable.remove(latest)
517 reachable.remove(latest)
518 if reachable:
518 if reachable:
519 bheads = [b for b in bheads if b not in reachable]
519 bheads = [b for b in bheads if b not in reachable]
520 partial[branch] = bheads
520 partial[branch] = bheads
521
521
522 def lookup(self, key):
522 def lookup(self, key):
523 if isinstance(key, int):
523 if isinstance(key, int):
524 return self.changelog.node(key)
524 return self.changelog.node(key)
525 elif key == '.':
525 elif key == '.':
526 return self.dirstate.p1()
526 return self.dirstate.p1()
527 elif key == 'null':
527 elif key == 'null':
528 return nullid
528 return nullid
529 elif key == 'tip':
529 elif key == 'tip':
530 return self.changelog.tip()
530 return self.changelog.tip()
531 n = self.changelog._match(key)
531 n = self.changelog._match(key)
532 if n:
532 if n:
533 return n
533 return n
534 if key in self._bookmarks:
534 if key in self._bookmarks:
535 return self._bookmarks[key]
535 return self._bookmarks[key]
536 if key in self.tags():
536 if key in self.tags():
537 return self.tags()[key]
537 return self.tags()[key]
538 if key in self.branchtags():
538 if key in self.branchtags():
539 return self.branchtags()[key]
539 return self.branchtags()[key]
540 n = self.changelog._partialmatch(key)
540 n = self.changelog._partialmatch(key)
541 if n:
541 if n:
542 return n
542 return n
543
543
544 # can't find key, check if it might have come from damaged dirstate
544 # can't find key, check if it might have come from damaged dirstate
545 if key in self.dirstate.parents():
545 if key in self.dirstate.parents():
546 raise error.Abort(_("working directory has unknown parent '%s'!")
546 raise error.Abort(_("working directory has unknown parent '%s'!")
547 % short(key))
547 % short(key))
548 try:
548 try:
549 if len(key) == 20:
549 if len(key) == 20:
550 key = hex(key)
550 key = hex(key)
551 except TypeError:
551 except TypeError:
552 pass
552 pass
553 raise error.RepoLookupError(_("unknown revision '%s'") % key)
553 raise error.RepoLookupError(_("unknown revision '%s'") % key)
554
554
555 def lookupbranch(self, key, remote=None):
555 def lookupbranch(self, key, remote=None):
556 repo = remote or self
556 repo = remote or self
557 if key in repo.branchmap():
557 if key in repo.branchmap():
558 return key
558 return key
559
559
560 repo = (remote and remote.local()) and remote or self
560 repo = (remote and remote.local()) and remote or self
561 return repo[key].branch()
561 return repo[key].branch()
562
562
563 def known(self, nodes):
563 def known(self, nodes):
564 nm = self.changelog.nodemap
564 nm = self.changelog.nodemap
565 return [(n in nm) for n in nodes]
565 return [(n in nm) for n in nodes]
566
566
567 def local(self):
567 def local(self):
568 return self
568 return self
569
569
570 def join(self, f):
570 def join(self, f):
571 return os.path.join(self.path, f)
571 return os.path.join(self.path, f)
572
572
573 def wjoin(self, f):
573 def wjoin(self, f):
574 return os.path.join(self.root, f)
574 return os.path.join(self.root, f)
575
575
576 def file(self, f):
576 def file(self, f):
577 if f[0] == '/':
577 if f[0] == '/':
578 f = f[1:]
578 f = f[1:]
579 return filelog.filelog(self.sopener, f)
579 return filelog.filelog(self.sopener, f)
580
580
581 def changectx(self, changeid):
581 def changectx(self, changeid):
582 return self[changeid]
582 return self[changeid]
583
583
584 def parents(self, changeid=None):
584 def parents(self, changeid=None):
585 '''get list of changectxs for parents of changeid'''
585 '''get list of changectxs for parents of changeid'''
586 return self[changeid].parents()
586 return self[changeid].parents()
587
587
588 def filectx(self, path, changeid=None, fileid=None):
588 def filectx(self, path, changeid=None, fileid=None):
589 """changeid can be a changeset revision, node, or tag.
589 """changeid can be a changeset revision, node, or tag.
590 fileid can be a file revision or node."""
590 fileid can be a file revision or node."""
591 return context.filectx(self, path, changeid, fileid)
591 return context.filectx(self, path, changeid, fileid)
592
592
593 def getcwd(self):
593 def getcwd(self):
594 return self.dirstate.getcwd()
594 return self.dirstate.getcwd()
595
595
596 def pathto(self, f, cwd=None):
596 def pathto(self, f, cwd=None):
597 return self.dirstate.pathto(f, cwd)
597 return self.dirstate.pathto(f, cwd)
598
598
599 def wfile(self, f, mode='r'):
599 def wfile(self, f, mode='r'):
600 return self.wopener(f, mode)
600 return self.wopener(f, mode)
601
601
602 def _link(self, f):
602 def _link(self, f):
603 return os.path.islink(self.wjoin(f))
603 return os.path.islink(self.wjoin(f))
604
604
605 def _loadfilter(self, filter):
605 def _loadfilter(self, filter):
606 if filter not in self.filterpats:
606 if filter not in self.filterpats:
607 l = []
607 l = []
608 for pat, cmd in self.ui.configitems(filter):
608 for pat, cmd in self.ui.configitems(filter):
609 if cmd == '!':
609 if cmd == '!':
610 continue
610 continue
611 mf = matchmod.match(self.root, '', [pat])
611 mf = matchmod.match(self.root, '', [pat])
612 fn = None
612 fn = None
613 params = cmd
613 params = cmd
614 for name, filterfn in self._datafilters.iteritems():
614 for name, filterfn in self._datafilters.iteritems():
615 if cmd.startswith(name):
615 if cmd.startswith(name):
616 fn = filterfn
616 fn = filterfn
617 params = cmd[len(name):].lstrip()
617 params = cmd[len(name):].lstrip()
618 break
618 break
619 if not fn:
619 if not fn:
620 fn = lambda s, c, **kwargs: util.filter(s, c)
620 fn = lambda s, c, **kwargs: util.filter(s, c)
621 # Wrap old filters not supporting keyword arguments
621 # Wrap old filters not supporting keyword arguments
622 if not inspect.getargspec(fn)[2]:
622 if not inspect.getargspec(fn)[2]:
623 oldfn = fn
623 oldfn = fn
624 fn = lambda s, c, **kwargs: oldfn(s, c)
624 fn = lambda s, c, **kwargs: oldfn(s, c)
625 l.append((mf, fn, params))
625 l.append((mf, fn, params))
626 self.filterpats[filter] = l
626 self.filterpats[filter] = l
627 return self.filterpats[filter]
627 return self.filterpats[filter]
628
628
629 def _filter(self, filterpats, filename, data):
629 def _filter(self, filterpats, filename, data):
630 for mf, fn, cmd in filterpats:
630 for mf, fn, cmd in filterpats:
631 if mf(filename):
631 if mf(filename):
632 self.ui.debug("filtering %s through %s\n" % (filename, cmd))
632 self.ui.debug("filtering %s through %s\n" % (filename, cmd))
633 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
633 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
634 break
634 break
635
635
636 return data
636 return data
637
637
638 @propertycache
638 @propertycache
639 def _encodefilterpats(self):
639 def _encodefilterpats(self):
640 return self._loadfilter('encode')
640 return self._loadfilter('encode')
641
641
642 @propertycache
642 @propertycache
643 def _decodefilterpats(self):
643 def _decodefilterpats(self):
644 return self._loadfilter('decode')
644 return self._loadfilter('decode')
645
645
646 def adddatafilter(self, name, filter):
646 def adddatafilter(self, name, filter):
647 self._datafilters[name] = filter
647 self._datafilters[name] = filter
648
648
649 def wread(self, filename):
649 def wread(self, filename):
650 if self._link(filename):
650 if self._link(filename):
651 data = os.readlink(self.wjoin(filename))
651 data = os.readlink(self.wjoin(filename))
652 else:
652 else:
653 data = self.wopener.read(filename)
653 data = self.wopener.read(filename)
654 return self._filter(self._encodefilterpats, filename, data)
654 return self._filter(self._encodefilterpats, filename, data)
655
655
656 def wwrite(self, filename, data, flags):
656 def wwrite(self, filename, data, flags):
657 data = self._filter(self._decodefilterpats, filename, data)
657 data = self._filter(self._decodefilterpats, filename, data)
658 if 'l' in flags:
658 if 'l' in flags:
659 self.wopener.symlink(data, filename)
659 self.wopener.symlink(data, filename)
660 else:
660 else:
661 self.wopener.write(filename, data)
661 self.wopener.write(filename, data)
662 if 'x' in flags:
662 if 'x' in flags:
663 util.setflags(self.wjoin(filename), False, True)
663 util.setflags(self.wjoin(filename), False, True)
664
664
665 def wwritedata(self, filename, data):
665 def wwritedata(self, filename, data):
666 return self._filter(self._decodefilterpats, filename, data)
666 return self._filter(self._decodefilterpats, filename, data)
667
667
668 def transaction(self, desc):
668 def transaction(self, desc):
669 tr = self._transref and self._transref() or None
669 tr = self._transref and self._transref() or None
670 if tr and tr.running():
670 if tr and tr.running():
671 return tr.nest()
671 return tr.nest()
672
672
673 # abort here if the journal already exists
673 # abort here if the journal already exists
674 if os.path.exists(self.sjoin("journal")):
674 if os.path.exists(self.sjoin("journal")):
675 raise error.RepoError(
675 raise error.RepoError(
676 _("abandoned transaction found - run hg recover"))
676 _("abandoned transaction found - run hg recover"))
677
677
678 journalfiles = self._writejournal(desc)
678 journalfiles = self._writejournal(desc)
679 renames = [(x, undoname(x)) for x in journalfiles]
679 renames = [(x, undoname(x)) for x in journalfiles]
680
680
681 tr = transaction.transaction(self.ui.warn, self.sopener,
681 tr = transaction.transaction(self.ui.warn, self.sopener,
682 self.sjoin("journal"),
682 self.sjoin("journal"),
683 aftertrans(renames),
683 aftertrans(renames),
684 self.store.createmode)
684 self.store.createmode)
685 self._transref = weakref.ref(tr)
685 self._transref = weakref.ref(tr)
686 return tr
686 return tr
687
687
688 def _writejournal(self, desc):
688 def _writejournal(self, desc):
689 # save dirstate for rollback
689 # save dirstate for rollback
690 try:
690 try:
691 ds = self.opener.read("dirstate")
691 ds = self.opener.read("dirstate")
692 except IOError:
692 except IOError:
693 ds = ""
693 ds = ""
694 self.opener.write("journal.dirstate", ds)
694 self.opener.write("journal.dirstate", ds)
695 self.opener.write("journal.branch",
695 self.opener.write("journal.branch",
696 encoding.fromlocal(self.dirstate.branch()))
696 encoding.fromlocal(self.dirstate.branch()))
697 self.opener.write("journal.desc",
697 self.opener.write("journal.desc",
698 "%d\n%s\n" % (len(self), desc))
698 "%d\n%s\n" % (len(self), desc))
699
699
700 bkname = self.join('bookmarks')
700 bkname = self.join('bookmarks')
701 if os.path.exists(bkname):
701 if os.path.exists(bkname):
702 util.copyfile(bkname, self.join('journal.bookmarks'))
702 util.copyfile(bkname, self.join('journal.bookmarks'))
703 else:
703 else:
704 self.opener.write('journal.bookmarks', '')
704 self.opener.write('journal.bookmarks', '')
705
705
706 return (self.sjoin('journal'), self.join('journal.dirstate'),
706 return (self.sjoin('journal'), self.join('journal.dirstate'),
707 self.join('journal.branch'), self.join('journal.desc'),
707 self.join('journal.branch'), self.join('journal.desc'),
708 self.join('journal.bookmarks'))
708 self.join('journal.bookmarks'))
709
709
710 def recover(self):
710 def recover(self):
711 lock = self.lock()
711 lock = self.lock()
712 try:
712 try:
713 if os.path.exists(self.sjoin("journal")):
713 if os.path.exists(self.sjoin("journal")):
714 self.ui.status(_("rolling back interrupted transaction\n"))
714 self.ui.status(_("rolling back interrupted transaction\n"))
715 transaction.rollback(self.sopener, self.sjoin("journal"),
715 transaction.rollback(self.sopener, self.sjoin("journal"),
716 self.ui.warn)
716 self.ui.warn)
717 self.invalidate()
717 self.invalidate()
718 return True
718 return True
719 else:
719 else:
720 self.ui.warn(_("no interrupted transaction available\n"))
720 self.ui.warn(_("no interrupted transaction available\n"))
721 return False
721 return False
722 finally:
722 finally:
723 lock.release()
723 lock.release()
724
724
725 def rollback(self, dryrun=False):
725 def rollback(self, dryrun=False):
726 wlock = lock = None
726 wlock = lock = None
727 try:
727 try:
728 wlock = self.wlock()
728 wlock = self.wlock()
729 lock = self.lock()
729 lock = self.lock()
730 if os.path.exists(self.sjoin("undo")):
730 if os.path.exists(self.sjoin("undo")):
731 try:
731 try:
732 args = self.opener.read("undo.desc").splitlines()
732 args = self.opener.read("undo.desc").splitlines()
733 if len(args) >= 3 and self.ui.verbose:
733 if len(args) >= 3 and self.ui.verbose:
734 desc = _("repository tip rolled back to revision %s"
734 desc = _("repository tip rolled back to revision %s"
735 " (undo %s: %s)\n") % (
735 " (undo %s: %s)\n") % (
736 int(args[0]) - 1, args[1], args[2])
736 int(args[0]) - 1, args[1], args[2])
737 elif len(args) >= 2:
737 elif len(args) >= 2:
738 desc = _("repository tip rolled back to revision %s"
738 desc = _("repository tip rolled back to revision %s"
739 " (undo %s)\n") % (
739 " (undo %s)\n") % (
740 int(args[0]) - 1, args[1])
740 int(args[0]) - 1, args[1])
741 except IOError:
741 except IOError:
742 desc = _("rolling back unknown transaction\n")
742 desc = _("rolling back unknown transaction\n")
743 self.ui.status(desc)
743 self.ui.status(desc)
744 if dryrun:
744 if dryrun:
745 return
745 return
746 transaction.rollback(self.sopener, self.sjoin("undo"),
746 transaction.rollback(self.sopener, self.sjoin("undo"),
747 self.ui.warn)
747 self.ui.warn)
748 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
748 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
749 if os.path.exists(self.join('undo.bookmarks')):
749 if os.path.exists(self.join('undo.bookmarks')):
750 util.rename(self.join('undo.bookmarks'),
750 util.rename(self.join('undo.bookmarks'),
751 self.join('bookmarks'))
751 self.join('bookmarks'))
752 try:
752 try:
753 branch = self.opener.read("undo.branch")
753 branch = self.opener.read("undo.branch")
754 self.dirstate.setbranch(branch)
754 self.dirstate.setbranch(branch)
755 except IOError:
755 except IOError:
756 self.ui.warn(_("named branch could not be reset, "
756 self.ui.warn(_("named branch could not be reset, "
757 "current branch is still: %s\n")
757 "current branch is still: %s\n")
758 % self.dirstate.branch())
758 % self.dirstate.branch())
759 self.invalidate()
759 self.invalidate()
760 self.dirstate.invalidate()
760 self.dirstate.invalidate()
761 self.destroyed()
761 self.destroyed()
762 parents = tuple([p.rev() for p in self.parents()])
762 parents = tuple([p.rev() for p in self.parents()])
763 if len(parents) > 1:
763 if len(parents) > 1:
764 self.ui.status(_("working directory now based on "
764 self.ui.status(_("working directory now based on "
765 "revisions %d and %d\n") % parents)
765 "revisions %d and %d\n") % parents)
766 else:
766 else:
767 self.ui.status(_("working directory now based on "
767 self.ui.status(_("working directory now based on "
768 "revision %d\n") % parents)
768 "revision %d\n") % parents)
769 else:
769 else:
770 self.ui.warn(_("no rollback information available\n"))
770 self.ui.warn(_("no rollback information available\n"))
771 return 1
771 return 1
772 finally:
772 finally:
773 release(lock, wlock)
773 release(lock, wlock)
774
774
775 def invalidatecaches(self):
775 def invalidatecaches(self):
776 self._tags = None
776 self._tags = None
777 self._tagtypes = None
777 self._tagtypes = None
778 self.nodetagscache = None
778 self.nodetagscache = None
779 self._branchcache = None # in UTF-8
779 self._branchcache = None # in UTF-8
780 self._branchcachetip = None
780 self._branchcachetip = None
781
781
782 def invalidate(self):
782 def invalidate(self):
783 for a in ("changelog", "manifest", "_bookmarks", "_bookmarkcurrent"):
783 for a in ("changelog", "manifest", "_bookmarks", "_bookmarkcurrent"):
784 if a in self.__dict__:
784 if a in self.__dict__:
785 delattr(self, a)
785 delattr(self, a)
786 self.invalidatecaches()
786 self.invalidatecaches()
787
787
788 def _lock(self, lockname, wait, releasefn, acquirefn, desc):
788 def _lock(self, lockname, wait, releasefn, acquirefn, desc):
789 try:
789 try:
790 l = lock.lock(lockname, 0, releasefn, desc=desc)
790 l = lock.lock(lockname, 0, releasefn, desc=desc)
791 except error.LockHeld, inst:
791 except error.LockHeld, inst:
792 if not wait:
792 if not wait:
793 raise
793 raise
794 self.ui.warn(_("waiting for lock on %s held by %r\n") %
794 self.ui.warn(_("waiting for lock on %s held by %r\n") %
795 (desc, inst.locker))
795 (desc, inst.locker))
796 # default to 600 seconds timeout
796 # default to 600 seconds timeout
797 l = lock.lock(lockname, int(self.ui.config("ui", "timeout", "600")),
797 l = lock.lock(lockname, int(self.ui.config("ui", "timeout", "600")),
798 releasefn, desc=desc)
798 releasefn, desc=desc)
799 if acquirefn:
799 if acquirefn:
800 acquirefn()
800 acquirefn()
801 return l
801 return l
802
802
803 def lock(self, wait=True):
803 def lock(self, wait=True):
804 '''Lock the repository store (.hg/store) and return a weak reference
804 '''Lock the repository store (.hg/store) and return a weak reference
805 to the lock. Use this before modifying the store (e.g. committing or
805 to the lock. Use this before modifying the store (e.g. committing or
806 stripping). If you are opening a transaction, get a lock as well.)'''
806 stripping). If you are opening a transaction, get a lock as well.)'''
807 l = self._lockref and self._lockref()
807 l = self._lockref and self._lockref()
808 if l is not None and l.held:
808 if l is not None and l.held:
809 l.lock()
809 l.lock()
810 return l
810 return l
811
811
812 l = self._lock(self.sjoin("lock"), wait, self.store.write,
812 l = self._lock(self.sjoin("lock"), wait, self.store.write,
813 self.invalidate, _('repository %s') % self.origroot)
813 self.invalidate, _('repository %s') % self.origroot)
814 self._lockref = weakref.ref(l)
814 self._lockref = weakref.ref(l)
815 return l
815 return l
816
816
817 def wlock(self, wait=True):
817 def wlock(self, wait=True):
818 '''Lock the non-store parts of the repository (everything under
818 '''Lock the non-store parts of the repository (everything under
819 .hg except .hg/store) and return a weak reference to the lock.
819 .hg except .hg/store) and return a weak reference to the lock.
820 Use this before modifying files in .hg.'''
820 Use this before modifying files in .hg.'''
821 l = self._wlockref and self._wlockref()
821 l = self._wlockref and self._wlockref()
822 if l is not None and l.held:
822 if l is not None and l.held:
823 l.lock()
823 l.lock()
824 return l
824 return l
825
825
826 l = self._lock(self.join("wlock"), wait, self.dirstate.write,
826 l = self._lock(self.join("wlock"), wait, self.dirstate.write,
827 self.dirstate.invalidate, _('working directory of %s') %
827 self.dirstate.invalidate, _('working directory of %s') %
828 self.origroot)
828 self.origroot)
829 self._wlockref = weakref.ref(l)
829 self._wlockref = weakref.ref(l)
830 return l
830 return l
831
831
832 def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist):
832 def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist):
833 """
833 """
834 commit an individual file as part of a larger transaction
834 commit an individual file as part of a larger transaction
835 """
835 """
836
836
837 fname = fctx.path()
837 fname = fctx.path()
838 text = fctx.data()
838 text = fctx.data()
839 flog = self.file(fname)
839 flog = self.file(fname)
840 fparent1 = manifest1.get(fname, nullid)
840 fparent1 = manifest1.get(fname, nullid)
841 fparent2 = fparent2o = manifest2.get(fname, nullid)
841 fparent2 = fparent2o = manifest2.get(fname, nullid)
842
842
843 meta = {}
843 meta = {}
844 copy = fctx.renamed()
844 copy = fctx.renamed()
845 if copy and copy[0] != fname:
845 if copy and copy[0] != fname:
846 # Mark the new revision of this file as a copy of another
846 # Mark the new revision of this file as a copy of another
847 # file. This copy data will effectively act as a parent
847 # file. This copy data will effectively act as a parent
848 # of this new revision. If this is a merge, the first
848 # of this new revision. If this is a merge, the first
849 # parent will be the nullid (meaning "look up the copy data")
849 # parent will be the nullid (meaning "look up the copy data")
850 # and the second one will be the other parent. For example:
850 # and the second one will be the other parent. For example:
851 #
851 #
852 # 0 --- 1 --- 3 rev1 changes file foo
852 # 0 --- 1 --- 3 rev1 changes file foo
853 # \ / rev2 renames foo to bar and changes it
853 # \ / rev2 renames foo to bar and changes it
854 # \- 2 -/ rev3 should have bar with all changes and
854 # \- 2 -/ rev3 should have bar with all changes and
855 # should record that bar descends from
855 # should record that bar descends from
856 # bar in rev2 and foo in rev1
856 # bar in rev2 and foo in rev1
857 #
857 #
858 # this allows this merge to succeed:
858 # this allows this merge to succeed:
859 #
859 #
860 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
860 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
861 # \ / merging rev3 and rev4 should use bar@rev2
861 # \ / merging rev3 and rev4 should use bar@rev2
862 # \- 2 --- 4 as the merge base
862 # \- 2 --- 4 as the merge base
863 #
863 #
864
864
865 cfname = copy[0]
865 cfname = copy[0]
866 crev = manifest1.get(cfname)
866 crev = manifest1.get(cfname)
867 newfparent = fparent2
867 newfparent = fparent2
868
868
869 if manifest2: # branch merge
869 if manifest2: # branch merge
870 if fparent2 == nullid or crev is None: # copied on remote side
870 if fparent2 == nullid or crev is None: # copied on remote side
871 if cfname in manifest2:
871 if cfname in manifest2:
872 crev = manifest2[cfname]
872 crev = manifest2[cfname]
873 newfparent = fparent1
873 newfparent = fparent1
874
874
875 # find source in nearest ancestor if we've lost track
875 # find source in nearest ancestor if we've lost track
876 if not crev:
876 if not crev:
877 self.ui.debug(" %s: searching for copy revision for %s\n" %
877 self.ui.debug(" %s: searching for copy revision for %s\n" %
878 (fname, cfname))
878 (fname, cfname))
879 for ancestor in self[None].ancestors():
879 for ancestor in self[None].ancestors():
880 if cfname in ancestor:
880 if cfname in ancestor:
881 crev = ancestor[cfname].filenode()
881 crev = ancestor[cfname].filenode()
882 break
882 break
883
883
884 if crev:
884 if crev:
885 self.ui.debug(" %s: copy %s:%s\n" % (fname, cfname, hex(crev)))
885 self.ui.debug(" %s: copy %s:%s\n" % (fname, cfname, hex(crev)))
886 meta["copy"] = cfname
886 meta["copy"] = cfname
887 meta["copyrev"] = hex(crev)
887 meta["copyrev"] = hex(crev)
888 fparent1, fparent2 = nullid, newfparent
888 fparent1, fparent2 = nullid, newfparent
889 else:
889 else:
890 self.ui.warn(_("warning: can't find ancestor for '%s' "
890 self.ui.warn(_("warning: can't find ancestor for '%s' "
891 "copied from '%s'!\n") % (fname, cfname))
891 "copied from '%s'!\n") % (fname, cfname))
892
892
893 elif fparent2 != nullid:
893 elif fparent2 != nullid:
894 # is one parent an ancestor of the other?
894 # is one parent an ancestor of the other?
895 fparentancestor = flog.ancestor(fparent1, fparent2)
895 fparentancestor = flog.ancestor(fparent1, fparent2)
896 if fparentancestor == fparent1:
896 if fparentancestor == fparent1:
897 fparent1, fparent2 = fparent2, nullid
897 fparent1, fparent2 = fparent2, nullid
898 elif fparentancestor == fparent2:
898 elif fparentancestor == fparent2:
899 fparent2 = nullid
899 fparent2 = nullid
900
900
901 # is the file changed?
901 # is the file changed?
902 if fparent2 != nullid or flog.cmp(fparent1, text) or meta:
902 if fparent2 != nullid or flog.cmp(fparent1, text) or meta:
903 changelist.append(fname)
903 changelist.append(fname)
904 return flog.add(text, meta, tr, linkrev, fparent1, fparent2)
904 return flog.add(text, meta, tr, linkrev, fparent1, fparent2)
905
905
906 # are just the flags changed during merge?
906 # are just the flags changed during merge?
907 if fparent1 != fparent2o and manifest1.flags(fname) != fctx.flags():
907 if fparent1 != fparent2o and manifest1.flags(fname) != fctx.flags():
908 changelist.append(fname)
908 changelist.append(fname)
909
909
910 return fparent1
910 return fparent1
911
911
912 def commit(self, text="", user=None, date=None, match=None, force=False,
912 def commit(self, text="", user=None, date=None, match=None, force=False,
913 editor=False, extra={}):
913 editor=False, extra={}):
914 """Add a new revision to current repository.
914 """Add a new revision to current repository.
915
915
916 Revision information is gathered from the working directory,
916 Revision information is gathered from the working directory,
917 match can be used to filter the committed files. If editor is
917 match can be used to filter the committed files. If editor is
918 supplied, it is called to get a commit message.
918 supplied, it is called to get a commit message.
919 """
919 """
920
920
921 def fail(f, msg):
921 def fail(f, msg):
922 raise util.Abort('%s: %s' % (f, msg))
922 raise util.Abort('%s: %s' % (f, msg))
923
923
924 if not match:
924 if not match:
925 match = matchmod.always(self.root, '')
925 match = matchmod.always(self.root, '')
926
926
927 if not force:
927 if not force:
928 vdirs = []
928 vdirs = []
929 match.dir = vdirs.append
929 match.dir = vdirs.append
930 match.bad = fail
930 match.bad = fail
931
931
932 wlock = self.wlock()
932 wlock = self.wlock()
933 try:
933 try:
934 wctx = self[None]
934 wctx = self[None]
935 merge = len(wctx.parents()) > 1
935 merge = len(wctx.parents()) > 1
936
936
937 if (not force and merge and match and
937 if (not force and merge and match and
938 (match.files() or match.anypats())):
938 (match.files() or match.anypats())):
939 raise util.Abort(_('cannot partially commit a merge '
939 raise util.Abort(_('cannot partially commit a merge '
940 '(do not specify files or patterns)'))
940 '(do not specify files or patterns)'))
941
941
942 changes = self.status(match=match, clean=force)
942 changes = self.status(match=match, clean=force)
943 if force:
943 if force:
944 changes[0].extend(changes[6]) # mq may commit unchanged files
944 changes[0].extend(changes[6]) # mq may commit unchanged files
945
945
946 # check subrepos
946 # check subrepos
947 subs = []
947 subs = []
948 removedsubs = set()
948 removedsubs = set()
949 if '.hgsub' in wctx:
949 if '.hgsub' in wctx:
950 # only manage subrepos and .hgsubstate if .hgsub is present
950 # only manage subrepos and .hgsubstate if .hgsub is present
951 for p in wctx.parents():
951 for p in wctx.parents():
952 removedsubs.update(s for s in p.substate if match(s))
952 removedsubs.update(s for s in p.substate if match(s))
953 for s in wctx.substate:
953 for s in wctx.substate:
954 removedsubs.discard(s)
954 removedsubs.discard(s)
955 if match(s) and wctx.sub(s).dirty():
955 if match(s) and wctx.sub(s).dirty():
956 subs.append(s)
956 subs.append(s)
957 if (subs or removedsubs):
957 if (subs or removedsubs):
958 if (not match('.hgsub') and
958 if (not match('.hgsub') and
959 '.hgsub' in (wctx.modified() + wctx.added())):
959 '.hgsub' in (wctx.modified() + wctx.added())):
960 raise util.Abort(
960 raise util.Abort(
961 _("can't commit subrepos without .hgsub"))
961 _("can't commit subrepos without .hgsub"))
962 if '.hgsubstate' not in changes[0]:
962 if '.hgsubstate' not in changes[0]:
963 changes[0].insert(0, '.hgsubstate')
963 changes[0].insert(0, '.hgsubstate')
964 if '.hgsubstate' in changes[2]:
964 if '.hgsubstate' in changes[2]:
965 changes[2].remove('.hgsubstate')
965 changes[2].remove('.hgsubstate')
966 elif '.hgsub' in changes[2]:
966 elif '.hgsub' in changes[2]:
967 # clean up .hgsubstate when .hgsub is removed
967 # clean up .hgsubstate when .hgsub is removed
968 if ('.hgsubstate' in wctx and
968 if ('.hgsubstate' in wctx and
969 '.hgsubstate' not in changes[0] + changes[1] + changes[2]):
969 '.hgsubstate' not in changes[0] + changes[1] + changes[2]):
970 changes[2].insert(0, '.hgsubstate')
970 changes[2].insert(0, '.hgsubstate')
971
971
972 if subs and not self.ui.configbool('ui', 'commitsubrepos', True):
972 if subs and not self.ui.configbool('ui', 'commitsubrepos', True):
973 changedsubs = [s for s in subs if wctx.sub(s).dirty(True)]
973 changedsubs = [s for s in subs if wctx.sub(s).dirty(True)]
974 if changedsubs:
974 if changedsubs:
975 raise util.Abort(_("uncommitted changes in subrepo %s")
975 raise util.Abort(_("uncommitted changes in subrepo %s")
976 % changedsubs[0])
976 % changedsubs[0])
977
977
978 # make sure all explicit patterns are matched
978 # make sure all explicit patterns are matched
979 if not force and match.files():
979 if not force and match.files():
980 matched = set(changes[0] + changes[1] + changes[2])
980 matched = set(changes[0] + changes[1] + changes[2])
981
981
982 for f in match.files():
982 for f in match.files():
983 if f == '.' or f in matched or f in wctx.substate:
983 if f == '.' or f in matched or f in wctx.substate:
984 continue
984 continue
985 if f in changes[3]: # missing
985 if f in changes[3]: # missing
986 fail(f, _('file not found!'))
986 fail(f, _('file not found!'))
987 if f in vdirs: # visited directory
987 if f in vdirs: # visited directory
988 d = f + '/'
988 d = f + '/'
989 for mf in matched:
989 for mf in matched:
990 if mf.startswith(d):
990 if mf.startswith(d):
991 break
991 break
992 else:
992 else:
993 fail(f, _("no match under directory!"))
993 fail(f, _("no match under directory!"))
994 elif f not in self.dirstate:
994 elif f not in self.dirstate:
995 fail(f, _("file not tracked!"))
995 fail(f, _("file not tracked!"))
996
996
997 if (not force and not extra.get("close") and not merge
997 if (not force and not extra.get("close") and not merge
998 and not (changes[0] or changes[1] or changes[2])
998 and not (changes[0] or changes[1] or changes[2])
999 and wctx.branch() == wctx.p1().branch()):
999 and wctx.branch() == wctx.p1().branch()):
1000 return None
1000 return None
1001
1001
1002 ms = mergemod.mergestate(self)
1002 ms = mergemod.mergestate(self)
1003 for f in changes[0]:
1003 for f in changes[0]:
1004 if f in ms and ms[f] == 'u':
1004 if f in ms and ms[f] == 'u':
1005 raise util.Abort(_("unresolved merge conflicts "
1005 raise util.Abort(_("unresolved merge conflicts "
1006 "(see hg help resolve)"))
1006 "(see hg help resolve)"))
1007
1007
1008 cctx = context.workingctx(self, text, user, date, extra, changes)
1008 cctx = context.workingctx(self, text, user, date, extra, changes)
1009 if editor:
1009 if editor:
1010 cctx._text = editor(self, cctx, subs)
1010 cctx._text = editor(self, cctx, subs)
1011 edited = (text != cctx._text)
1011 edited = (text != cctx._text)
1012
1012
1013 # commit subs
1013 # commit subs
1014 if subs or removedsubs:
1014 if subs or removedsubs:
1015 state = wctx.substate.copy()
1015 state = wctx.substate.copy()
1016 for s in sorted(subs):
1016 for s in sorted(subs):
1017 sub = wctx.sub(s)
1017 sub = wctx.sub(s)
1018 self.ui.status(_('committing subrepository %s\n') %
1018 self.ui.status(_('committing subrepository %s\n') %
1019 subrepo.subrelpath(sub))
1019 subrepo.subrelpath(sub))
1020 sr = sub.commit(cctx._text, user, date)
1020 sr = sub.commit(cctx._text, user, date)
1021 state[s] = (state[s][0], sr)
1021 state[s] = (state[s][0], sr)
1022 subrepo.writestate(self, state)
1022 subrepo.writestate(self, state)
1023
1023
1024 # Save commit message in case this transaction gets rolled back
1024 # Save commit message in case this transaction gets rolled back
1025 # (e.g. by a pretxncommit hook). Leave the content alone on
1025 # (e.g. by a pretxncommit hook). Leave the content alone on
1026 # the assumption that the user will use the same editor again.
1026 # the assumption that the user will use the same editor again.
1027 msgfn = self.savecommitmessage(cctx._text)
1027 msgfn = self.savecommitmessage(cctx._text)
1028
1028
1029 p1, p2 = self.dirstate.parents()
1029 p1, p2 = self.dirstate.parents()
1030 hookp1, hookp2 = hex(p1), (p2 != nullid and hex(p2) or '')
1030 hookp1, hookp2 = hex(p1), (p2 != nullid and hex(p2) or '')
1031 try:
1031 try:
1032 self.hook("precommit", throw=True, parent1=hookp1, parent2=hookp2)
1032 self.hook("precommit", throw=True, parent1=hookp1, parent2=hookp2)
1033 ret = self.commitctx(cctx, True)
1033 ret = self.commitctx(cctx, True)
1034 except:
1034 except:
1035 if edited:
1035 if edited:
1036 self.ui.write(
1036 self.ui.write(
1037 _('note: commit message saved in %s\n') % msgfn)
1037 _('note: commit message saved in %s\n') % msgfn)
1038 raise
1038 raise
1039
1039
1040 # update bookmarks, dirstate and mergestate
1040 # update bookmarks, dirstate and mergestate
1041 bookmarks.update(self, p1, ret)
1041 bookmarks.update(self, p1, ret)
1042 for f in changes[0] + changes[1]:
1042 for f in changes[0] + changes[1]:
1043 self.dirstate.normal(f)
1043 self.dirstate.normal(f)
1044 for f in changes[2]:
1044 for f in changes[2]:
1045 self.dirstate.drop(f)
1045 self.dirstate.drop(f)
1046 self.dirstate.setparents(ret)
1046 self.dirstate.setparents(ret)
1047 ms.reset()
1047 ms.reset()
1048 finally:
1048 finally:
1049 wlock.release()
1049 wlock.release()
1050
1050
1051 self.hook("commit", node=hex(ret), parent1=hookp1, parent2=hookp2)
1051 self.hook("commit", node=hex(ret), parent1=hookp1, parent2=hookp2)
1052 return ret
1052 return ret
1053
1053
1054 def commitctx(self, ctx, error=False):
1054 def commitctx(self, ctx, error=False):
1055 """Add a new revision to current repository.
1055 """Add a new revision to current repository.
1056 Revision information is passed via the context argument.
1056 Revision information is passed via the context argument.
1057 """
1057 """
1058
1058
1059 tr = lock = None
1059 tr = lock = None
1060 removed = list(ctx.removed())
1060 removed = list(ctx.removed())
1061 p1, p2 = ctx.p1(), ctx.p2()
1061 p1, p2 = ctx.p1(), ctx.p2()
1062 user = ctx.user()
1062 user = ctx.user()
1063
1063
1064 lock = self.lock()
1064 lock = self.lock()
1065 try:
1065 try:
1066 tr = self.transaction("commit")
1066 tr = self.transaction("commit")
1067 trp = weakref.proxy(tr)
1067 trp = weakref.proxy(tr)
1068
1068
1069 if ctx.files():
1069 if ctx.files():
1070 m1 = p1.manifest().copy()
1070 m1 = p1.manifest().copy()
1071 m2 = p2.manifest()
1071 m2 = p2.manifest()
1072
1072
1073 # check in files
1073 # check in files
1074 new = {}
1074 new = {}
1075 changed = []
1075 changed = []
1076 linkrev = len(self)
1076 linkrev = len(self)
1077 for f in sorted(ctx.modified() + ctx.added()):
1077 for f in sorted(ctx.modified() + ctx.added()):
1078 self.ui.note(f + "\n")
1078 self.ui.note(f + "\n")
1079 try:
1079 try:
1080 fctx = ctx[f]
1080 fctx = ctx[f]
1081 new[f] = self._filecommit(fctx, m1, m2, linkrev, trp,
1081 new[f] = self._filecommit(fctx, m1, m2, linkrev, trp,
1082 changed)
1082 changed)
1083 m1.set(f, fctx.flags())
1083 m1.set(f, fctx.flags())
1084 except OSError, inst:
1084 except OSError, inst:
1085 self.ui.warn(_("trouble committing %s!\n") % f)
1085 self.ui.warn(_("trouble committing %s!\n") % f)
1086 raise
1086 raise
1087 except IOError, inst:
1087 except IOError, inst:
1088 errcode = getattr(inst, 'errno', errno.ENOENT)
1088 errcode = getattr(inst, 'errno', errno.ENOENT)
1089 if error or errcode and errcode != errno.ENOENT:
1089 if error or errcode and errcode != errno.ENOENT:
1090 self.ui.warn(_("trouble committing %s!\n") % f)
1090 self.ui.warn(_("trouble committing %s!\n") % f)
1091 raise
1091 raise
1092 else:
1092 else:
1093 removed.append(f)
1093 removed.append(f)
1094
1094
1095 # update manifest
1095 # update manifest
1096 m1.update(new)
1096 m1.update(new)
1097 removed = [f for f in sorted(removed) if f in m1 or f in m2]
1097 removed = [f for f in sorted(removed) if f in m1 or f in m2]
1098 drop = [f for f in removed if f in m1]
1098 drop = [f for f in removed if f in m1]
1099 for f in drop:
1099 for f in drop:
1100 del m1[f]
1100 del m1[f]
1101 mn = self.manifest.add(m1, trp, linkrev, p1.manifestnode(),
1101 mn = self.manifest.add(m1, trp, linkrev, p1.manifestnode(),
1102 p2.manifestnode(), (new, drop))
1102 p2.manifestnode(), (new, drop))
1103 files = changed + removed
1103 files = changed + removed
1104 else:
1104 else:
1105 mn = p1.manifestnode()
1105 mn = p1.manifestnode()
1106 files = []
1106 files = []
1107
1107
1108 # update changelog
1108 # update changelog
1109 self.changelog.delayupdate()
1109 self.changelog.delayupdate()
1110 n = self.changelog.add(mn, files, ctx.description(),
1110 n = self.changelog.add(mn, files, ctx.description(),
1111 trp, p1.node(), p2.node(),
1111 trp, p1.node(), p2.node(),
1112 user, ctx.date(), ctx.extra().copy())
1112 user, ctx.date(), ctx.extra().copy())
1113 p = lambda: self.changelog.writepending() and self.root or ""
1113 p = lambda: self.changelog.writepending() and self.root or ""
1114 xp1, xp2 = p1.hex(), p2 and p2.hex() or ''
1114 xp1, xp2 = p1.hex(), p2 and p2.hex() or ''
1115 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
1115 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
1116 parent2=xp2, pending=p)
1116 parent2=xp2, pending=p)
1117 self.changelog.finalize(trp)
1117 self.changelog.finalize(trp)
1118 tr.close()
1118 tr.close()
1119
1119
1120 if self._branchcache:
1120 if self._branchcache:
1121 self.updatebranchcache()
1121 self.updatebranchcache()
1122 return n
1122 return n
1123 finally:
1123 finally:
1124 if tr:
1124 if tr:
1125 tr.release()
1125 tr.release()
1126 lock.release()
1126 lock.release()
1127
1127
1128 def destroyed(self):
1128 def destroyed(self):
1129 '''Inform the repository that nodes have been destroyed.
1129 '''Inform the repository that nodes have been destroyed.
1130 Intended for use by strip and rollback, so there's a common
1130 Intended for use by strip and rollback, so there's a common
1131 place for anything that has to be done after destroying history.'''
1131 place for anything that has to be done after destroying history.'''
1132 # XXX it might be nice if we could take the list of destroyed
1132 # XXX it might be nice if we could take the list of destroyed
1133 # nodes, but I don't see an easy way for rollback() to do that
1133 # nodes, but I don't see an easy way for rollback() to do that
1134
1134
1135 # Ensure the persistent tag cache is updated. Doing it now
1135 # Ensure the persistent tag cache is updated. Doing it now
1136 # means that the tag cache only has to worry about destroyed
1136 # means that the tag cache only has to worry about destroyed
1137 # heads immediately after a strip/rollback. That in turn
1137 # heads immediately after a strip/rollback. That in turn
1138 # guarantees that "cachetip == currenttip" (comparing both rev
1138 # guarantees that "cachetip == currenttip" (comparing both rev
1139 # and node) always means no nodes have been added or destroyed.
1139 # and node) always means no nodes have been added or destroyed.
1140
1140
1141 # XXX this is suboptimal when qrefresh'ing: we strip the current
1141 # XXX this is suboptimal when qrefresh'ing: we strip the current
1142 # head, refresh the tag cache, then immediately add a new head.
1142 # head, refresh the tag cache, then immediately add a new head.
1143 # But I think doing it this way is necessary for the "instant
1143 # But I think doing it this way is necessary for the "instant
1144 # tag cache retrieval" case to work.
1144 # tag cache retrieval" case to work.
1145 self.invalidatecaches()
1145 self.invalidatecaches()
1146
1146
1147 def walk(self, match, node=None):
1147 def walk(self, match, node=None):
1148 '''
1148 '''
1149 walk recursively through the directory tree or a given
1149 walk recursively through the directory tree or a given
1150 changeset, finding all files matched by the match
1150 changeset, finding all files matched by the match
1151 function
1151 function
1152 '''
1152 '''
1153 return self[node].walk(match)
1153 return self[node].walk(match)
1154
1154
1155 def status(self, node1='.', node2=None, match=None,
1155 def status(self, node1='.', node2=None, match=None,
1156 ignored=False, clean=False, unknown=False,
1156 ignored=False, clean=False, unknown=False,
1157 listsubrepos=False):
1157 listsubrepos=False):
1158 """return status of files between two nodes or node and working directory
1158 """return status of files between two nodes or node and working directory
1159
1159
1160 If node1 is None, use the first dirstate parent instead.
1160 If node1 is None, use the first dirstate parent instead.
1161 If node2 is None, compare node1 with working directory.
1161 If node2 is None, compare node1 with working directory.
1162 """
1162 """
1163
1163
1164 def mfmatches(ctx):
1164 def mfmatches(ctx):
1165 mf = ctx.manifest().copy()
1165 mf = ctx.manifest().copy()
1166 for fn in mf.keys():
1166 for fn in mf.keys():
1167 if not match(fn):
1167 if not match(fn):
1168 del mf[fn]
1168 del mf[fn]
1169 return mf
1169 return mf
1170
1170
1171 if isinstance(node1, context.changectx):
1171 if isinstance(node1, context.changectx):
1172 ctx1 = node1
1172 ctx1 = node1
1173 else:
1173 else:
1174 ctx1 = self[node1]
1174 ctx1 = self[node1]
1175 if isinstance(node2, context.changectx):
1175 if isinstance(node2, context.changectx):
1176 ctx2 = node2
1176 ctx2 = node2
1177 else:
1177 else:
1178 ctx2 = self[node2]
1178 ctx2 = self[node2]
1179
1179
1180 working = ctx2.rev() is None
1180 working = ctx2.rev() is None
1181 parentworking = working and ctx1 == self['.']
1181 parentworking = working and ctx1 == self['.']
1182 match = match or matchmod.always(self.root, self.getcwd())
1182 match = match or matchmod.always(self.root, self.getcwd())
1183 listignored, listclean, listunknown = ignored, clean, unknown
1183 listignored, listclean, listunknown = ignored, clean, unknown
1184
1184
1185 # load earliest manifest first for caching reasons
1185 # load earliest manifest first for caching reasons
1186 if not working and ctx2.rev() < ctx1.rev():
1186 if not working and ctx2.rev() < ctx1.rev():
1187 ctx2.manifest()
1187 ctx2.manifest()
1188
1188
1189 if not parentworking:
1189 if not parentworking:
1190 def bad(f, msg):
1190 def bad(f, msg):
1191 if f not in ctx1:
1191 if f not in ctx1:
1192 self.ui.warn('%s: %s\n' % (self.dirstate.pathto(f), msg))
1192 self.ui.warn('%s: %s\n' % (self.dirstate.pathto(f), msg))
1193 match.bad = bad
1193 match.bad = bad
1194
1194
1195 if working: # we need to scan the working dir
1195 if working: # we need to scan the working dir
1196 subrepos = []
1196 subrepos = []
1197 if '.hgsub' in self.dirstate:
1197 if '.hgsub' in self.dirstate:
1198 subrepos = ctx1.substate.keys()
1198 subrepos = ctx1.substate.keys()
1199 s = self.dirstate.status(match, subrepos, listignored,
1199 s = self.dirstate.status(match, subrepos, listignored,
1200 listclean, listunknown)
1200 listclean, listunknown)
1201 cmp, modified, added, removed, deleted, unknown, ignored, clean = s
1201 cmp, modified, added, removed, deleted, unknown, ignored, clean = s
1202
1202
1203 # check for any possibly clean files
1203 # check for any possibly clean files
1204 if parentworking and cmp:
1204 if parentworking and cmp:
1205 fixup = []
1205 fixup = []
1206 # do a full compare of any files that might have changed
1206 # do a full compare of any files that might have changed
1207 for f in sorted(cmp):
1207 for f in sorted(cmp):
1208 if (f not in ctx1 or ctx2.flags(f) != ctx1.flags(f)
1208 if (f not in ctx1 or ctx2.flags(f) != ctx1.flags(f)
1209 or ctx1[f].cmp(ctx2[f])):
1209 or ctx1[f].cmp(ctx2[f])):
1210 modified.append(f)
1210 modified.append(f)
1211 else:
1211 else:
1212 fixup.append(f)
1212 fixup.append(f)
1213
1213
1214 # update dirstate for files that are actually clean
1214 # update dirstate for files that are actually clean
1215 if fixup:
1215 if fixup:
1216 if listclean:
1216 if listclean:
1217 clean += fixup
1217 clean += fixup
1218
1218
1219 try:
1219 try:
1220 # updating the dirstate is optional
1220 # updating the dirstate is optional
1221 # so we don't wait on the lock
1221 # so we don't wait on the lock
1222 wlock = self.wlock(False)
1222 wlock = self.wlock(False)
1223 try:
1223 try:
1224 for f in fixup:
1224 for f in fixup:
1225 self.dirstate.normal(f)
1225 self.dirstate.normal(f)
1226 finally:
1226 finally:
1227 wlock.release()
1227 wlock.release()
1228 except error.LockError:
1228 except error.LockError:
1229 pass
1229 pass
1230
1230
1231 if not parentworking:
1231 if not parentworking:
1232 mf1 = mfmatches(ctx1)
1232 mf1 = mfmatches(ctx1)
1233 if working:
1233 if working:
1234 # we are comparing working dir against non-parent
1234 # we are comparing working dir against non-parent
1235 # generate a pseudo-manifest for the working dir
1235 # generate a pseudo-manifest for the working dir
1236 mf2 = mfmatches(self['.'])
1236 mf2 = mfmatches(self['.'])
1237 for f in cmp + modified + added:
1237 for f in cmp + modified + added:
1238 mf2[f] = None
1238 mf2[f] = None
1239 mf2.set(f, ctx2.flags(f))
1239 mf2.set(f, ctx2.flags(f))
1240 for f in removed:
1240 for f in removed:
1241 if f in mf2:
1241 if f in mf2:
1242 del mf2[f]
1242 del mf2[f]
1243 else:
1243 else:
1244 # we are comparing two revisions
1244 # we are comparing two revisions
1245 deleted, unknown, ignored = [], [], []
1245 deleted, unknown, ignored = [], [], []
1246 mf2 = mfmatches(ctx2)
1246 mf2 = mfmatches(ctx2)
1247
1247
1248 modified, added, clean = [], [], []
1248 modified, added, clean = [], [], []
1249 for fn in mf2:
1249 for fn in mf2:
1250 if fn in mf1:
1250 if fn in mf1:
1251 if (fn not in deleted and
1251 if (fn not in deleted and
1252 (mf1.flags(fn) != mf2.flags(fn) or
1252 (mf1.flags(fn) != mf2.flags(fn) or
1253 (mf1[fn] != mf2[fn] and
1253 (mf1[fn] != mf2[fn] and
1254 (mf2[fn] or ctx1[fn].cmp(ctx2[fn]))))):
1254 (mf2[fn] or ctx1[fn].cmp(ctx2[fn]))))):
1255 modified.append(fn)
1255 modified.append(fn)
1256 elif listclean:
1256 elif listclean:
1257 clean.append(fn)
1257 clean.append(fn)
1258 del mf1[fn]
1258 del mf1[fn]
1259 elif fn not in deleted:
1259 elif fn not in deleted:
1260 added.append(fn)
1260 added.append(fn)
1261 removed = mf1.keys()
1261 removed = mf1.keys()
1262
1262
1263 r = modified, added, removed, deleted, unknown, ignored, clean
1263 r = modified, added, removed, deleted, unknown, ignored, clean
1264
1264
1265 if listsubrepos:
1265 if listsubrepos:
1266 for subpath, sub in subrepo.itersubrepos(ctx1, ctx2):
1266 for subpath, sub in subrepo.itersubrepos(ctx1, ctx2):
1267 if working:
1267 if working:
1268 rev2 = None
1268 rev2 = None
1269 else:
1269 else:
1270 rev2 = ctx2.substate[subpath][1]
1270 rev2 = ctx2.substate[subpath][1]
1271 try:
1271 try:
1272 submatch = matchmod.narrowmatcher(subpath, match)
1272 submatch = matchmod.narrowmatcher(subpath, match)
1273 s = sub.status(rev2, match=submatch, ignored=listignored,
1273 s = sub.status(rev2, match=submatch, ignored=listignored,
1274 clean=listclean, unknown=listunknown,
1274 clean=listclean, unknown=listunknown,
1275 listsubrepos=True)
1275 listsubrepos=True)
1276 for rfiles, sfiles in zip(r, s):
1276 for rfiles, sfiles in zip(r, s):
1277 rfiles.extend("%s/%s" % (subpath, f) for f in sfiles)
1277 rfiles.extend("%s/%s" % (subpath, f) for f in sfiles)
1278 except error.LookupError:
1278 except error.LookupError:
1279 self.ui.status(_("skipping missing subrepository: %s\n")
1279 self.ui.status(_("skipping missing subrepository: %s\n")
1280 % subpath)
1280 % subpath)
1281
1281
1282 for l in r:
1282 for l in r:
1283 l.sort()
1283 l.sort()
1284 return r
1284 return r
1285
1285
1286 def heads(self, start=None):
1286 def heads(self, start=None):
1287 heads = self.changelog.heads(start)
1287 heads = self.changelog.heads(start)
1288 # sort the output in rev descending order
1288 # sort the output in rev descending order
1289 return sorted(heads, key=self.changelog.rev, reverse=True)
1289 return sorted(heads, key=self.changelog.rev, reverse=True)
1290
1290
1291 def branchheads(self, branch=None, start=None, closed=False):
1291 def branchheads(self, branch=None, start=None, closed=False):
1292 '''return a (possibly filtered) list of heads for the given branch
1292 '''return a (possibly filtered) list of heads for the given branch
1293
1293
1294 Heads are returned in topological order, from newest to oldest.
1294 Heads are returned in topological order, from newest to oldest.
1295 If branch is None, use the dirstate branch.
1295 If branch is None, use the dirstate branch.
1296 If start is not None, return only heads reachable from start.
1296 If start is not None, return only heads reachable from start.
1297 If closed is True, return heads that are marked as closed as well.
1297 If closed is True, return heads that are marked as closed as well.
1298 '''
1298 '''
1299 if branch is None:
1299 if branch is None:
1300 branch = self[None].branch()
1300 branch = self[None].branch()
1301 branches = self.branchmap()
1301 branches = self.branchmap()
1302 if branch not in branches:
1302 if branch not in branches:
1303 return []
1303 return []
1304 # the cache returns heads ordered lowest to highest
1304 # the cache returns heads ordered lowest to highest
1305 bheads = list(reversed(branches[branch]))
1305 bheads = list(reversed(branches[branch]))
1306 if start is not None:
1306 if start is not None:
1307 # filter out the heads that cannot be reached from startrev
1307 # filter out the heads that cannot be reached from startrev
1308 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
1308 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
1309 bheads = [h for h in bheads if h in fbheads]
1309 bheads = [h for h in bheads if h in fbheads]
1310 if not closed:
1310 if not closed:
1311 bheads = [h for h in bheads if
1311 bheads = [h for h in bheads if
1312 ('close' not in self.changelog.read(h)[5])]
1312 ('close' not in self.changelog.read(h)[5])]
1313 return bheads
1313 return bheads
1314
1314
1315 def branches(self, nodes):
1315 def branches(self, nodes):
1316 if not nodes:
1316 if not nodes:
1317 nodes = [self.changelog.tip()]
1317 nodes = [self.changelog.tip()]
1318 b = []
1318 b = []
1319 for n in nodes:
1319 for n in nodes:
1320 t = n
1320 t = n
1321 while True:
1321 while True:
1322 p = self.changelog.parents(n)
1322 p = self.changelog.parents(n)
1323 if p[1] != nullid or p[0] == nullid:
1323 if p[1] != nullid or p[0] == nullid:
1324 b.append((t, n, p[0], p[1]))
1324 b.append((t, n, p[0], p[1]))
1325 break
1325 break
1326 n = p[0]
1326 n = p[0]
1327 return b
1327 return b
1328
1328
1329 def between(self, pairs):
1329 def between(self, pairs):
1330 r = []
1330 r = []
1331
1331
1332 for top, bottom in pairs:
1332 for top, bottom in pairs:
1333 n, l, i = top, [], 0
1333 n, l, i = top, [], 0
1334 f = 1
1334 f = 1
1335
1335
1336 while n != bottom and n != nullid:
1336 while n != bottom and n != nullid:
1337 p = self.changelog.parents(n)[0]
1337 p = self.changelog.parents(n)[0]
1338 if i == f:
1338 if i == f:
1339 l.append(n)
1339 l.append(n)
1340 f = f * 2
1340 f = f * 2
1341 n = p
1341 n = p
1342 i += 1
1342 i += 1
1343
1343
1344 r.append(l)
1344 r.append(l)
1345
1345
1346 return r
1346 return r
1347
1347
1348 def pull(self, remote, heads=None, force=False):
1348 def pull(self, remote, heads=None, force=False):
1349 lock = self.lock()
1349 lock = self.lock()
1350 try:
1350 try:
1351 tmp = discovery.findcommonincoming(self, remote, heads=heads,
1351 tmp = discovery.findcommonincoming(self, remote, heads=heads,
1352 force=force)
1352 force=force)
1353 common, fetch, rheads = tmp
1353 common, fetch, rheads = tmp
1354 if not fetch:
1354 if not fetch:
1355 self.ui.status(_("no changes found\n"))
1355 self.ui.status(_("no changes found\n"))
1356 result = 0
1356 result = 0
1357 else:
1357 else:
1358 if heads is None and list(common) == [nullid]:
1358 if heads is None and list(common) == [nullid]:
1359 self.ui.status(_("requesting all changes\n"))
1359 self.ui.status(_("requesting all changes\n"))
1360 elif heads is None and remote.capable('changegroupsubset'):
1360 elif heads is None and remote.capable('changegroupsubset'):
1361 # issue1320, avoid a race if remote changed after discovery
1361 # issue1320, avoid a race if remote changed after discovery
1362 heads = rheads
1362 heads = rheads
1363
1363
1364 if remote.capable('getbundle'):
1364 if remote.capable('getbundle'):
1365 cg = remote.getbundle('pull', common=common,
1365 cg = remote.getbundle('pull', common=common,
1366 heads=heads or rheads)
1366 heads=heads or rheads)
1367 elif heads is None:
1367 elif heads is None:
1368 cg = remote.changegroup(fetch, 'pull')
1368 cg = remote.changegroup(fetch, 'pull')
1369 elif not remote.capable('changegroupsubset'):
1369 elif not remote.capable('changegroupsubset'):
1370 raise util.Abort(_("partial pull cannot be done because "
1370 raise util.Abort(_("partial pull cannot be done because "
1371 "other repository doesn't support "
1371 "other repository doesn't support "
1372 "changegroupsubset."))
1372 "changegroupsubset."))
1373 else:
1373 else:
1374 cg = remote.changegroupsubset(fetch, heads, 'pull')
1374 cg = remote.changegroupsubset(fetch, heads, 'pull')
1375 result = self.addchangegroup(cg, 'pull', remote.url(),
1375 result = self.addchangegroup(cg, 'pull', remote.url(),
1376 lock=lock)
1376 lock=lock)
1377 finally:
1377 finally:
1378 lock.release()
1378 lock.release()
1379
1379
1380 return result
1380 return result
1381
1381
1382 def checkpush(self, force, revs):
1382 def checkpush(self, force, revs):
1383 """Extensions can override this function if additional checks have
1383 """Extensions can override this function if additional checks have
1384 to be performed before pushing, or call it if they override push
1384 to be performed before pushing, or call it if they override push
1385 command.
1385 command.
1386 """
1386 """
1387 pass
1387 pass
1388
1388
1389 def push(self, remote, force=False, revs=None, newbranch=False):
1389 def push(self, remote, force=False, revs=None, newbranch=False):
1390 '''Push outgoing changesets (limited by revs) from the current
1390 '''Push outgoing changesets (limited by revs) from the current
1391 repository to remote. Return an integer:
1391 repository to remote. Return an integer:
1392 - 0 means HTTP error *or* nothing to push
1392 - 0 means HTTP error *or* nothing to push
1393 - 1 means we pushed and remote head count is unchanged *or*
1393 - 1 means we pushed and remote head count is unchanged *or*
1394 we have outgoing changesets but refused to push
1394 we have outgoing changesets but refused to push
1395 - other values as described by addchangegroup()
1395 - other values as described by addchangegroup()
1396 '''
1396 '''
1397 # there are two ways to push to remote repo:
1397 # there are two ways to push to remote repo:
1398 #
1398 #
1399 # addchangegroup assumes local user can lock remote
1399 # addchangegroup assumes local user can lock remote
1400 # repo (local filesystem, old ssh servers).
1400 # repo (local filesystem, old ssh servers).
1401 #
1401 #
1402 # unbundle assumes local user cannot lock remote repo (new ssh
1402 # unbundle assumes local user cannot lock remote repo (new ssh
1403 # servers, http servers).
1403 # servers, http servers).
1404
1404
1405 self.checkpush(force, revs)
1405 self.checkpush(force, revs)
1406 lock = None
1406 lock = None
1407 unbundle = remote.capable('unbundle')
1407 unbundle = remote.capable('unbundle')
1408 if not unbundle:
1408 if not unbundle:
1409 lock = remote.lock()
1409 lock = remote.lock()
1410 try:
1410 try:
1411 cg, remote_heads = discovery.prepush(self, remote, force, revs,
1411 cg, remote_heads = discovery.prepush(self, remote, force, revs,
1412 newbranch)
1412 newbranch)
1413 ret = remote_heads
1413 ret = remote_heads
1414 if cg is not None:
1414 if cg is not None:
1415 if unbundle:
1415 if unbundle:
1416 # local repo finds heads on server, finds out what
1416 # local repo finds heads on server, finds out what
1417 # revs it must push. once revs transferred, if server
1417 # revs it must push. once revs transferred, if server
1418 # finds it has different heads (someone else won
1418 # finds it has different heads (someone else won
1419 # commit/push race), server aborts.
1419 # commit/push race), server aborts.
1420 if force:
1420 if force:
1421 remote_heads = ['force']
1421 remote_heads = ['force']
1422 # ssh: return remote's addchangegroup()
1422 # ssh: return remote's addchangegroup()
1423 # http: return remote's addchangegroup() or 0 for error
1423 # http: return remote's addchangegroup() or 0 for error
1424 ret = remote.unbundle(cg, remote_heads, 'push')
1424 ret = remote.unbundle(cg, remote_heads, 'push')
1425 else:
1425 else:
1426 # we return an integer indicating remote head count change
1426 # we return an integer indicating remote head count change
1427 ret = remote.addchangegroup(cg, 'push', self.url(),
1427 ret = remote.addchangegroup(cg, 'push', self.url(),
1428 lock=lock)
1428 lock=lock)
1429 finally:
1429 finally:
1430 if lock is not None:
1430 if lock is not None:
1431 lock.release()
1431 lock.release()
1432
1432
1433 self.ui.debug("checking for updated bookmarks\n")
1433 self.ui.debug("checking for updated bookmarks\n")
1434 rb = remote.listkeys('bookmarks')
1434 rb = remote.listkeys('bookmarks')
1435 for k in rb.keys():
1435 for k in rb.keys():
1436 if k in self._bookmarks:
1436 if k in self._bookmarks:
1437 nr, nl = rb[k], hex(self._bookmarks[k])
1437 nr, nl = rb[k], hex(self._bookmarks[k])
1438 if nr in self:
1438 if nr in self:
1439 cr = self[nr]
1439 cr = self[nr]
1440 cl = self[nl]
1440 cl = self[nl]
1441 if cl in cr.descendants():
1441 if cl in cr.descendants():
1442 r = remote.pushkey('bookmarks', k, nr, nl)
1442 r = remote.pushkey('bookmarks', k, nr, nl)
1443 if r:
1443 if r:
1444 self.ui.status(_("updating bookmark %s\n") % k)
1444 self.ui.status(_("updating bookmark %s\n") % k)
1445 else:
1445 else:
1446 self.ui.warn(_('updating bookmark %s'
1446 self.ui.warn(_('updating bookmark %s'
1447 ' failed!\n') % k)
1447 ' failed!\n') % k)
1448
1448
1449 return ret
1449 return ret
1450
1450
1451 def changegroupinfo(self, nodes, source):
1451 def changegroupinfo(self, nodes, source):
1452 if self.ui.verbose or source == 'bundle':
1452 if self.ui.verbose or source == 'bundle':
1453 self.ui.status(_("%d changesets found\n") % len(nodes))
1453 self.ui.status(_("%d changesets found\n") % len(nodes))
1454 if self.ui.debugflag:
1454 if self.ui.debugflag:
1455 self.ui.debug("list of changesets:\n")
1455 self.ui.debug("list of changesets:\n")
1456 for node in nodes:
1456 for node in nodes:
1457 self.ui.debug("%s\n" % hex(node))
1457 self.ui.debug("%s\n" % hex(node))
1458
1458
1459 def changegroupsubset(self, bases, heads, source):
1459 def changegroupsubset(self, bases, heads, source):
1460 """Compute a changegroup consisting of all the nodes that are
1460 """Compute a changegroup consisting of all the nodes that are
1461 descendants of any of the bases and ancestors of any of the heads.
1461 descendants of any of the bases and ancestors of any of the heads.
1462 Return a chunkbuffer object whose read() method will return
1462 Return a chunkbuffer object whose read() method will return
1463 successive changegroup chunks.
1463 successive changegroup chunks.
1464
1464
1465 It is fairly complex as determining which filenodes and which
1465 It is fairly complex as determining which filenodes and which
1466 manifest nodes need to be included for the changeset to be complete
1466 manifest nodes need to be included for the changeset to be complete
1467 is non-trivial.
1467 is non-trivial.
1468
1468
1469 Another wrinkle is doing the reverse, figuring out which changeset in
1469 Another wrinkle is doing the reverse, figuring out which changeset in
1470 the changegroup a particular filenode or manifestnode belongs to.
1470 the changegroup a particular filenode or manifestnode belongs to.
1471 """
1471 """
1472 cl = self.changelog
1472 cl = self.changelog
1473 if not bases:
1473 if not bases:
1474 bases = [nullid]
1474 bases = [nullid]
1475 csets, bases, heads = cl.nodesbetween(bases, heads)
1475 csets, bases, heads = cl.nodesbetween(bases, heads)
1476 # We assume that all ancestors of bases are known
1476 # We assume that all ancestors of bases are known
1477 common = set(cl.ancestors(*[cl.rev(n) for n in bases]))
1477 common = set(cl.ancestors(*[cl.rev(n) for n in bases]))
1478 return self._changegroupsubset(common, csets, heads, source)
1478 return self._changegroupsubset(common, csets, heads, source)
1479
1479
1480 def getbundle(self, source, heads=None, common=None):
1480 def getbundle(self, source, heads=None, common=None):
1481 """Like changegroupsubset, but returns the set difference between the
1481 """Like changegroupsubset, but returns the set difference between the
1482 ancestors of heads and the ancestors common.
1482 ancestors of heads and the ancestors common.
1483
1483
1484 If heads is None, use the local heads. If common is None, use [nullid].
1484 If heads is None, use the local heads. If common is None, use [nullid].
1485
1485
1486 The nodes in common might not all be known locally due to the way the
1486 The nodes in common might not all be known locally due to the way the
1487 current discovery protocol works.
1487 current discovery protocol works.
1488 """
1488 """
1489 cl = self.changelog
1489 cl = self.changelog
1490 if common:
1490 if common:
1491 nm = cl.nodemap
1491 nm = cl.nodemap
1492 common = [n for n in common if n in nm]
1492 common = [n for n in common if n in nm]
1493 else:
1493 else:
1494 common = [nullid]
1494 common = [nullid]
1495 if not heads:
1495 if not heads:
1496 heads = cl.heads()
1496 heads = cl.heads()
1497 common, missing = cl.findcommonmissing(common, heads)
1497 common, missing = cl.findcommonmissing(common, heads)
1498 if not missing:
1498 if not missing:
1499 return None
1499 return None
1500 return self._changegroupsubset(common, missing, heads, source)
1500 return self._changegroupsubset(common, missing, heads, source)
1501
1501
1502 def _changegroupsubset(self, commonrevs, csets, heads, source):
1502 def _changegroupsubset(self, commonrevs, csets, heads, source):
1503
1503
1504 cl = self.changelog
1504 cl = self.changelog
1505 mf = self.manifest
1505 mf = self.manifest
1506 mfs = {} # needed manifests
1506 mfs = {} # needed manifests
1507 fnodes = {} # needed file nodes
1507 fnodes = {} # needed file nodes
1508 changedfiles = set()
1508 changedfiles = set()
1509 fstate = ['', {}]
1509 fstate = ['', {}]
1510 count = [0]
1510 count = [0]
1511
1511
1512 # can we go through the fast path ?
1512 # can we go through the fast path ?
1513 heads.sort()
1513 heads.sort()
1514 if heads == sorted(self.heads()):
1514 if heads == sorted(self.heads()):
1515 return self._changegroup(csets, source)
1515 return self._changegroup(csets, source)
1516
1516
1517 # slow path
1517 # slow path
1518 self.hook('preoutgoing', throw=True, source=source)
1518 self.hook('preoutgoing', throw=True, source=source)
1519 self.changegroupinfo(csets, source)
1519 self.changegroupinfo(csets, source)
1520
1520
1521 # filter any nodes that claim to be part of the known set
1521 # filter any nodes that claim to be part of the known set
1522 def prune(revlog, missing):
1522 def prune(revlog, missing):
1523 return [n for n in missing
1523 return [n for n in missing
1524 if revlog.linkrev(revlog.rev(n)) not in commonrevs]
1524 if revlog.linkrev(revlog.rev(n)) not in commonrevs]
1525
1525
1526 def lookup(revlog, x):
1526 def lookup(revlog, x):
1527 if revlog == cl:
1527 if revlog == cl:
1528 c = cl.read(x)
1528 c = cl.read(x)
1529 changedfiles.update(c[3])
1529 changedfiles.update(c[3])
1530 mfs.setdefault(c[0], x)
1530 mfs.setdefault(c[0], x)
1531 count[0] += 1
1531 count[0] += 1
1532 self.ui.progress(_('bundling'), count[0],
1532 self.ui.progress(_('bundling'), count[0],
1533 unit=_('changesets'), total=len(csets))
1533 unit=_('changesets'), total=len(csets))
1534 return x
1534 return x
1535 elif revlog == mf:
1535 elif revlog == mf:
1536 clnode = mfs[x]
1536 clnode = mfs[x]
1537 mdata = mf.readfast(x)
1537 mdata = mf.readfast(x)
1538 for f in changedfiles:
1538 for f in changedfiles:
1539 if f in mdata:
1539 if f in mdata:
1540 fnodes.setdefault(f, {}).setdefault(mdata[f], clnode)
1540 fnodes.setdefault(f, {}).setdefault(mdata[f], clnode)
1541 count[0] += 1
1541 count[0] += 1
1542 self.ui.progress(_('bundling'), count[0],
1542 self.ui.progress(_('bundling'), count[0],
1543 unit=_('manifests'), total=len(mfs))
1543 unit=_('manifests'), total=len(mfs))
1544 return mfs[x]
1544 return mfs[x]
1545 else:
1545 else:
1546 self.ui.progress(
1546 self.ui.progress(
1547 _('bundling'), count[0], item=fstate[0],
1547 _('bundling'), count[0], item=fstate[0],
1548 unit=_('files'), total=len(changedfiles))
1548 unit=_('files'), total=len(changedfiles))
1549 return fstate[1][x]
1549 return fstate[1][x]
1550
1550
1551 bundler = changegroup.bundle10(lookup)
1551 bundler = changegroup.bundle10(lookup)
1552 reorder = self.ui.config('bundle', 'reorder', 'auto')
1552 reorder = self.ui.config('bundle', 'reorder', 'auto')
1553 if reorder == 'auto':
1553 if reorder == 'auto':
1554 reorder = None
1554 reorder = None
1555 else:
1555 else:
1556 reorder = util.parsebool(reorder)
1556 reorder = util.parsebool(reorder)
1557
1557
1558 def gengroup():
1558 def gengroup():
1559 # Create a changenode group generator that will call our functions
1559 # Create a changenode group generator that will call our functions
1560 # back to lookup the owning changenode and collect information.
1560 # back to lookup the owning changenode and collect information.
1561 for chunk in cl.group(csets, bundler, reorder=reorder):
1561 for chunk in cl.group(csets, bundler, reorder=reorder):
1562 yield chunk
1562 yield chunk
1563 self.ui.progress(_('bundling'), None)
1563 self.ui.progress(_('bundling'), None)
1564
1564
1565 # Create a generator for the manifestnodes that calls our lookup
1565 # Create a generator for the manifestnodes that calls our lookup
1566 # and data collection functions back.
1566 # and data collection functions back.
1567 count[0] = 0
1567 count[0] = 0
1568 for chunk in mf.group(prune(mf, mfs), bundler, reorder=reorder):
1568 for chunk in mf.group(prune(mf, mfs), bundler, reorder=reorder):
1569 yield chunk
1569 yield chunk
1570 self.ui.progress(_('bundling'), None)
1570 self.ui.progress(_('bundling'), None)
1571
1571
1572 mfs.clear()
1572 mfs.clear()
1573
1573
1574 # Go through all our files in order sorted by name.
1574 # Go through all our files in order sorted by name.
1575 count[0] = 0
1575 count[0] = 0
1576 for fname in sorted(changedfiles):
1576 for fname in sorted(changedfiles):
1577 filerevlog = self.file(fname)
1577 filerevlog = self.file(fname)
1578 if not len(filerevlog):
1578 if not len(filerevlog):
1579 raise util.Abort(_("empty or missing revlog for %s") % fname)
1579 raise util.Abort(_("empty or missing revlog for %s") % fname)
1580 fstate[0] = fname
1580 fstate[0] = fname
1581 fstate[1] = fnodes.pop(fname, {})
1581 fstate[1] = fnodes.pop(fname, {})
1582
1582
1583 nodelist = prune(filerevlog, fstate[1])
1583 nodelist = prune(filerevlog, fstate[1])
1584 if nodelist:
1584 if nodelist:
1585 count[0] += 1
1585 count[0] += 1
1586 yield bundler.fileheader(fname)
1586 yield bundler.fileheader(fname)
1587 for chunk in filerevlog.group(nodelist, bundler, reorder):
1587 for chunk in filerevlog.group(nodelist, bundler, reorder):
1588 yield chunk
1588 yield chunk
1589
1589
1590 # Signal that no more groups are left.
1590 # Signal that no more groups are left.
1591 yield bundler.close()
1591 yield bundler.close()
1592 self.ui.progress(_('bundling'), None)
1592 self.ui.progress(_('bundling'), None)
1593
1593
1594 if csets:
1594 if csets:
1595 self.hook('outgoing', node=hex(csets[0]), source=source)
1595 self.hook('outgoing', node=hex(csets[0]), source=source)
1596
1596
1597 return changegroup.unbundle10(util.chunkbuffer(gengroup()), 'UN')
1597 return changegroup.unbundle10(util.chunkbuffer(gengroup()), 'UN')
1598
1598
1599 def changegroup(self, basenodes, source):
1599 def changegroup(self, basenodes, source):
1600 # to avoid a race we use changegroupsubset() (issue1320)
1600 # to avoid a race we use changegroupsubset() (issue1320)
1601 return self.changegroupsubset(basenodes, self.heads(), source)
1601 return self.changegroupsubset(basenodes, self.heads(), source)
1602
1602
1603 def _changegroup(self, nodes, source):
1603 def _changegroup(self, nodes, source):
1604 """Compute the changegroup of all nodes that we have that a recipient
1604 """Compute the changegroup of all nodes that we have that a recipient
1605 doesn't. Return a chunkbuffer object whose read() method will return
1605 doesn't. Return a chunkbuffer object whose read() method will return
1606 successive changegroup chunks.
1606 successive changegroup chunks.
1607
1607
1608 This is much easier than the previous function as we can assume that
1608 This is much easier than the previous function as we can assume that
1609 the recipient has any changenode we aren't sending them.
1609 the recipient has any changenode we aren't sending them.
1610
1610
1611 nodes is the set of nodes to send"""
1611 nodes is the set of nodes to send"""
1612
1612
1613 cl = self.changelog
1613 cl = self.changelog
1614 mf = self.manifest
1614 mf = self.manifest
1615 mfs = {}
1615 mfs = {}
1616 changedfiles = set()
1616 changedfiles = set()
1617 fstate = ['']
1617 fstate = ['']
1618 count = [0]
1618 count = [0]
1619
1619
1620 self.hook('preoutgoing', throw=True, source=source)
1620 self.hook('preoutgoing', throw=True, source=source)
1621 self.changegroupinfo(nodes, source)
1621 self.changegroupinfo(nodes, source)
1622
1622
1623 revset = set([cl.rev(n) for n in nodes])
1623 revset = set([cl.rev(n) for n in nodes])
1624
1624
1625 def gennodelst(log):
1625 def gennodelst(log):
1626 return [log.node(r) for r in log if log.linkrev(r) in revset]
1626 return [log.node(r) for r in log if log.linkrev(r) in revset]
1627
1627
1628 def lookup(revlog, x):
1628 def lookup(revlog, x):
1629 if revlog == cl:
1629 if revlog == cl:
1630 c = cl.read(x)
1630 c = cl.read(x)
1631 changedfiles.update(c[3])
1631 changedfiles.update(c[3])
1632 mfs.setdefault(c[0], x)
1632 mfs.setdefault(c[0], x)
1633 count[0] += 1
1633 count[0] += 1
1634 self.ui.progress(_('bundling'), count[0],
1634 self.ui.progress(_('bundling'), count[0],
1635 unit=_('changesets'), total=len(nodes))
1635 unit=_('changesets'), total=len(nodes))
1636 return x
1636 return x
1637 elif revlog == mf:
1637 elif revlog == mf:
1638 count[0] += 1
1638 count[0] += 1
1639 self.ui.progress(_('bundling'), count[0],
1639 self.ui.progress(_('bundling'), count[0],
1640 unit=_('manifests'), total=len(mfs))
1640 unit=_('manifests'), total=len(mfs))
1641 return cl.node(revlog.linkrev(revlog.rev(x)))
1641 return cl.node(revlog.linkrev(revlog.rev(x)))
1642 else:
1642 else:
1643 self.ui.progress(
1643 self.ui.progress(
1644 _('bundling'), count[0], item=fstate[0],
1644 _('bundling'), count[0], item=fstate[0],
1645 total=len(changedfiles), unit=_('files'))
1645 total=len(changedfiles), unit=_('files'))
1646 return cl.node(revlog.linkrev(revlog.rev(x)))
1646 return cl.node(revlog.linkrev(revlog.rev(x)))
1647
1647
1648 bundler = changegroup.bundle10(lookup)
1648 bundler = changegroup.bundle10(lookup)
1649 reorder = self.ui.config('bundle', 'reorder', 'auto')
1649 reorder = self.ui.config('bundle', 'reorder', 'auto')
1650 if reorder == 'auto':
1650 if reorder == 'auto':
1651 reorder = None
1651 reorder = None
1652 else:
1652 else:
1653 reorder = util.parsebool(reorder)
1653 reorder = util.parsebool(reorder)
1654
1654
1655 def gengroup():
1655 def gengroup():
1656 '''yield a sequence of changegroup chunks (strings)'''
1656 '''yield a sequence of changegroup chunks (strings)'''
1657 # construct a list of all changed files
1657 # construct a list of all changed files
1658
1658
1659 for chunk in cl.group(nodes, bundler, reorder=reorder):
1659 for chunk in cl.group(nodes, bundler, reorder=reorder):
1660 yield chunk
1660 yield chunk
1661 self.ui.progress(_('bundling'), None)
1661 self.ui.progress(_('bundling'), None)
1662
1662
1663 count[0] = 0
1663 count[0] = 0
1664 for chunk in mf.group(gennodelst(mf), bundler, reorder=reorder):
1664 for chunk in mf.group(gennodelst(mf), bundler, reorder=reorder):
1665 yield chunk
1665 yield chunk
1666 self.ui.progress(_('bundling'), None)
1666 self.ui.progress(_('bundling'), None)
1667
1667
1668 count[0] = 0
1668 count[0] = 0
1669 for fname in sorted(changedfiles):
1669 for fname in sorted(changedfiles):
1670 filerevlog = self.file(fname)
1670 filerevlog = self.file(fname)
1671 if not len(filerevlog):
1671 if not len(filerevlog):
1672 raise util.Abort(_("empty or missing revlog for %s") % fname)
1672 raise util.Abort(_("empty or missing revlog for %s") % fname)
1673 fstate[0] = fname
1673 fstate[0] = fname
1674 nodelist = gennodelst(filerevlog)
1674 nodelist = gennodelst(filerevlog)
1675 if nodelist:
1675 if nodelist:
1676 count[0] += 1
1676 count[0] += 1
1677 yield bundler.fileheader(fname)
1677 yield bundler.fileheader(fname)
1678 for chunk in filerevlog.group(nodelist, bundler, reorder):
1678 for chunk in filerevlog.group(nodelist, bundler, reorder):
1679 yield chunk
1679 yield chunk
1680 yield bundler.close()
1680 yield bundler.close()
1681 self.ui.progress(_('bundling'), None)
1681 self.ui.progress(_('bundling'), None)
1682
1682
1683 if nodes:
1683 if nodes:
1684 self.hook('outgoing', node=hex(nodes[0]), source=source)
1684 self.hook('outgoing', node=hex(nodes[0]), source=source)
1685
1685
1686 return changegroup.unbundle10(util.chunkbuffer(gengroup()), 'UN')
1686 return changegroup.unbundle10(util.chunkbuffer(gengroup()), 'UN')
1687
1687
1688 def addchangegroup(self, source, srctype, url, emptyok=False, lock=None):
1688 def addchangegroup(self, source, srctype, url, emptyok=False, lock=None):
1689 """Add the changegroup returned by source.read() to this repo.
1689 """Add the changegroup returned by source.read() to this repo.
1690 srctype is a string like 'push', 'pull', or 'unbundle'. url is
1690 srctype is a string like 'push', 'pull', or 'unbundle'. url is
1691 the URL of the repo where this changegroup is coming from.
1691 the URL of the repo where this changegroup is coming from.
1692 If lock is not None, the function takes ownership of the lock
1692 If lock is not None, the function takes ownership of the lock
1693 and releases it after the changegroup is added.
1693 and releases it after the changegroup is added.
1694
1694
1695 Return an integer summarizing the change to this repo:
1695 Return an integer summarizing the change to this repo:
1696 - nothing changed or no source: 0
1696 - nothing changed or no source: 0
1697 - more heads than before: 1+added heads (2..n)
1697 - more heads than before: 1+added heads (2..n)
1698 - fewer heads than before: -1-removed heads (-2..-n)
1698 - fewer heads than before: -1-removed heads (-2..-n)
1699 - number of heads stays the same: 1
1699 - number of heads stays the same: 1
1700 """
1700 """
1701 def csmap(x):
1701 def csmap(x):
1702 self.ui.debug("add changeset %s\n" % short(x))
1702 self.ui.debug("add changeset %s\n" % short(x))
1703 return len(cl)
1703 return len(cl)
1704
1704
1705 def revmap(x):
1705 def revmap(x):
1706 return cl.rev(x)
1706 return cl.rev(x)
1707
1707
1708 if not source:
1708 if not source:
1709 return 0
1709 return 0
1710
1710
1711 self.hook('prechangegroup', throw=True, source=srctype, url=url)
1711 self.hook('prechangegroup', throw=True, source=srctype, url=url)
1712
1712
1713 changesets = files = revisions = 0
1713 changesets = files = revisions = 0
1714 efiles = set()
1714 efiles = set()
1715
1715
1716 # write changelog data to temp files so concurrent readers will not see
1716 # write changelog data to temp files so concurrent readers will not see
1717 # inconsistent view
1717 # inconsistent view
1718 cl = self.changelog
1718 cl = self.changelog
1719 cl.delayupdate()
1719 cl.delayupdate()
1720 oldheads = cl.heads()
1720 oldheads = cl.heads()
1721
1721
1722 tr = self.transaction("\n".join([srctype, util.hidepassword(url)]))
1722 tr = self.transaction("\n".join([srctype, util.hidepassword(url)]))
1723 try:
1723 try:
1724 trp = weakref.proxy(tr)
1724 trp = weakref.proxy(tr)
1725 # pull off the changeset group
1725 # pull off the changeset group
1726 self.ui.status(_("adding changesets\n"))
1726 self.ui.status(_("adding changesets\n"))
1727 clstart = len(cl)
1727 clstart = len(cl)
1728 class prog(object):
1728 class prog(object):
1729 step = _('changesets')
1729 step = _('changesets')
1730 count = 1
1730 count = 1
1731 ui = self.ui
1731 ui = self.ui
1732 total = None
1732 total = None
1733 def __call__(self):
1733 def __call__(self):
1734 self.ui.progress(self.step, self.count, unit=_('chunks'),
1734 self.ui.progress(self.step, self.count, unit=_('chunks'),
1735 total=self.total)
1735 total=self.total)
1736 self.count += 1
1736 self.count += 1
1737 pr = prog()
1737 pr = prog()
1738 source.callback = pr
1738 source.callback = pr
1739
1739
1740 source.changelogheader()
1740 source.changelogheader()
1741 if (cl.addgroup(source, csmap, trp) is None
1741 if (cl.addgroup(source, csmap, trp) is None
1742 and not emptyok):
1742 and not emptyok):
1743 raise util.Abort(_("received changelog group is empty"))
1743 raise util.Abort(_("received changelog group is empty"))
1744 clend = len(cl)
1744 clend = len(cl)
1745 changesets = clend - clstart
1745 changesets = clend - clstart
1746 for c in xrange(clstart, clend):
1746 for c in xrange(clstart, clend):
1747 efiles.update(self[c].files())
1747 efiles.update(self[c].files())
1748 efiles = len(efiles)
1748 efiles = len(efiles)
1749 self.ui.progress(_('changesets'), None)
1749 self.ui.progress(_('changesets'), None)
1750
1750
1751 # pull off the manifest group
1751 # pull off the manifest group
1752 self.ui.status(_("adding manifests\n"))
1752 self.ui.status(_("adding manifests\n"))
1753 pr.step = _('manifests')
1753 pr.step = _('manifests')
1754 pr.count = 1
1754 pr.count = 1
1755 pr.total = changesets # manifests <= changesets
1755 pr.total = changesets # manifests <= changesets
1756 # no need to check for empty manifest group here:
1756 # no need to check for empty manifest group here:
1757 # if the result of the merge of 1 and 2 is the same in 3 and 4,
1757 # if the result of the merge of 1 and 2 is the same in 3 and 4,
1758 # no new manifest will be created and the manifest group will
1758 # no new manifest will be created and the manifest group will
1759 # be empty during the pull
1759 # be empty during the pull
1760 source.manifestheader()
1760 source.manifestheader()
1761 self.manifest.addgroup(source, revmap, trp)
1761 self.manifest.addgroup(source, revmap, trp)
1762 self.ui.progress(_('manifests'), None)
1762 self.ui.progress(_('manifests'), None)
1763
1763
1764 needfiles = {}
1764 needfiles = {}
1765 if self.ui.configbool('server', 'validate', default=False):
1765 if self.ui.configbool('server', 'validate', default=False):
1766 # validate incoming csets have their manifests
1766 # validate incoming csets have their manifests
1767 for cset in xrange(clstart, clend):
1767 for cset in xrange(clstart, clend):
1768 mfest = self.changelog.read(self.changelog.node(cset))[0]
1768 mfest = self.changelog.read(self.changelog.node(cset))[0]
1769 mfest = self.manifest.readdelta(mfest)
1769 mfest = self.manifest.readdelta(mfest)
1770 # store file nodes we must see
1770 # store file nodes we must see
1771 for f, n in mfest.iteritems():
1771 for f, n in mfest.iteritems():
1772 needfiles.setdefault(f, set()).add(n)
1772 needfiles.setdefault(f, set()).add(n)
1773
1773
1774 # process the files
1774 # process the files
1775 self.ui.status(_("adding file changes\n"))
1775 self.ui.status(_("adding file changes\n"))
1776 pr.step = 'files'
1776 pr.step = _('files')
1777 pr.count = 1
1777 pr.count = 1
1778 pr.total = efiles
1778 pr.total = efiles
1779 source.callback = None
1779 source.callback = None
1780
1780
1781 while True:
1781 while True:
1782 chunkdata = source.filelogheader()
1782 chunkdata = source.filelogheader()
1783 if not chunkdata:
1783 if not chunkdata:
1784 break
1784 break
1785 f = chunkdata["filename"]
1785 f = chunkdata["filename"]
1786 self.ui.debug("adding %s revisions\n" % f)
1786 self.ui.debug("adding %s revisions\n" % f)
1787 pr()
1787 pr()
1788 fl = self.file(f)
1788 fl = self.file(f)
1789 o = len(fl)
1789 o = len(fl)
1790 if fl.addgroup(source, revmap, trp) is None:
1790 if fl.addgroup(source, revmap, trp) is None:
1791 raise util.Abort(_("received file revlog group is empty"))
1791 raise util.Abort(_("received file revlog group is empty"))
1792 revisions += len(fl) - o
1792 revisions += len(fl) - o
1793 files += 1
1793 files += 1
1794 if f in needfiles:
1794 if f in needfiles:
1795 needs = needfiles[f]
1795 needs = needfiles[f]
1796 for new in xrange(o, len(fl)):
1796 for new in xrange(o, len(fl)):
1797 n = fl.node(new)
1797 n = fl.node(new)
1798 if n in needs:
1798 if n in needs:
1799 needs.remove(n)
1799 needs.remove(n)
1800 if not needs:
1800 if not needs:
1801 del needfiles[f]
1801 del needfiles[f]
1802 self.ui.progress(_('files'), None)
1802 self.ui.progress(_('files'), None)
1803
1803
1804 for f, needs in needfiles.iteritems():
1804 for f, needs in needfiles.iteritems():
1805 fl = self.file(f)
1805 fl = self.file(f)
1806 for n in needs:
1806 for n in needs:
1807 try:
1807 try:
1808 fl.rev(n)
1808 fl.rev(n)
1809 except error.LookupError:
1809 except error.LookupError:
1810 raise util.Abort(
1810 raise util.Abort(
1811 _('missing file data for %s:%s - run hg verify') %
1811 _('missing file data for %s:%s - run hg verify') %
1812 (f, hex(n)))
1812 (f, hex(n)))
1813
1813
1814 dh = 0
1814 dh = 0
1815 if oldheads:
1815 if oldheads:
1816 heads = cl.heads()
1816 heads = cl.heads()
1817 dh = len(heads) - len(oldheads)
1817 dh = len(heads) - len(oldheads)
1818 for h in heads:
1818 for h in heads:
1819 if h not in oldheads and 'close' in self[h].extra():
1819 if h not in oldheads and 'close' in self[h].extra():
1820 dh -= 1
1820 dh -= 1
1821 htext = ""
1821 htext = ""
1822 if dh:
1822 if dh:
1823 htext = _(" (%+d heads)") % dh
1823 htext = _(" (%+d heads)") % dh
1824
1824
1825 self.ui.status(_("added %d changesets"
1825 self.ui.status(_("added %d changesets"
1826 " with %d changes to %d files%s\n")
1826 " with %d changes to %d files%s\n")
1827 % (changesets, revisions, files, htext))
1827 % (changesets, revisions, files, htext))
1828
1828
1829 if changesets > 0:
1829 if changesets > 0:
1830 p = lambda: cl.writepending() and self.root or ""
1830 p = lambda: cl.writepending() and self.root or ""
1831 self.hook('pretxnchangegroup', throw=True,
1831 self.hook('pretxnchangegroup', throw=True,
1832 node=hex(cl.node(clstart)), source=srctype,
1832 node=hex(cl.node(clstart)), source=srctype,
1833 url=url, pending=p)
1833 url=url, pending=p)
1834
1834
1835 # make changelog see real files again
1835 # make changelog see real files again
1836 cl.finalize(trp)
1836 cl.finalize(trp)
1837
1837
1838 tr.close()
1838 tr.close()
1839 finally:
1839 finally:
1840 tr.release()
1840 tr.release()
1841 if lock:
1841 if lock:
1842 lock.release()
1842 lock.release()
1843
1843
1844 if changesets > 0:
1844 if changesets > 0:
1845 # forcefully update the on-disk branch cache
1845 # forcefully update the on-disk branch cache
1846 self.ui.debug("updating the branch cache\n")
1846 self.ui.debug("updating the branch cache\n")
1847 self.updatebranchcache()
1847 self.updatebranchcache()
1848 self.hook("changegroup", node=hex(cl.node(clstart)),
1848 self.hook("changegroup", node=hex(cl.node(clstart)),
1849 source=srctype, url=url)
1849 source=srctype, url=url)
1850
1850
1851 for i in xrange(clstart, clend):
1851 for i in xrange(clstart, clend):
1852 self.hook("incoming", node=hex(cl.node(i)),
1852 self.hook("incoming", node=hex(cl.node(i)),
1853 source=srctype, url=url)
1853 source=srctype, url=url)
1854
1854
1855 # never return 0 here:
1855 # never return 0 here:
1856 if dh < 0:
1856 if dh < 0:
1857 return dh - 1
1857 return dh - 1
1858 else:
1858 else:
1859 return dh + 1
1859 return dh + 1
1860
1860
1861 def stream_in(self, remote, requirements):
1861 def stream_in(self, remote, requirements):
1862 lock = self.lock()
1862 lock = self.lock()
1863 try:
1863 try:
1864 fp = remote.stream_out()
1864 fp = remote.stream_out()
1865 l = fp.readline()
1865 l = fp.readline()
1866 try:
1866 try:
1867 resp = int(l)
1867 resp = int(l)
1868 except ValueError:
1868 except ValueError:
1869 raise error.ResponseError(
1869 raise error.ResponseError(
1870 _('Unexpected response from remote server:'), l)
1870 _('Unexpected response from remote server:'), l)
1871 if resp == 1:
1871 if resp == 1:
1872 raise util.Abort(_('operation forbidden by server'))
1872 raise util.Abort(_('operation forbidden by server'))
1873 elif resp == 2:
1873 elif resp == 2:
1874 raise util.Abort(_('locking the remote repository failed'))
1874 raise util.Abort(_('locking the remote repository failed'))
1875 elif resp != 0:
1875 elif resp != 0:
1876 raise util.Abort(_('the server sent an unknown error code'))
1876 raise util.Abort(_('the server sent an unknown error code'))
1877 self.ui.status(_('streaming all changes\n'))
1877 self.ui.status(_('streaming all changes\n'))
1878 l = fp.readline()
1878 l = fp.readline()
1879 try:
1879 try:
1880 total_files, total_bytes = map(int, l.split(' ', 1))
1880 total_files, total_bytes = map(int, l.split(' ', 1))
1881 except (ValueError, TypeError):
1881 except (ValueError, TypeError):
1882 raise error.ResponseError(
1882 raise error.ResponseError(
1883 _('Unexpected response from remote server:'), l)
1883 _('Unexpected response from remote server:'), l)
1884 self.ui.status(_('%d files to transfer, %s of data\n') %
1884 self.ui.status(_('%d files to transfer, %s of data\n') %
1885 (total_files, util.bytecount(total_bytes)))
1885 (total_files, util.bytecount(total_bytes)))
1886 start = time.time()
1886 start = time.time()
1887 for i in xrange(total_files):
1887 for i in xrange(total_files):
1888 # XXX doesn't support '\n' or '\r' in filenames
1888 # XXX doesn't support '\n' or '\r' in filenames
1889 l = fp.readline()
1889 l = fp.readline()
1890 try:
1890 try:
1891 name, size = l.split('\0', 1)
1891 name, size = l.split('\0', 1)
1892 size = int(size)
1892 size = int(size)
1893 except (ValueError, TypeError):
1893 except (ValueError, TypeError):
1894 raise error.ResponseError(
1894 raise error.ResponseError(
1895 _('Unexpected response from remote server:'), l)
1895 _('Unexpected response from remote server:'), l)
1896 self.ui.debug('adding %s (%s)\n' % (name, util.bytecount(size)))
1896 self.ui.debug('adding %s (%s)\n' % (name, util.bytecount(size)))
1897 # for backwards compat, name was partially encoded
1897 # for backwards compat, name was partially encoded
1898 ofp = self.sopener(store.decodedir(name), 'w')
1898 ofp = self.sopener(store.decodedir(name), 'w')
1899 for chunk in util.filechunkiter(fp, limit=size):
1899 for chunk in util.filechunkiter(fp, limit=size):
1900 ofp.write(chunk)
1900 ofp.write(chunk)
1901 ofp.close()
1901 ofp.close()
1902 elapsed = time.time() - start
1902 elapsed = time.time() - start
1903 if elapsed <= 0:
1903 if elapsed <= 0:
1904 elapsed = 0.001
1904 elapsed = 0.001
1905 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
1905 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
1906 (util.bytecount(total_bytes), elapsed,
1906 (util.bytecount(total_bytes), elapsed,
1907 util.bytecount(total_bytes / elapsed)))
1907 util.bytecount(total_bytes / elapsed)))
1908
1908
1909 # new requirements = old non-format requirements + new format-related
1909 # new requirements = old non-format requirements + new format-related
1910 # requirements from the streamed-in repository
1910 # requirements from the streamed-in repository
1911 requirements.update(set(self.requirements) - self.supportedformats)
1911 requirements.update(set(self.requirements) - self.supportedformats)
1912 self._applyrequirements(requirements)
1912 self._applyrequirements(requirements)
1913 self._writerequirements()
1913 self._writerequirements()
1914
1914
1915 self.invalidate()
1915 self.invalidate()
1916 return len(self.heads()) + 1
1916 return len(self.heads()) + 1
1917 finally:
1917 finally:
1918 lock.release()
1918 lock.release()
1919
1919
1920 def clone(self, remote, heads=[], stream=False):
1920 def clone(self, remote, heads=[], stream=False):
1921 '''clone remote repository.
1921 '''clone remote repository.
1922
1922
1923 keyword arguments:
1923 keyword arguments:
1924 heads: list of revs to clone (forces use of pull)
1924 heads: list of revs to clone (forces use of pull)
1925 stream: use streaming clone if possible'''
1925 stream: use streaming clone if possible'''
1926
1926
1927 # now, all clients that can request uncompressed clones can
1927 # now, all clients that can request uncompressed clones can
1928 # read repo formats supported by all servers that can serve
1928 # read repo formats supported by all servers that can serve
1929 # them.
1929 # them.
1930
1930
1931 # if revlog format changes, client will have to check version
1931 # if revlog format changes, client will have to check version
1932 # and format flags on "stream" capability, and use
1932 # and format flags on "stream" capability, and use
1933 # uncompressed only if compatible.
1933 # uncompressed only if compatible.
1934
1934
1935 if stream and not heads:
1935 if stream and not heads:
1936 # 'stream' means remote revlog format is revlogv1 only
1936 # 'stream' means remote revlog format is revlogv1 only
1937 if remote.capable('stream'):
1937 if remote.capable('stream'):
1938 return self.stream_in(remote, set(('revlogv1',)))
1938 return self.stream_in(remote, set(('revlogv1',)))
1939 # otherwise, 'streamreqs' contains the remote revlog format
1939 # otherwise, 'streamreqs' contains the remote revlog format
1940 streamreqs = remote.capable('streamreqs')
1940 streamreqs = remote.capable('streamreqs')
1941 if streamreqs:
1941 if streamreqs:
1942 streamreqs = set(streamreqs.split(','))
1942 streamreqs = set(streamreqs.split(','))
1943 # if we support it, stream in and adjust our requirements
1943 # if we support it, stream in and adjust our requirements
1944 if not streamreqs - self.supportedformats:
1944 if not streamreqs - self.supportedformats:
1945 return self.stream_in(remote, streamreqs)
1945 return self.stream_in(remote, streamreqs)
1946 return self.pull(remote, heads)
1946 return self.pull(remote, heads)
1947
1947
1948 def pushkey(self, namespace, key, old, new):
1948 def pushkey(self, namespace, key, old, new):
1949 self.hook('prepushkey', throw=True, namespace=namespace, key=key,
1949 self.hook('prepushkey', throw=True, namespace=namespace, key=key,
1950 old=old, new=new)
1950 old=old, new=new)
1951 ret = pushkey.push(self, namespace, key, old, new)
1951 ret = pushkey.push(self, namespace, key, old, new)
1952 self.hook('pushkey', namespace=namespace, key=key, old=old, new=new,
1952 self.hook('pushkey', namespace=namespace, key=key, old=old, new=new,
1953 ret=ret)
1953 ret=ret)
1954 return ret
1954 return ret
1955
1955
1956 def listkeys(self, namespace):
1956 def listkeys(self, namespace):
1957 self.hook('prelistkeys', throw=True, namespace=namespace)
1957 self.hook('prelistkeys', throw=True, namespace=namespace)
1958 values = pushkey.list(self, namespace)
1958 values = pushkey.list(self, namespace)
1959 self.hook('listkeys', namespace=namespace, values=values)
1959 self.hook('listkeys', namespace=namespace, values=values)
1960 return values
1960 return values
1961
1961
1962 def debugwireargs(self, one, two, three=None, four=None, five=None):
1962 def debugwireargs(self, one, two, three=None, four=None, five=None):
1963 '''used to test argument passing over the wire'''
1963 '''used to test argument passing over the wire'''
1964 return "%s %s %s %s %s" % (one, two, three, four, five)
1964 return "%s %s %s %s %s" % (one, two, three, four, five)
1965
1965
1966 def savecommitmessage(self, text):
1966 def savecommitmessage(self, text):
1967 fp = self.opener('last-message.txt', 'wb')
1967 fp = self.opener('last-message.txt', 'wb')
1968 try:
1968 try:
1969 fp.write(text)
1969 fp.write(text)
1970 finally:
1970 finally:
1971 fp.close()
1971 fp.close()
1972 return self.pathto(fp.name[len(self.root)+1:])
1972 return self.pathto(fp.name[len(self.root)+1:])
1973
1973
1974 # used to avoid circular references so destructors work
1974 # used to avoid circular references so destructors work
1975 def aftertrans(files):
1975 def aftertrans(files):
1976 renamefiles = [tuple(t) for t in files]
1976 renamefiles = [tuple(t) for t in files]
1977 def a():
1977 def a():
1978 for src, dest in renamefiles:
1978 for src, dest in renamefiles:
1979 util.rename(src, dest)
1979 util.rename(src, dest)
1980 return a
1980 return a
1981
1981
1982 def undoname(fn):
1982 def undoname(fn):
1983 base, name = os.path.split(fn)
1983 base, name = os.path.split(fn)
1984 assert name.startswith('journal')
1984 assert name.startswith('journal')
1985 return os.path.join(base, name.replace('journal', 'undo', 1))
1985 return os.path.join(base, name.replace('journal', 'undo', 1))
1986
1986
1987 def instance(ui, path, create):
1987 def instance(ui, path, create):
1988 return localrepository(ui, util.localpath(path), create)
1988 return localrepository(ui, util.localpath(path), create)
1989
1989
1990 def islocal(path):
1990 def islocal(path):
1991 return True
1991 return True
@@ -1,175 +1,173 b''
1 Use hgrc within $TESTTMP
1 Use hgrc within $TESTTMP
2
2
3 $ HGRCPATH=`pwd`/hgrc
3 $ HGRCPATH=`pwd`/hgrc
4 $ export HGRCPATH
4 $ export HGRCPATH
5
5
6 Basic syntax error
6 Basic syntax error
7
7
8 $ echo "invalid" > $HGRCPATH
8 $ echo "invalid" > $HGRCPATH
9 $ hg version
9 $ hg version
10 hg: parse error at $TESTTMP/hgrc:1: invalid
10 hg: parse error at $TESTTMP/hgrc:1: invalid
11 [255]
11 [255]
12 $ echo "" > $HGRCPATH
12 $ echo "" > $HGRCPATH
13
13
14 Issue1199: Can't use '%' in hgrc (eg url encoded username)
14 Issue1199: Can't use '%' in hgrc (eg url encoded username)
15
15
16 $ hg init "foo%bar"
16 $ hg init "foo%bar"
17 $ hg clone "foo%bar" foobar
17 $ hg clone "foo%bar" foobar
18 updating to branch default
18 updating to branch default
19 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
19 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
20 $ cd foobar
20 $ cd foobar
21 $ cat .hg/hgrc
21 $ cat .hg/hgrc
22 [paths]
22 [paths]
23 default = $TESTTMP/foo%bar
23 default = $TESTTMP/foo%bar
24 $ hg paths
24 $ hg paths
25 default = $TESTTMP/foo%bar
25 default = $TESTTMP/foo%bar
26 $ hg showconfig
26 $ hg showconfig
27 bundle.mainreporoot=$TESTTMP/foobar
27 bundle.mainreporoot=$TESTTMP/foobar
28 paths.default=$TESTTMP/foo%bar
28 paths.default=$TESTTMP/foo%bar
29 $ cd ..
29 $ cd ..
30
30
31 issue1829: wrong indentation
31 issue1829: wrong indentation
32
32
33 $ echo '[foo]' > $HGRCPATH
33 $ echo '[foo]' > $HGRCPATH
34 $ echo ' x = y' >> $HGRCPATH
34 $ echo ' x = y' >> $HGRCPATH
35 $ hg version
35 $ hg version
36 hg: parse error at $TESTTMP/hgrc:2: x = y
36 hg: parse error at $TESTTMP/hgrc:2: x = y
37 [255]
37 [255]
38
38
39 $ python -c "print '[foo]\nbar = a\n b\n c \n de\n fg \nbaz = bif cb \n'" \
39 $ python -c "print '[foo]\nbar = a\n b\n c \n de\n fg \nbaz = bif cb \n'" \
40 > > $HGRCPATH
40 > > $HGRCPATH
41 $ hg showconfig foo
41 $ hg showconfig foo
42 foo.bar=a\nb\nc\nde\nfg
42 foo.bar=a\nb\nc\nde\nfg
43 foo.baz=bif cb
43 foo.baz=bif cb
44
44
45 $ FAKEPATH=/path/to/nowhere
45 $ FAKEPATH=/path/to/nowhere
46 $ export FAKEPATH
46 $ export FAKEPATH
47 $ echo '%include $FAKEPATH/no-such-file' > $HGRCPATH
47 $ echo '%include $FAKEPATH/no-such-file' > $HGRCPATH
48 $ hg version
48 $ hg version
49 Mercurial Distributed SCM (version *) (glob)
49 Mercurial Distributed SCM (version *) (glob)
50 (see http://mercurial.selenic.com for more information)
50 (see http://mercurial.selenic.com for more information)
51
51
52 Copyright (C) 2005-2011 Matt Mackall and others
52 Copyright (C) 2005-2011 Matt Mackall and others
53 This is free software; see the source for copying conditions. There is NO
53 This is free software; see the source for copying conditions. There is NO
54 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
54 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
55 $ unset FAKEPATH
55 $ unset FAKEPATH
56
56
57 make sure unspecified global ui options don't override old values
58
59 $ hg showconfig --config ui.verbose=True --quiet
60 ui.verbose=True
61 ui.quiet=True
62
57 username expansion
63 username expansion
58
64
59 $ olduser=$HGUSER
65 $ olduser=$HGUSER
60 $ unset HGUSER
66 $ unset HGUSER
61
67
62 $ FAKEUSER='John Doe'
68 $ FAKEUSER='John Doe'
63 $ export FAKEUSER
69 $ export FAKEUSER
64 $ echo '[ui]' > $HGRCPATH
70 $ echo '[ui]' > $HGRCPATH
65 $ echo 'username = $FAKEUSER' >> $HGRCPATH
71 $ echo 'username = $FAKEUSER' >> $HGRCPATH
66
72
67 $ hg init usertest
73 $ hg init usertest
68 $ cd usertest
74 $ cd usertest
69 $ touch bar
75 $ touch bar
70 $ hg commit --addremove --quiet -m "added bar"
76 $ hg commit --addremove --quiet -m "added bar"
71 $ hg log --template "{author}\n"
77 $ hg log --template "{author}\n"
72 John Doe
78 John Doe
73 $ cd ..
79 $ cd ..
74
80
75 $ hg showconfig
81 $ hg showconfig
76 ui.username=$FAKEUSER
82 ui.username=$FAKEUSER
77
83
78 $ unset FAKEUSER
84 $ unset FAKEUSER
79 $ HGUSER=$olduser
85 $ HGUSER=$olduser
80 $ export HGUSER
86 $ export HGUSER
81
87
82 showconfig with multiple arguments
88 showconfig with multiple arguments
83
89
84 $ echo "[alias]" > $HGRCPATH
90 $ echo "[alias]" > $HGRCPATH
85 $ echo "log = log -g" >> $HGRCPATH
91 $ echo "log = log -g" >> $HGRCPATH
86 $ echo "[defaults]" >> $HGRCPATH
92 $ echo "[defaults]" >> $HGRCPATH
87 $ echo "identify = -n" >> $HGRCPATH
93 $ echo "identify = -n" >> $HGRCPATH
88 $ hg showconfig alias defaults
94 $ hg showconfig alias defaults
89 alias.log=log -g
95 alias.log=log -g
90 defaults.identify=-n
96 defaults.identify=-n
91 $ hg showconfig alias defaults.identify
97 $ hg showconfig alias defaults.identify
92 abort: only one config item permitted
98 abort: only one config item permitted
93 [255]
99 [255]
94 $ hg showconfig alias.log defaults.identify
100 $ hg showconfig alias.log defaults.identify
95 abort: only one config item permitted
101 abort: only one config item permitted
96 [255]
102 [255]
97
103
98 HGPLAIN
104 HGPLAIN
99
105
100 $ cd ..
106 $ cd ..
101 $ p=`pwd`
107 $ p=`pwd`
102 $ echo "[ui]" > $HGRCPATH
108 $ echo "[ui]" > $HGRCPATH
103 $ echo "debug=true" >> $HGRCPATH
109 $ echo "debug=true" >> $HGRCPATH
104 $ echo "fallbackencoding=ASCII" >> $HGRCPATH
110 $ echo "fallbackencoding=ASCII" >> $HGRCPATH
105 $ echo "quiet=true" >> $HGRCPATH
111 $ echo "quiet=true" >> $HGRCPATH
106 $ echo "slash=true" >> $HGRCPATH
112 $ echo "slash=true" >> $HGRCPATH
107 $ echo "traceback=true" >> $HGRCPATH
113 $ echo "traceback=true" >> $HGRCPATH
108 $ echo "verbose=true" >> $HGRCPATH
114 $ echo "verbose=true" >> $HGRCPATH
109 $ echo "style=~/.hgstyle" >> $HGRCPATH
115 $ echo "style=~/.hgstyle" >> $HGRCPATH
110 $ echo "logtemplate={node}" >> $HGRCPATH
116 $ echo "logtemplate={node}" >> $HGRCPATH
111 $ echo "[defaults]" >> $HGRCPATH
117 $ echo "[defaults]" >> $HGRCPATH
112 $ echo "identify=-n" >> $HGRCPATH
118 $ echo "identify=-n" >> $HGRCPATH
113 $ echo "[alias]" >> $HGRCPATH
119 $ echo "[alias]" >> $HGRCPATH
114 $ echo "log=log -g" >> $HGRCPATH
120 $ echo "log=log -g" >> $HGRCPATH
115
121
116 customized hgrc
122 customized hgrc
117
123
118 $ hg showconfig
124 $ hg showconfig
119 read config from: $TESTTMP/hgrc
125 read config from: $TESTTMP/hgrc
120 $TESTTMP/hgrc:13: alias.log=log -g
126 $TESTTMP/hgrc:13: alias.log=log -g
121 $TESTTMP/hgrc:11: defaults.identify=-n
127 $TESTTMP/hgrc:11: defaults.identify=-n
122 $TESTTMP/hgrc:2: ui.debug=true
128 $TESTTMP/hgrc:2: ui.debug=true
123 $TESTTMP/hgrc:3: ui.fallbackencoding=ASCII
129 $TESTTMP/hgrc:3: ui.fallbackencoding=ASCII
124 $TESTTMP/hgrc:4: ui.quiet=true
130 $TESTTMP/hgrc:4: ui.quiet=true
125 $TESTTMP/hgrc:5: ui.slash=true
131 $TESTTMP/hgrc:5: ui.slash=true
126 $TESTTMP/hgrc:6: ui.traceback=true
132 $TESTTMP/hgrc:6: ui.traceback=true
127 $TESTTMP/hgrc:7: ui.verbose=true
133 $TESTTMP/hgrc:7: ui.verbose=true
128 $TESTTMP/hgrc:8: ui.style=~/.hgstyle
134 $TESTTMP/hgrc:8: ui.style=~/.hgstyle
129 $TESTTMP/hgrc:9: ui.logtemplate={node}
135 $TESTTMP/hgrc:9: ui.logtemplate={node}
130
136
131 plain hgrc
137 plain hgrc
132
138
133 $ HGPLAIN=; export HGPLAIN
139 $ HGPLAIN=; export HGPLAIN
134 $ hg showconfig --config ui.traceback=True --debug
140 $ hg showconfig --config ui.traceback=True --debug
135 read config from: $TESTTMP/hgrc
141 read config from: $TESTTMP/hgrc
136 none: ui.traceback=True
142 none: ui.traceback=True
137 none: ui.verbose=False
138 none: ui.debug=True
143 none: ui.debug=True
139 none: ui.quiet=False
140
144
141 plain mode with exceptions
145 plain mode with exceptions
142
146
143 $ cat > plain.py <<EOF
147 $ cat > plain.py <<EOF
144 > def uisetup(ui):
148 > def uisetup(ui):
145 > ui.write('plain: %r\n' % ui.plain())
149 > ui.write('plain: %r\n' % ui.plain())
146 > EOF
150 > EOF
147 $ echo "[extensions]" >> $HGRCPATH
151 $ echo "[extensions]" >> $HGRCPATH
148 $ echo "plain=./plain.py" >> $HGRCPATH
152 $ echo "plain=./plain.py" >> $HGRCPATH
149 $ HGPLAINEXCEPT=; export HGPLAINEXCEPT
153 $ HGPLAINEXCEPT=; export HGPLAINEXCEPT
150 $ hg showconfig --config ui.traceback=True --debug
154 $ hg showconfig --config ui.traceback=True --debug
151 plain: True
155 plain: True
152 read config from: $TESTTMP/hgrc
156 read config from: $TESTTMP/hgrc
153 $TESTTMP/hgrc:15: extensions.plain=./plain.py
157 $TESTTMP/hgrc:15: extensions.plain=./plain.py
154 none: ui.traceback=True
158 none: ui.traceback=True
155 none: ui.verbose=False
156 none: ui.debug=True
159 none: ui.debug=True
157 none: ui.quiet=False
158 $ unset HGPLAIN
160 $ unset HGPLAIN
159 $ hg showconfig --config ui.traceback=True --debug
161 $ hg showconfig --config ui.traceback=True --debug
160 plain: True
162 plain: True
161 read config from: $TESTTMP/hgrc
163 read config from: $TESTTMP/hgrc
162 $TESTTMP/hgrc:15: extensions.plain=./plain.py
164 $TESTTMP/hgrc:15: extensions.plain=./plain.py
163 none: ui.traceback=True
165 none: ui.traceback=True
164 none: ui.verbose=False
165 none: ui.debug=True
166 none: ui.debug=True
166 none: ui.quiet=False
167 $ HGPLAINEXCEPT=i18n; export HGPLAINEXCEPT
167 $ HGPLAINEXCEPT=i18n; export HGPLAINEXCEPT
168 $ hg showconfig --config ui.traceback=True --debug
168 $ hg showconfig --config ui.traceback=True --debug
169 plain: True
169 plain: True
170 read config from: $TESTTMP/hgrc
170 read config from: $TESTTMP/hgrc
171 $TESTTMP/hgrc:15: extensions.plain=./plain.py
171 $TESTTMP/hgrc:15: extensions.plain=./plain.py
172 none: ui.traceback=True
172 none: ui.traceback=True
173 none: ui.verbose=False
174 none: ui.debug=True
173 none: ui.debug=True
175 none: ui.quiet=False
@@ -1,273 +1,273 b''
1 $ hg init repo
1 $ hg init repo
2 $ cd repo
2 $ cd repo
3 $ echo 123 > a
3 $ echo 123 > a
4 $ echo 123 > c
4 $ echo 123 > c
5 $ echo 123 > e
5 $ echo 123 > e
6 $ hg add a c e
6 $ hg add a c e
7 $ hg commit -m "first" a c e
7 $ hg commit -m "first" a c e
8
8
9 nothing changed
9 nothing changed
10
10
11 $ hg revert
11 $ hg revert
12 abort: no files or directories specified
12 abort: no files or directories specified
13 (use --all to discard all changes)
13 (use --all to revert all files)
14 [255]
14 [255]
15
15
16 $ echo 123 > b
16 $ echo 123 > b
17
17
18 should show b unknown
18 should show b unknown
19
19
20 $ hg status
20 $ hg status
21 ? b
21 ? b
22 $ echo 12 > c
22 $ echo 12 > c
23
23
24 should show b unknown and c modified
24 should show b unknown and c modified
25
25
26 $ hg status
26 $ hg status
27 M c
27 M c
28 ? b
28 ? b
29 $ hg add b
29 $ hg add b
30
30
31 should show b added and c modified
31 should show b added and c modified
32
32
33 $ hg status
33 $ hg status
34 M c
34 M c
35 A b
35 A b
36 $ hg rm a
36 $ hg rm a
37
37
38 should show a removed, b added and c modified
38 should show a removed, b added and c modified
39
39
40 $ hg status
40 $ hg status
41 M c
41 M c
42 A b
42 A b
43 R a
43 R a
44 $ hg revert a
44 $ hg revert a
45
45
46 should show b added, copy saved, and c modified
46 should show b added, copy saved, and c modified
47
47
48 $ hg status
48 $ hg status
49 M c
49 M c
50 A b
50 A b
51 $ hg revert b
51 $ hg revert b
52
52
53 should show b unknown, and c modified
53 should show b unknown, and c modified
54
54
55 $ hg status
55 $ hg status
56 M c
56 M c
57 ? b
57 ? b
58 $ hg revert --no-backup c
58 $ hg revert --no-backup c
59
59
60 should show unknown: b
60 should show unknown: b
61
61
62 $ hg status
62 $ hg status
63 ? b
63 ? b
64 $ hg add b
64 $ hg add b
65
65
66 should show b added
66 should show b added
67
67
68 $ hg status b
68 $ hg status b
69 A b
69 A b
70 $ rm b
70 $ rm b
71
71
72 should show b deleted
72 should show b deleted
73
73
74 $ hg status b
74 $ hg status b
75 ! b
75 ! b
76 $ hg revert -v b
76 $ hg revert -v b
77 forgetting b
77 forgetting b
78
78
79 should not find b
79 should not find b
80
80
81 $ hg status b
81 $ hg status b
82 b: No such file or directory
82 b: No such file or directory
83
83
84 should show a c e
84 should show a c e
85
85
86 $ ls
86 $ ls
87 a
87 a
88 c
88 c
89 e
89 e
90
90
91 should verbosely save backup to e.orig
91 should verbosely save backup to e.orig
92
92
93 $ echo z > e
93 $ echo z > e
94 $ hg revert --all -v
94 $ hg revert --all -v
95 saving current version of e as e.orig
95 saving current version of e as e.orig
96 reverting e
96 reverting e
97
97
98 should say no changes needed
98 should say no changes needed
99
99
100 $ hg revert a
100 $ hg revert a
101 no changes needed to a
101 no changes needed to a
102
102
103 should say file not managed
103 should say file not managed
104
104
105 $ echo q > q
105 $ echo q > q
106 $ hg revert q
106 $ hg revert q
107 file not managed: q
107 file not managed: q
108 $ rm q
108 $ rm q
109
109
110 should say file not found
110 should say file not found
111
111
112 $ hg revert notfound
112 $ hg revert notfound
113 notfound: no such file in rev 334a9e57682c
113 notfound: no such file in rev 334a9e57682c
114 $ touch d
114 $ touch d
115 $ hg add d
115 $ hg add d
116 $ hg rm a
116 $ hg rm a
117 $ hg commit -m "second"
117 $ hg commit -m "second"
118 $ echo z > z
118 $ echo z > z
119 $ hg add z
119 $ hg add z
120 $ hg st
120 $ hg st
121 A z
121 A z
122 ? e.orig
122 ? e.orig
123
123
124 should add a, remove d, forget z
124 should add a, remove d, forget z
125
125
126 $ hg revert --all -r0
126 $ hg revert --all -r0
127 adding a
127 adding a
128 removing d
128 removing d
129 forgetting z
129 forgetting z
130
130
131 should forget a, undelete d
131 should forget a, undelete d
132
132
133 $ hg revert --all -rtip
133 $ hg revert --all -rtip
134 forgetting a
134 forgetting a
135 undeleting d
135 undeleting d
136 $ rm a *.orig
136 $ rm a *.orig
137
137
138 should silently add a
138 should silently add a
139
139
140 $ hg revert -r0 a
140 $ hg revert -r0 a
141 $ hg st a
141 $ hg st a
142 A a
142 A a
143 $ hg rm d
143 $ hg rm d
144 $ hg st d
144 $ hg st d
145 R d
145 R d
146
146
147 should silently keep d removed
147 should silently keep d removed
148
148
149 $ hg revert -r0 d
149 $ hg revert -r0 d
150 $ hg st d
150 $ hg st d
151 R d
151 R d
152
152
153 $ hg update -C
153 $ hg update -C
154 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
154 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
155 $ chmod +x c
155 $ chmod +x c
156 $ hg revert --all
156 $ hg revert --all
157 reverting c
157 reverting c
158
158
159 should print non-executable
159 should print non-executable
160
160
161 $ test -x c || echo non-executable
161 $ test -x c || echo non-executable
162 non-executable
162 non-executable
163
163
164 $ chmod +x c
164 $ chmod +x c
165 $ hg commit -m exe
165 $ hg commit -m exe
166
166
167 $ chmod -x c
167 $ chmod -x c
168 $ hg revert --all
168 $ hg revert --all
169 reverting c
169 reverting c
170
170
171 should print executable
171 should print executable
172
172
173 $ test -x c && echo executable
173 $ test -x c && echo executable
174 executable
174 executable
175
175
176 $ cd ..
176 $ cd ..
177
177
178
178
179 Issue241: update and revert produces inconsistent repositories
179 Issue241: update and revert produces inconsistent repositories
180
180
181 $ hg init a
181 $ hg init a
182 $ cd a
182 $ cd a
183 $ echo a >> a
183 $ echo a >> a
184 $ hg commit -A -d '1 0' -m a
184 $ hg commit -A -d '1 0' -m a
185 adding a
185 adding a
186 $ echo a >> a
186 $ echo a >> a
187 $ hg commit -d '2 0' -m a
187 $ hg commit -d '2 0' -m a
188 $ hg update 0
188 $ hg update 0
189 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
189 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
190 $ mkdir b
190 $ mkdir b
191 $ echo b > b/b
191 $ echo b > b/b
192
192
193 should fail - no arguments
193 should fail - no arguments
194
194
195 $ hg revert -rtip
195 $ hg revert -rtip
196 abort: no files or directories specified
196 abort: no files or directories specified
197 (use --all to revert all files, or 'hg update 1' to update)
197 (use --all to revert all files, or 'hg update 1' to update)
198 [255]
198 [255]
199
199
200 should succeed
200 should succeed
201
201
202 $ hg revert --all -rtip
202 $ hg revert --all -rtip
203 reverting a
203 reverting a
204
204
205
205
206 Issue332: confusing message when reverting directory
206 Issue332: confusing message when reverting directory
207
207
208 $ hg ci -A -m b
208 $ hg ci -A -m b
209 adding b/b
209 adding b/b
210 created new head
210 created new head
211 $ echo foobar > b/b
211 $ echo foobar > b/b
212 $ mkdir newdir
212 $ mkdir newdir
213 $ echo foo > newdir/newfile
213 $ echo foo > newdir/newfile
214 $ hg add newdir/newfile
214 $ hg add newdir/newfile
215 $ hg revert b newdir
215 $ hg revert b newdir
216 reverting b/b
216 reverting b/b
217 forgetting newdir/newfile
217 forgetting newdir/newfile
218 $ echo foobar > b/b
218 $ echo foobar > b/b
219 $ hg revert .
219 $ hg revert .
220 reverting b/b
220 reverting b/b
221
221
222
222
223 reverting a rename target should revert the source
223 reverting a rename target should revert the source
224
224
225 $ hg mv a newa
225 $ hg mv a newa
226 $ hg revert newa
226 $ hg revert newa
227 $ hg st a newa
227 $ hg st a newa
228 ? newa
228 ? newa
229
229
230 $ cd ..
230 $ cd ..
231
231
232 $ hg init ignored
232 $ hg init ignored
233 $ cd ignored
233 $ cd ignored
234 $ echo '^ignored$' > .hgignore
234 $ echo '^ignored$' > .hgignore
235 $ echo '^ignoreddir$' >> .hgignore
235 $ echo '^ignoreddir$' >> .hgignore
236 $ echo '^removed$' >> .hgignore
236 $ echo '^removed$' >> .hgignore
237
237
238 $ mkdir ignoreddir
238 $ mkdir ignoreddir
239 $ touch ignoreddir/file
239 $ touch ignoreddir/file
240 $ touch ignoreddir/removed
240 $ touch ignoreddir/removed
241 $ touch ignored
241 $ touch ignored
242 $ touch removed
242 $ touch removed
243
243
244 4 ignored files (we will add/commit everything)
244 4 ignored files (we will add/commit everything)
245
245
246 $ hg st -A -X .hgignore
246 $ hg st -A -X .hgignore
247 I ignored
247 I ignored
248 I ignoreddir/file
248 I ignoreddir/file
249 I ignoreddir/removed
249 I ignoreddir/removed
250 I removed
250 I removed
251 $ hg ci -qAm 'add files' ignored ignoreddir/file ignoreddir/removed removed
251 $ hg ci -qAm 'add files' ignored ignoreddir/file ignoreddir/removed removed
252
252
253 $ echo >> ignored
253 $ echo >> ignored
254 $ echo >> ignoreddir/file
254 $ echo >> ignoreddir/file
255 $ hg rm removed ignoreddir/removed
255 $ hg rm removed ignoreddir/removed
256
256
257 should revert ignored* and undelete *removed
257 should revert ignored* and undelete *removed
258
258
259 $ hg revert -a --no-backup
259 $ hg revert -a --no-backup
260 reverting ignored
260 reverting ignored
261 reverting ignoreddir/file
261 reverting ignoreddir/file
262 undeleting ignoreddir/removed
262 undeleting ignoreddir/removed
263 undeleting removed
263 undeleting removed
264 $ hg st -mardi
264 $ hg st -mardi
265
265
266 $ hg up -qC
266 $ hg up -qC
267 $ echo >> ignored
267 $ echo >> ignored
268 $ hg rm removed
268 $ hg rm removed
269
269
270 should silently revert the named files
270 should silently revert the named files
271
271
272 $ hg revert --no-backup ignored removed
272 $ hg revert --no-backup ignored removed
273 $ hg st -mardi
273 $ hg st -mardi
@@ -1,294 +1,296 b''
1 $ echo "[extensions]" >> $HGRCPATH
1 $ echo "[extensions]" >> $HGRCPATH
2 $ echo "color=" >> $HGRCPATH
2 $ echo "color=" >> $HGRCPATH
3 $ echo "[color]" >> $HGRCPATH
3 $ echo "[color]" >> $HGRCPATH
4 $ echo "mode=ansi" >> $HGRCPATH
4 $ echo "mode=ansi" >> $HGRCPATH
5 Terminfo codes compatibility fix
5 Terminfo codes compatibility fix
6 $ echo "color.none=0" >> $HGRCPATH
6 $ echo "color.none=0" >> $HGRCPATH
7
7
8 $ hg init repo1
8 $ hg init repo1
9 $ cd repo1
9 $ cd repo1
10 $ mkdir a b a/1 b/1 b/2
10 $ mkdir a b a/1 b/1 b/2
11 $ touch in_root a/in_a b/in_b a/1/in_a_1 b/1/in_b_1 b/2/in_b_2
11 $ touch in_root a/in_a b/in_b a/1/in_a_1 b/1/in_b_1 b/2/in_b_2
12
12
13 hg status in repo root:
13 hg status in repo root:
14
14
15 $ hg status --color=always
15 $ hg status --color=always
16 \x1b[0;35;1;4m? a/1/in_a_1\x1b[0m (esc)
16 \x1b[0;35;1;4m? a/1/in_a_1\x1b[0m (esc)
17 \x1b[0;35;1;4m? a/in_a\x1b[0m (esc)
17 \x1b[0;35;1;4m? a/in_a\x1b[0m (esc)
18 \x1b[0;35;1;4m? b/1/in_b_1\x1b[0m (esc)
18 \x1b[0;35;1;4m? b/1/in_b_1\x1b[0m (esc)
19 \x1b[0;35;1;4m? b/2/in_b_2\x1b[0m (esc)
19 \x1b[0;35;1;4m? b/2/in_b_2\x1b[0m (esc)
20 \x1b[0;35;1;4m? b/in_b\x1b[0m (esc)
20 \x1b[0;35;1;4m? b/in_b\x1b[0m (esc)
21 \x1b[0;35;1;4m? in_root\x1b[0m (esc)
21 \x1b[0;35;1;4m? in_root\x1b[0m (esc)
22
22
23 hg status . in repo root:
23 hg status . in repo root:
24
24
25 $ hg status --color=always .
25 $ hg status --color=always .
26 \x1b[0;35;1;4m? a/1/in_a_1\x1b[0m (esc)
26 \x1b[0;35;1;4m? a/1/in_a_1\x1b[0m (esc)
27 \x1b[0;35;1;4m? a/in_a\x1b[0m (esc)
27 \x1b[0;35;1;4m? a/in_a\x1b[0m (esc)
28 \x1b[0;35;1;4m? b/1/in_b_1\x1b[0m (esc)
28 \x1b[0;35;1;4m? b/1/in_b_1\x1b[0m (esc)
29 \x1b[0;35;1;4m? b/2/in_b_2\x1b[0m (esc)
29 \x1b[0;35;1;4m? b/2/in_b_2\x1b[0m (esc)
30 \x1b[0;35;1;4m? b/in_b\x1b[0m (esc)
30 \x1b[0;35;1;4m? b/in_b\x1b[0m (esc)
31 \x1b[0;35;1;4m? in_root\x1b[0m (esc)
31 \x1b[0;35;1;4m? in_root\x1b[0m (esc)
32
32
33 $ hg status --color=always --cwd a
33 $ hg status --color=always --cwd a
34 \x1b[0;35;1;4m? a/1/in_a_1\x1b[0m (esc)
34 \x1b[0;35;1;4m? a/1/in_a_1\x1b[0m (esc)
35 \x1b[0;35;1;4m? a/in_a\x1b[0m (esc)
35 \x1b[0;35;1;4m? a/in_a\x1b[0m (esc)
36 \x1b[0;35;1;4m? b/1/in_b_1\x1b[0m (esc)
36 \x1b[0;35;1;4m? b/1/in_b_1\x1b[0m (esc)
37 \x1b[0;35;1;4m? b/2/in_b_2\x1b[0m (esc)
37 \x1b[0;35;1;4m? b/2/in_b_2\x1b[0m (esc)
38 \x1b[0;35;1;4m? b/in_b\x1b[0m (esc)
38 \x1b[0;35;1;4m? b/in_b\x1b[0m (esc)
39 \x1b[0;35;1;4m? in_root\x1b[0m (esc)
39 \x1b[0;35;1;4m? in_root\x1b[0m (esc)
40 $ hg status --color=always --cwd a .
40 $ hg status --color=always --cwd a .
41 \x1b[0;35;1;4m? 1/in_a_1\x1b[0m (esc)
41 \x1b[0;35;1;4m? 1/in_a_1\x1b[0m (esc)
42 \x1b[0;35;1;4m? in_a\x1b[0m (esc)
42 \x1b[0;35;1;4m? in_a\x1b[0m (esc)
43 $ hg status --color=always --cwd a ..
43 $ hg status --color=always --cwd a ..
44 \x1b[0;35;1;4m? 1/in_a_1\x1b[0m (esc)
44 \x1b[0;35;1;4m? 1/in_a_1\x1b[0m (esc)
45 \x1b[0;35;1;4m? in_a\x1b[0m (esc)
45 \x1b[0;35;1;4m? in_a\x1b[0m (esc)
46 \x1b[0;35;1;4m? ../b/1/in_b_1\x1b[0m (esc)
46 \x1b[0;35;1;4m? ../b/1/in_b_1\x1b[0m (esc)
47 \x1b[0;35;1;4m? ../b/2/in_b_2\x1b[0m (esc)
47 \x1b[0;35;1;4m? ../b/2/in_b_2\x1b[0m (esc)
48 \x1b[0;35;1;4m? ../b/in_b\x1b[0m (esc)
48 \x1b[0;35;1;4m? ../b/in_b\x1b[0m (esc)
49 \x1b[0;35;1;4m? ../in_root\x1b[0m (esc)
49 \x1b[0;35;1;4m? ../in_root\x1b[0m (esc)
50
50
51 $ hg status --color=always --cwd b
51 $ hg status --color=always --cwd b
52 \x1b[0;35;1;4m? a/1/in_a_1\x1b[0m (esc)
52 \x1b[0;35;1;4m? a/1/in_a_1\x1b[0m (esc)
53 \x1b[0;35;1;4m? a/in_a\x1b[0m (esc)
53 \x1b[0;35;1;4m? a/in_a\x1b[0m (esc)
54 \x1b[0;35;1;4m? b/1/in_b_1\x1b[0m (esc)
54 \x1b[0;35;1;4m? b/1/in_b_1\x1b[0m (esc)
55 \x1b[0;35;1;4m? b/2/in_b_2\x1b[0m (esc)
55 \x1b[0;35;1;4m? b/2/in_b_2\x1b[0m (esc)
56 \x1b[0;35;1;4m? b/in_b\x1b[0m (esc)
56 \x1b[0;35;1;4m? b/in_b\x1b[0m (esc)
57 \x1b[0;35;1;4m? in_root\x1b[0m (esc)
57 \x1b[0;35;1;4m? in_root\x1b[0m (esc)
58 $ hg status --color=always --cwd b .
58 $ hg status --color=always --cwd b .
59 \x1b[0;35;1;4m? 1/in_b_1\x1b[0m (esc)
59 \x1b[0;35;1;4m? 1/in_b_1\x1b[0m (esc)
60 \x1b[0;35;1;4m? 2/in_b_2\x1b[0m (esc)
60 \x1b[0;35;1;4m? 2/in_b_2\x1b[0m (esc)
61 \x1b[0;35;1;4m? in_b\x1b[0m (esc)
61 \x1b[0;35;1;4m? in_b\x1b[0m (esc)
62 $ hg status --color=always --cwd b ..
62 $ hg status --color=always --cwd b ..
63 \x1b[0;35;1;4m? ../a/1/in_a_1\x1b[0m (esc)
63 \x1b[0;35;1;4m? ../a/1/in_a_1\x1b[0m (esc)
64 \x1b[0;35;1;4m? ../a/in_a\x1b[0m (esc)
64 \x1b[0;35;1;4m? ../a/in_a\x1b[0m (esc)
65 \x1b[0;35;1;4m? 1/in_b_1\x1b[0m (esc)
65 \x1b[0;35;1;4m? 1/in_b_1\x1b[0m (esc)
66 \x1b[0;35;1;4m? 2/in_b_2\x1b[0m (esc)
66 \x1b[0;35;1;4m? 2/in_b_2\x1b[0m (esc)
67 \x1b[0;35;1;4m? in_b\x1b[0m (esc)
67 \x1b[0;35;1;4m? in_b\x1b[0m (esc)
68 \x1b[0;35;1;4m? ../in_root\x1b[0m (esc)
68 \x1b[0;35;1;4m? ../in_root\x1b[0m (esc)
69
69
70 $ hg status --color=always --cwd a/1
70 $ hg status --color=always --cwd a/1
71 \x1b[0;35;1;4m? a/1/in_a_1\x1b[0m (esc)
71 \x1b[0;35;1;4m? a/1/in_a_1\x1b[0m (esc)
72 \x1b[0;35;1;4m? a/in_a\x1b[0m (esc)
72 \x1b[0;35;1;4m? a/in_a\x1b[0m (esc)
73 \x1b[0;35;1;4m? b/1/in_b_1\x1b[0m (esc)
73 \x1b[0;35;1;4m? b/1/in_b_1\x1b[0m (esc)
74 \x1b[0;35;1;4m? b/2/in_b_2\x1b[0m (esc)
74 \x1b[0;35;1;4m? b/2/in_b_2\x1b[0m (esc)
75 \x1b[0;35;1;4m? b/in_b\x1b[0m (esc)
75 \x1b[0;35;1;4m? b/in_b\x1b[0m (esc)
76 \x1b[0;35;1;4m? in_root\x1b[0m (esc)
76 \x1b[0;35;1;4m? in_root\x1b[0m (esc)
77 $ hg status --color=always --cwd a/1 .
77 $ hg status --color=always --cwd a/1 .
78 \x1b[0;35;1;4m? in_a_1\x1b[0m (esc)
78 \x1b[0;35;1;4m? in_a_1\x1b[0m (esc)
79 $ hg status --color=always --cwd a/1 ..
79 $ hg status --color=always --cwd a/1 ..
80 \x1b[0;35;1;4m? in_a_1\x1b[0m (esc)
80 \x1b[0;35;1;4m? in_a_1\x1b[0m (esc)
81 \x1b[0;35;1;4m? ../in_a\x1b[0m (esc)
81 \x1b[0;35;1;4m? ../in_a\x1b[0m (esc)
82
82
83 $ hg status --color=always --cwd b/1
83 $ hg status --color=always --cwd b/1
84 \x1b[0;35;1;4m? a/1/in_a_1\x1b[0m (esc)
84 \x1b[0;35;1;4m? a/1/in_a_1\x1b[0m (esc)
85 \x1b[0;35;1;4m? a/in_a\x1b[0m (esc)
85 \x1b[0;35;1;4m? a/in_a\x1b[0m (esc)
86 \x1b[0;35;1;4m? b/1/in_b_1\x1b[0m (esc)
86 \x1b[0;35;1;4m? b/1/in_b_1\x1b[0m (esc)
87 \x1b[0;35;1;4m? b/2/in_b_2\x1b[0m (esc)
87 \x1b[0;35;1;4m? b/2/in_b_2\x1b[0m (esc)
88 \x1b[0;35;1;4m? b/in_b\x1b[0m (esc)
88 \x1b[0;35;1;4m? b/in_b\x1b[0m (esc)
89 \x1b[0;35;1;4m? in_root\x1b[0m (esc)
89 \x1b[0;35;1;4m? in_root\x1b[0m (esc)
90 $ hg status --color=always --cwd b/1 .
90 $ hg status --color=always --cwd b/1 .
91 \x1b[0;35;1;4m? in_b_1\x1b[0m (esc)
91 \x1b[0;35;1;4m? in_b_1\x1b[0m (esc)
92 $ hg status --color=always --cwd b/1 ..
92 $ hg status --color=always --cwd b/1 ..
93 \x1b[0;35;1;4m? in_b_1\x1b[0m (esc)
93 \x1b[0;35;1;4m? in_b_1\x1b[0m (esc)
94 \x1b[0;35;1;4m? ../2/in_b_2\x1b[0m (esc)
94 \x1b[0;35;1;4m? ../2/in_b_2\x1b[0m (esc)
95 \x1b[0;35;1;4m? ../in_b\x1b[0m (esc)
95 \x1b[0;35;1;4m? ../in_b\x1b[0m (esc)
96
96
97 $ hg status --color=always --cwd b/2
97 $ hg status --color=always --cwd b/2
98 \x1b[0;35;1;4m? a/1/in_a_1\x1b[0m (esc)
98 \x1b[0;35;1;4m? a/1/in_a_1\x1b[0m (esc)
99 \x1b[0;35;1;4m? a/in_a\x1b[0m (esc)
99 \x1b[0;35;1;4m? a/in_a\x1b[0m (esc)
100 \x1b[0;35;1;4m? b/1/in_b_1\x1b[0m (esc)
100 \x1b[0;35;1;4m? b/1/in_b_1\x1b[0m (esc)
101 \x1b[0;35;1;4m? b/2/in_b_2\x1b[0m (esc)
101 \x1b[0;35;1;4m? b/2/in_b_2\x1b[0m (esc)
102 \x1b[0;35;1;4m? b/in_b\x1b[0m (esc)
102 \x1b[0;35;1;4m? b/in_b\x1b[0m (esc)
103 \x1b[0;35;1;4m? in_root\x1b[0m (esc)
103 \x1b[0;35;1;4m? in_root\x1b[0m (esc)
104 $ hg status --color=always --cwd b/2 .
104 $ hg status --color=always --cwd b/2 .
105 \x1b[0;35;1;4m? in_b_2\x1b[0m (esc)
105 \x1b[0;35;1;4m? in_b_2\x1b[0m (esc)
106 $ hg status --color=always --cwd b/2 ..
106 $ hg status --color=always --cwd b/2 ..
107 \x1b[0;35;1;4m? ../1/in_b_1\x1b[0m (esc)
107 \x1b[0;35;1;4m? ../1/in_b_1\x1b[0m (esc)
108 \x1b[0;35;1;4m? in_b_2\x1b[0m (esc)
108 \x1b[0;35;1;4m? in_b_2\x1b[0m (esc)
109 \x1b[0;35;1;4m? ../in_b\x1b[0m (esc)
109 \x1b[0;35;1;4m? ../in_b\x1b[0m (esc)
110 $ cd ..
110 $ cd ..
111
111
112 $ hg init repo2
112 $ hg init repo2
113 $ cd repo2
113 $ cd repo2
114 $ touch modified removed deleted ignored
114 $ touch modified removed deleted ignored
115 $ echo "^ignored$" > .hgignore
115 $ echo "^ignored$" > .hgignore
116 $ hg ci -A -m 'initial checkin'
116 $ hg ci -A -m 'initial checkin'
117 adding .hgignore
117 adding .hgignore
118 adding deleted
118 adding deleted
119 adding modified
119 adding modified
120 adding removed
120 adding removed
121 $ touch modified added unknown ignored
121 $ touch modified added unknown ignored
122 $ hg add added
122 $ hg add added
123 $ hg remove removed
123 $ hg remove removed
124 $ rm deleted
124 $ rm deleted
125
125
126 hg status:
126 hg status:
127
127
128 $ hg status --color=always
128 $ hg status --color=always
129 \x1b[0;32;1mA added\x1b[0m (esc)
129 \x1b[0;32;1mA added\x1b[0m (esc)
130 \x1b[0;31;1mR removed\x1b[0m (esc)
130 \x1b[0;31;1mR removed\x1b[0m (esc)
131 \x1b[0;36;1;4m! deleted\x1b[0m (esc)
131 \x1b[0;36;1;4m! deleted\x1b[0m (esc)
132 \x1b[0;35;1;4m? unknown\x1b[0m (esc)
132 \x1b[0;35;1;4m? unknown\x1b[0m (esc)
133
133
134 hg status modified added removed deleted unknown never-existed ignored:
134 hg status modified added removed deleted unknown never-existed ignored:
135
135
136 $ hg status --color=always modified added removed deleted unknown never-existed ignored
136 $ hg status --color=always modified added removed deleted unknown never-existed ignored
137 never-existed: No such file or directory
137 never-existed: No such file or directory
138 \x1b[0;32;1mA added\x1b[0m (esc)
138 \x1b[0;32;1mA added\x1b[0m (esc)
139 \x1b[0;31;1mR removed\x1b[0m (esc)
139 \x1b[0;31;1mR removed\x1b[0m (esc)
140 \x1b[0;36;1;4m! deleted\x1b[0m (esc)
140 \x1b[0;36;1;4m! deleted\x1b[0m (esc)
141 \x1b[0;35;1;4m? unknown\x1b[0m (esc)
141 \x1b[0;35;1;4m? unknown\x1b[0m (esc)
142
142
143 $ hg copy modified copied
143 $ hg copy modified copied
144
144
145 hg status -C:
145 hg status -C:
146
146
147 $ hg status --color=always -C
147 $ hg status --color=always -C
148 \x1b[0;32;1mA added\x1b[0m (esc)
148 \x1b[0;32;1mA added\x1b[0m (esc)
149 \x1b[0;32;1mA copied\x1b[0m (esc)
149 \x1b[0;32;1mA copied\x1b[0m (esc)
150 \x1b[0;0m modified\x1b[0m (esc)
150 \x1b[0;0m modified\x1b[0m (esc)
151 \x1b[0;31;1mR removed\x1b[0m (esc)
151 \x1b[0;31;1mR removed\x1b[0m (esc)
152 \x1b[0;36;1;4m! deleted\x1b[0m (esc)
152 \x1b[0;36;1;4m! deleted\x1b[0m (esc)
153 \x1b[0;35;1;4m? unknown\x1b[0m (esc)
153 \x1b[0;35;1;4m? unknown\x1b[0m (esc)
154
154
155 hg status -A:
155 hg status -A:
156
156
157 $ hg status --color=always -A
157 $ hg status --color=always -A
158 \x1b[0;32;1mA added\x1b[0m (esc)
158 \x1b[0;32;1mA added\x1b[0m (esc)
159 \x1b[0;32;1mA copied\x1b[0m (esc)
159 \x1b[0;32;1mA copied\x1b[0m (esc)
160 \x1b[0;0m modified\x1b[0m (esc)
160 \x1b[0;0m modified\x1b[0m (esc)
161 \x1b[0;31;1mR removed\x1b[0m (esc)
161 \x1b[0;31;1mR removed\x1b[0m (esc)
162 \x1b[0;36;1;4m! deleted\x1b[0m (esc)
162 \x1b[0;36;1;4m! deleted\x1b[0m (esc)
163 \x1b[0;35;1;4m? unknown\x1b[0m (esc)
163 \x1b[0;35;1;4m? unknown\x1b[0m (esc)
164 \x1b[0;30;1mI ignored\x1b[0m (esc)
164 \x1b[0;30;1mI ignored\x1b[0m (esc)
165 \x1b[0;0mC .hgignore\x1b[0m (esc)
165 \x1b[0;0mC .hgignore\x1b[0m (esc)
166 \x1b[0;0mC modified\x1b[0m (esc)
166 \x1b[0;0mC modified\x1b[0m (esc)
167
167
168 hg status -A (with terminfo color):
168 hg status -A (with terminfo color):
169
169
170 $ TERM=xterm hg status --config color.mode=terminfo --color=always -A
170 $ mkdir $TESTTMP/terminfo
171 $ TERMINFO=$TESTTMP/terminfo tic $TESTDIR/hgterm.ti
172 $ TERM=hgterm TERMINFO=$TESTTMP/terminfo hg status --config color.mode=terminfo --color=always -A
171 \x1b[30m\x1b[32m\x1b[1mA added\x1b[30m (esc)
173 \x1b[30m\x1b[32m\x1b[1mA added\x1b[30m (esc)
172 \x1b[30m\x1b[32m\x1b[1mA copied\x1b[30m (esc)
174 \x1b[30m\x1b[32m\x1b[1mA copied\x1b[30m (esc)
173 \x1b[30m\x1b[30m modified\x1b[30m (esc)
175 \x1b[30m\x1b[30m modified\x1b[30m (esc)
174 \x1b[30m\x1b[31m\x1b[1mR removed\x1b[30m (esc)
176 \x1b[30m\x1b[31m\x1b[1mR removed\x1b[30m (esc)
175 \x1b[30m\x1b[36m\x1b[1m\x1b[4m! deleted\x1b[30m (esc)
177 \x1b[30m\x1b[36m\x1b[1m\x1b[4m! deleted\x1b[30m (esc)
176 \x1b[30m\x1b[35m\x1b[1m\x1b[4m? unknown\x1b[30m (esc)
178 \x1b[30m\x1b[35m\x1b[1m\x1b[4m? unknown\x1b[30m (esc)
177 \x1b[30m\x1b[30m\x1b[1mI ignored\x1b[30m (esc)
179 \x1b[30m\x1b[30m\x1b[1mI ignored\x1b[30m (esc)
178 \x1b[30m\x1b[30mC .hgignore\x1b[30m (esc)
180 \x1b[30m\x1b[30mC .hgignore\x1b[30m (esc)
179 \x1b[30m\x1b[30mC modified\x1b[30m (esc)
181 \x1b[30m\x1b[30mC modified\x1b[30m (esc)
180
182
181
183
182 $ echo "^ignoreddir$" > .hgignore
184 $ echo "^ignoreddir$" > .hgignore
183 $ mkdir ignoreddir
185 $ mkdir ignoreddir
184 $ touch ignoreddir/file
186 $ touch ignoreddir/file
185
187
186 hg status ignoreddir/file:
188 hg status ignoreddir/file:
187
189
188 $ hg status --color=always ignoreddir/file
190 $ hg status --color=always ignoreddir/file
189
191
190 hg status -i ignoreddir/file:
192 hg status -i ignoreddir/file:
191
193
192 $ hg status --color=always -i ignoreddir/file
194 $ hg status --color=always -i ignoreddir/file
193 \x1b[0;30;1mI ignoreddir/file\x1b[0m (esc)
195 \x1b[0;30;1mI ignoreddir/file\x1b[0m (esc)
194 $ cd ..
196 $ cd ..
195
197
196 check 'status -q' and some combinations
198 check 'status -q' and some combinations
197
199
198 $ hg init repo3
200 $ hg init repo3
199 $ cd repo3
201 $ cd repo3
200 $ touch modified removed deleted ignored
202 $ touch modified removed deleted ignored
201 $ echo "^ignored$" > .hgignore
203 $ echo "^ignored$" > .hgignore
202 $ hg commit -A -m 'initial checkin'
204 $ hg commit -A -m 'initial checkin'
203 adding .hgignore
205 adding .hgignore
204 adding deleted
206 adding deleted
205 adding modified
207 adding modified
206 adding removed
208 adding removed
207 $ touch added unknown ignored
209 $ touch added unknown ignored
208 $ hg add added
210 $ hg add added
209 $ echo "test" >> modified
211 $ echo "test" >> modified
210 $ hg remove removed
212 $ hg remove removed
211 $ rm deleted
213 $ rm deleted
212 $ hg copy modified copied
214 $ hg copy modified copied
213
215
214 test unknown color
216 test unknown color
215
217
216 $ hg --config color.status.modified=periwinkle status --color=always
218 $ hg --config color.status.modified=periwinkle status --color=always
217 ignoring unknown color/effect 'periwinkle' (configured in color.status.modified)
219 ignoring unknown color/effect 'periwinkle' (configured in color.status.modified)
218 M modified
220 M modified
219 \x1b[0;32;1mA added\x1b[0m (esc)
221 \x1b[0;32;1mA added\x1b[0m (esc)
220 \x1b[0;32;1mA copied\x1b[0m (esc)
222 \x1b[0;32;1mA copied\x1b[0m (esc)
221 \x1b[0;31;1mR removed\x1b[0m (esc)
223 \x1b[0;31;1mR removed\x1b[0m (esc)
222 \x1b[0;36;1;4m! deleted\x1b[0m (esc)
224 \x1b[0;36;1;4m! deleted\x1b[0m (esc)
223 \x1b[0;35;1;4m? unknown\x1b[0m (esc)
225 \x1b[0;35;1;4m? unknown\x1b[0m (esc)
224
226
225 Run status with 2 different flags.
227 Run status with 2 different flags.
226 Check if result is the same or different.
228 Check if result is the same or different.
227 If result is not as expected, raise error
229 If result is not as expected, raise error
228
230
229 $ assert() {
231 $ assert() {
230 > hg status --color=always $1 > ../a
232 > hg status --color=always $1 > ../a
231 > hg status --color=always $2 > ../b
233 > hg status --color=always $2 > ../b
232 > if diff ../a ../b > /dev/null; then
234 > if diff ../a ../b > /dev/null; then
233 > out=0
235 > out=0
234 > else
236 > else
235 > out=1
237 > out=1
236 > fi
238 > fi
237 > if [ $3 -eq 0 ]; then
239 > if [ $3 -eq 0 ]; then
238 > df="same"
240 > df="same"
239 > else
241 > else
240 > df="different"
242 > df="different"
241 > fi
243 > fi
242 > if [ $out -ne $3 ]; then
244 > if [ $out -ne $3 ]; then
243 > echo "Error on $1 and $2, should be $df."
245 > echo "Error on $1 and $2, should be $df."
244 > fi
246 > fi
245 > }
247 > }
246
248
247 assert flag1 flag2 [0-same | 1-different]
249 assert flag1 flag2 [0-same | 1-different]
248
250
249 $ assert "-q" "-mard" 0
251 $ assert "-q" "-mard" 0
250 $ assert "-A" "-marduicC" 0
252 $ assert "-A" "-marduicC" 0
251 $ assert "-qA" "-mardcC" 0
253 $ assert "-qA" "-mardcC" 0
252 $ assert "-qAui" "-A" 0
254 $ assert "-qAui" "-A" 0
253 $ assert "-qAu" "-marducC" 0
255 $ assert "-qAu" "-marducC" 0
254 $ assert "-qAi" "-mardicC" 0
256 $ assert "-qAi" "-mardicC" 0
255 $ assert "-qu" "-u" 0
257 $ assert "-qu" "-u" 0
256 $ assert "-q" "-u" 1
258 $ assert "-q" "-u" 1
257 $ assert "-m" "-a" 1
259 $ assert "-m" "-a" 1
258 $ assert "-r" "-d" 1
260 $ assert "-r" "-d" 1
259 $ cd ..
261 $ cd ..
260
262
261 test 'resolve -l'
263 test 'resolve -l'
262
264
263 $ hg init repo4
265 $ hg init repo4
264 $ cd repo4
266 $ cd repo4
265 $ echo "file a" > a
267 $ echo "file a" > a
266 $ echo "file b" > b
268 $ echo "file b" > b
267 $ hg add a b
269 $ hg add a b
268 $ hg commit -m "initial"
270 $ hg commit -m "initial"
269 $ echo "file a change 1" > a
271 $ echo "file a change 1" > a
270 $ echo "file b change 1" > b
272 $ echo "file b change 1" > b
271 $ hg commit -m "head 1"
273 $ hg commit -m "head 1"
272 $ hg update 0
274 $ hg update 0
273 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
275 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
274 $ echo "file a change 2" > a
276 $ echo "file a change 2" > a
275 $ echo "file b change 2" > b
277 $ echo "file b change 2" > b
276 $ hg commit -m "head 2"
278 $ hg commit -m "head 2"
277 created new head
279 created new head
278 $ hg merge
280 $ hg merge
279 merging a
281 merging a
280 warning: conflicts during merge.
282 warning: conflicts during merge.
281 merging a failed!
283 merging a failed!
282 merging b
284 merging b
283 warning: conflicts during merge.
285 warning: conflicts during merge.
284 merging b failed!
286 merging b failed!
285 0 files updated, 0 files merged, 0 files removed, 2 files unresolved
287 0 files updated, 0 files merged, 0 files removed, 2 files unresolved
286 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
288 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
287 [1]
289 [1]
288 $ hg resolve -m b
290 $ hg resolve -m b
289
291
290 hg resolve with one unresolved, one resolved:
292 hg resolve with one unresolved, one resolved:
291
293
292 $ hg resolve --color=always -l
294 $ hg resolve --color=always -l
293 \x1b[0;31;1mU a\x1b[0m (esc)
295 \x1b[0;31;1mU a\x1b[0m (esc)
294 \x1b[0;32;1mR b\x1b[0m (esc)
296 \x1b[0;32;1mR b\x1b[0m (esc)
General Comments 0
You need to be logged in to leave comments. Login now