##// END OF EJS Templates
grep: colorize all fields...
Idan Kamara -
r17806:dc7010ed default
parent child Browse files
Show More
@@ -1,503 +1,510 b''
1 1 # color.py color output for the status and qseries commands
2 2 #
3 3 # Copyright (C) 2007 Kevin Christen <kevin.christen@gmail.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 '''colorize output from some commands
9 9
10 10 This extension modifies the status and resolve commands to add color
11 11 to their output to reflect file status, the qseries command to add
12 12 color to reflect patch status (applied, unapplied, missing), and to
13 13 diff-related commands to highlight additions, removals, diff headers,
14 14 and trailing whitespace.
15 15
16 16 Other effects in addition to color, like bold and underlined text, are
17 17 also available. By default, the terminfo database is used to find the
18 18 terminal codes used to change color and effect. If terminfo is not
19 19 available, then effects are rendered with the ECMA-48 SGR control
20 20 function (aka ANSI escape codes).
21 21
22 22 Default effects may be overridden from your configuration file::
23 23
24 24 [color]
25 25 status.modified = blue bold underline red_background
26 26 status.added = green bold
27 27 status.removed = red bold blue_background
28 28 status.deleted = cyan bold underline
29 29 status.unknown = magenta bold underline
30 30 status.ignored = black bold
31 31
32 32 # 'none' turns off all effects
33 33 status.clean = none
34 34 status.copied = none
35 35
36 36 qseries.applied = blue bold underline
37 37 qseries.unapplied = black bold
38 38 qseries.missing = red bold
39 39
40 40 diff.diffline = bold
41 41 diff.extended = cyan bold
42 42 diff.file_a = red bold
43 43 diff.file_b = green bold
44 44 diff.hunk = magenta
45 45 diff.deleted = red
46 46 diff.inserted = green
47 47 diff.changed = white
48 48 diff.trailingwhitespace = bold red_background
49 49
50 50 resolve.unresolved = red bold
51 51 resolve.resolved = green bold
52 52
53 53 bookmarks.current = green
54 54
55 55 branches.active = none
56 56 branches.closed = black bold
57 57 branches.current = green
58 58 branches.inactive = none
59 59
60 60 tags.normal = green
61 61 tags.local = black bold
62 62
63 63 The available effects in terminfo mode are 'blink', 'bold', 'dim',
64 64 'inverse', 'invisible', 'italic', 'standout', and 'underline'; in
65 65 ECMA-48 mode, the options are 'bold', 'inverse', 'italic', and
66 66 'underline'. How each is rendered depends on the terminal emulator.
67 67 Some may not be available for a given terminal type, and will be
68 68 silently ignored.
69 69
70 70 Note that on some systems, terminfo mode may cause problems when using
71 71 color with the pager extension and less -R. less with the -R option
72 72 will only display ECMA-48 color codes, and terminfo mode may sometimes
73 73 emit codes that less doesn't understand. You can work around this by
74 74 either using ansi mode (or auto mode), or by using less -r (which will
75 75 pass through all terminal control codes, not just color control
76 76 codes).
77 77
78 78 Because there are only eight standard colors, this module allows you
79 79 to define color names for other color slots which might be available
80 80 for your terminal type, assuming terminfo mode. For instance::
81 81
82 82 color.brightblue = 12
83 83 color.pink = 207
84 84 color.orange = 202
85 85
86 86 to set 'brightblue' to color slot 12 (useful for 16 color terminals
87 87 that have brighter colors defined in the upper eight) and, 'pink' and
88 88 'orange' to colors in 256-color xterm's default color cube. These
89 89 defined colors may then be used as any of the pre-defined eight,
90 90 including appending '_background' to set the background to that color.
91 91
92 92 By default, the color extension will use ANSI mode (or win32 mode on
93 93 Windows) if it detects a terminal. To override auto mode (to enable
94 94 terminfo mode, for example), set the following configuration option::
95 95
96 96 [color]
97 97 mode = terminfo
98 98
99 99 Any value other than 'ansi', 'win32', 'terminfo', or 'auto' will
100 100 disable color.
101 101 '''
102 102
103 103 import os
104 104
105 105 from mercurial import commands, dispatch, extensions, ui as uimod, util
106 106 from mercurial.i18n import _
107 107
108 108 testedwith = 'internal'
109 109
110 110 # start and stop parameters for effects
111 111 _effects = {'none': 0, 'black': 30, 'red': 31, 'green': 32, 'yellow': 33,
112 112 'blue': 34, 'magenta': 35, 'cyan': 36, 'white': 37, 'bold': 1,
113 113 'italic': 3, 'underline': 4, 'inverse': 7,
114 114 'black_background': 40, 'red_background': 41,
115 115 'green_background': 42, 'yellow_background': 43,
116 116 'blue_background': 44, 'purple_background': 45,
117 117 'cyan_background': 46, 'white_background': 47}
118 118
119 119 def _terminfosetup(ui, mode):
120 120 '''Initialize terminfo data and the terminal if we're in terminfo mode.'''
121 121
122 122 global _terminfo_params
123 123 # If we failed to load curses, we go ahead and return.
124 124 if not _terminfo_params:
125 125 return
126 126 # Otherwise, see what the config file says.
127 127 if mode not in ('auto', 'terminfo'):
128 128 return
129 129
130 130 _terminfo_params.update((key[6:], (False, int(val)))
131 131 for key, val in ui.configitems('color')
132 132 if key.startswith('color.'))
133 133
134 134 try:
135 135 curses.setupterm()
136 136 except curses.error, e:
137 137 _terminfo_params = {}
138 138 return
139 139
140 140 for key, (b, e) in _terminfo_params.items():
141 141 if not b:
142 142 continue
143 143 if not curses.tigetstr(e):
144 144 # Most terminals don't support dim, invis, etc, so don't be
145 145 # noisy and use ui.debug().
146 146 ui.debug("no terminfo entry for %s\n" % e)
147 147 del _terminfo_params[key]
148 148 if not curses.tigetstr('setaf') or not curses.tigetstr('setab'):
149 149 # Only warn about missing terminfo entries if we explicitly asked for
150 150 # terminfo mode.
151 151 if mode == "terminfo":
152 152 ui.warn(_("no terminfo entry for setab/setaf: reverting to "
153 153 "ECMA-48 color\n"))
154 154 _terminfo_params = {}
155 155
156 156 def _modesetup(ui, opts):
157 157 global _terminfo_params
158 158
159 159 coloropt = opts['color']
160 160 auto = coloropt == 'auto'
161 161 always = not auto and util.parsebool(coloropt)
162 162 if not always and not auto:
163 163 return None
164 164
165 165 formatted = always or (os.environ.get('TERM') != 'dumb' and ui.formatted())
166 166
167 167 mode = ui.config('color', 'mode', 'auto')
168 168 realmode = mode
169 169 if mode == 'auto':
170 170 if os.name == 'nt' and 'TERM' not in os.environ:
171 171 # looks line a cmd.exe console, use win32 API or nothing
172 172 realmode = 'win32'
173 173 else:
174 174 realmode = 'ansi'
175 175
176 176 if realmode == 'win32':
177 177 _terminfo_params = {}
178 178 if not w32effects:
179 179 if mode == 'win32':
180 180 # only warn if color.mode is explicitly set to win32
181 181 ui.warn(_('warning: failed to set color mode to %s\n') % mode)
182 182 return None
183 183 _effects.update(w32effects)
184 184 elif realmode == 'ansi':
185 185 _terminfo_params = {}
186 186 elif realmode == 'terminfo':
187 187 _terminfosetup(ui, mode)
188 188 if not _terminfo_params:
189 189 if mode == 'terminfo':
190 190 ## FIXME Shouldn't we return None in this case too?
191 191 # only warn if color.mode is explicitly set to win32
192 192 ui.warn(_('warning: failed to set color mode to %s\n') % mode)
193 193 realmode = 'ansi'
194 194 else:
195 195 return None
196 196
197 197 if always or (auto and formatted):
198 198 return realmode
199 199 return None
200 200
201 201 try:
202 202 import curses
203 203 # Mapping from effect name to terminfo attribute name or color number.
204 204 # This will also force-load the curses module.
205 205 _terminfo_params = {'none': (True, 'sgr0'),
206 206 'standout': (True, 'smso'),
207 207 'underline': (True, 'smul'),
208 208 'reverse': (True, 'rev'),
209 209 'inverse': (True, 'rev'),
210 210 'blink': (True, 'blink'),
211 211 'dim': (True, 'dim'),
212 212 'bold': (True, 'bold'),
213 213 'invisible': (True, 'invis'),
214 214 'italic': (True, 'sitm'),
215 215 'black': (False, curses.COLOR_BLACK),
216 216 'red': (False, curses.COLOR_RED),
217 217 'green': (False, curses.COLOR_GREEN),
218 218 'yellow': (False, curses.COLOR_YELLOW),
219 219 'blue': (False, curses.COLOR_BLUE),
220 220 'magenta': (False, curses.COLOR_MAGENTA),
221 221 'cyan': (False, curses.COLOR_CYAN),
222 222 'white': (False, curses.COLOR_WHITE)}
223 223 except ImportError:
224 224 _terminfo_params = False
225 225
226 226 _styles = {'grep.match': 'red bold',
227 'grep.linenumber': 'green',
228 'grep.rev': 'green',
229 'grep.change': 'green',
230 'grep.sep': 'cyan',
231 'grep.filename': 'magenta',
232 'grep.user': 'magenta',
233 'grep.date': 'magenta',
227 234 'bookmarks.current': 'green',
228 235 'branches.active': 'none',
229 236 'branches.closed': 'black bold',
230 237 'branches.current': 'green',
231 238 'branches.inactive': 'none',
232 239 'diff.changed': 'white',
233 240 'diff.deleted': 'red',
234 241 'diff.diffline': 'bold',
235 242 'diff.extended': 'cyan bold',
236 243 'diff.file_a': 'red bold',
237 244 'diff.file_b': 'green bold',
238 245 'diff.hunk': 'magenta',
239 246 'diff.inserted': 'green',
240 247 'diff.trailingwhitespace': 'bold red_background',
241 248 'diffstat.deleted': 'red',
242 249 'diffstat.inserted': 'green',
243 250 'ui.prompt': 'yellow',
244 251 'log.changeset': 'yellow',
245 252 'resolve.resolved': 'green bold',
246 253 'resolve.unresolved': 'red bold',
247 254 'status.added': 'green bold',
248 255 'status.clean': 'none',
249 256 'status.copied': 'none',
250 257 'status.deleted': 'cyan bold underline',
251 258 'status.ignored': 'black bold',
252 259 'status.modified': 'blue bold',
253 260 'status.removed': 'red bold',
254 261 'status.unknown': 'magenta bold underline',
255 262 'tags.normal': 'green',
256 263 'tags.local': 'black bold'}
257 264
258 265
259 266 def _effect_str(effect):
260 267 '''Helper function for render_effects().'''
261 268
262 269 bg = False
263 270 if effect.endswith('_background'):
264 271 bg = True
265 272 effect = effect[:-11]
266 273 attr, val = _terminfo_params[effect]
267 274 if attr:
268 275 return curses.tigetstr(val)
269 276 elif bg:
270 277 return curses.tparm(curses.tigetstr('setab'), val)
271 278 else:
272 279 return curses.tparm(curses.tigetstr('setaf'), val)
273 280
274 281 def render_effects(text, effects):
275 282 'Wrap text in commands to turn on each effect.'
276 283 if not text:
277 284 return text
278 285 if not _terminfo_params:
279 286 start = [str(_effects[e]) for e in ['none'] + effects.split()]
280 287 start = '\033[' + ';'.join(start) + 'm'
281 288 stop = '\033[' + str(_effects['none']) + 'm'
282 289 else:
283 290 start = ''.join(_effect_str(effect)
284 291 for effect in ['none'] + effects.split())
285 292 stop = _effect_str('none')
286 293 return ''.join([start, text, stop])
287 294
288 295 def extstyles():
289 296 for name, ext in extensions.extensions():
290 297 _styles.update(getattr(ext, 'colortable', {}))
291 298
292 299 def configstyles(ui):
293 300 for status, cfgeffects in ui.configitems('color'):
294 301 if '.' not in status or status.startswith('color.'):
295 302 continue
296 303 cfgeffects = ui.configlist('color', status)
297 304 if cfgeffects:
298 305 good = []
299 306 for e in cfgeffects:
300 307 if not _terminfo_params and e in _effects:
301 308 good.append(e)
302 309 elif e in _terminfo_params or e[:-11] in _terminfo_params:
303 310 good.append(e)
304 311 else:
305 312 ui.warn(_("ignoring unknown color/effect %r "
306 313 "(configured in color.%s)\n")
307 314 % (e, status))
308 315 _styles[status] = ' '.join(good)
309 316
310 317 class colorui(uimod.ui):
311 318 def popbuffer(self, labeled=False):
312 319 if labeled:
313 320 return ''.join(self.label(a, label) for a, label
314 321 in self._buffers.pop())
315 322 return ''.join(a for a, label in self._buffers.pop())
316 323
317 324 _colormode = 'ansi'
318 325 def write(self, *args, **opts):
319 326 label = opts.get('label', '')
320 327 if self._buffers:
321 328 self._buffers[-1].extend([(str(a), label) for a in args])
322 329 elif self._colormode == 'win32':
323 330 for a in args:
324 331 win32print(a, super(colorui, self).write, **opts)
325 332 else:
326 333 return super(colorui, self).write(
327 334 *[self.label(str(a), label) for a in args], **opts)
328 335
329 336 def write_err(self, *args, **opts):
330 337 label = opts.get('label', '')
331 338 if self._colormode == 'win32':
332 339 for a in args:
333 340 win32print(a, super(colorui, self).write_err, **opts)
334 341 else:
335 342 return super(colorui, self).write_err(
336 343 *[self.label(str(a), label) for a in args], **opts)
337 344
338 345 def label(self, msg, label):
339 346 effects = []
340 347 for l in label.split():
341 348 s = _styles.get(l, '')
342 349 if s:
343 350 effects.append(s)
344 351 effects = ' '.join(effects)
345 352 if effects:
346 353 return '\n'.join([render_effects(s, effects)
347 354 for s in msg.split('\n')])
348 355 return msg
349 356
350 357
351 358 def uisetup(ui):
352 359 global _terminfo_params
353 360 if ui.plain():
354 361 return
355 362 def colorcmd(orig, ui_, opts, cmd, cmdfunc):
356 363 mode = _modesetup(ui_, opts)
357 364 if mode:
358 365 colorui._colormode = mode
359 366 if not issubclass(ui_.__class__, colorui):
360 367 colorui.__bases__ = (ui_.__class__,)
361 368 ui_.__class__ = colorui
362 369 extstyles()
363 370 configstyles(ui_)
364 371 return orig(ui_, opts, cmd, cmdfunc)
365 372 extensions.wrapfunction(dispatch, '_runcommand', colorcmd)
366 373
367 374 def extsetup(ui):
368 375 commands.globalopts.append(
369 376 ('', 'color', 'auto',
370 377 # i18n: 'always', 'auto', and 'never' are keywords and should
371 378 # not be translated
372 379 _("when to colorize (boolean, always, auto, or never)"),
373 380 _('TYPE')))
374 381
375 382 if os.name != 'nt':
376 383 w32effects = None
377 384 else:
378 385 import re, ctypes
379 386
380 387 _kernel32 = ctypes.windll.kernel32
381 388
382 389 _WORD = ctypes.c_ushort
383 390
384 391 _INVALID_HANDLE_VALUE = -1
385 392
386 393 class _COORD(ctypes.Structure):
387 394 _fields_ = [('X', ctypes.c_short),
388 395 ('Y', ctypes.c_short)]
389 396
390 397 class _SMALL_RECT(ctypes.Structure):
391 398 _fields_ = [('Left', ctypes.c_short),
392 399 ('Top', ctypes.c_short),
393 400 ('Right', ctypes.c_short),
394 401 ('Bottom', ctypes.c_short)]
395 402
396 403 class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
397 404 _fields_ = [('dwSize', _COORD),
398 405 ('dwCursorPosition', _COORD),
399 406 ('wAttributes', _WORD),
400 407 ('srWindow', _SMALL_RECT),
401 408 ('dwMaximumWindowSize', _COORD)]
402 409
403 410 _STD_OUTPUT_HANDLE = 0xfffffff5L # (DWORD)-11
404 411 _STD_ERROR_HANDLE = 0xfffffff4L # (DWORD)-12
405 412
406 413 _FOREGROUND_BLUE = 0x0001
407 414 _FOREGROUND_GREEN = 0x0002
408 415 _FOREGROUND_RED = 0x0004
409 416 _FOREGROUND_INTENSITY = 0x0008
410 417
411 418 _BACKGROUND_BLUE = 0x0010
412 419 _BACKGROUND_GREEN = 0x0020
413 420 _BACKGROUND_RED = 0x0040
414 421 _BACKGROUND_INTENSITY = 0x0080
415 422
416 423 _COMMON_LVB_REVERSE_VIDEO = 0x4000
417 424 _COMMON_LVB_UNDERSCORE = 0x8000
418 425
419 426 # http://msdn.microsoft.com/en-us/library/ms682088%28VS.85%29.aspx
420 427 w32effects = {
421 428 'none': -1,
422 429 'black': 0,
423 430 'red': _FOREGROUND_RED,
424 431 'green': _FOREGROUND_GREEN,
425 432 'yellow': _FOREGROUND_RED | _FOREGROUND_GREEN,
426 433 'blue': _FOREGROUND_BLUE,
427 434 'magenta': _FOREGROUND_BLUE | _FOREGROUND_RED,
428 435 'cyan': _FOREGROUND_BLUE | _FOREGROUND_GREEN,
429 436 'white': _FOREGROUND_RED | _FOREGROUND_GREEN | _FOREGROUND_BLUE,
430 437 'bold': _FOREGROUND_INTENSITY,
431 438 'black_background': 0x100, # unused value > 0x0f
432 439 'red_background': _BACKGROUND_RED,
433 440 'green_background': _BACKGROUND_GREEN,
434 441 'yellow_background': _BACKGROUND_RED | _BACKGROUND_GREEN,
435 442 'blue_background': _BACKGROUND_BLUE,
436 443 'purple_background': _BACKGROUND_BLUE | _BACKGROUND_RED,
437 444 'cyan_background': _BACKGROUND_BLUE | _BACKGROUND_GREEN,
438 445 'white_background': (_BACKGROUND_RED | _BACKGROUND_GREEN |
439 446 _BACKGROUND_BLUE),
440 447 'bold_background': _BACKGROUND_INTENSITY,
441 448 'underline': _COMMON_LVB_UNDERSCORE, # double-byte charsets only
442 449 'inverse': _COMMON_LVB_REVERSE_VIDEO, # double-byte charsets only
443 450 }
444 451
445 452 passthrough = set([_FOREGROUND_INTENSITY,
446 453 _BACKGROUND_INTENSITY,
447 454 _COMMON_LVB_UNDERSCORE,
448 455 _COMMON_LVB_REVERSE_VIDEO])
449 456
450 457 stdout = _kernel32.GetStdHandle(
451 458 _STD_OUTPUT_HANDLE) # don't close the handle returned
452 459 if stdout is None or stdout == _INVALID_HANDLE_VALUE:
453 460 w32effects = None
454 461 else:
455 462 csbi = _CONSOLE_SCREEN_BUFFER_INFO()
456 463 if not _kernel32.GetConsoleScreenBufferInfo(
457 464 stdout, ctypes.byref(csbi)):
458 465 # stdout may not support GetConsoleScreenBufferInfo()
459 466 # when called from subprocess or redirected
460 467 w32effects = None
461 468 else:
462 469 origattr = csbi.wAttributes
463 470 ansire = re.compile('\033\[([^m]*)m([^\033]*)(.*)',
464 471 re.MULTILINE | re.DOTALL)
465 472
466 473 def win32print(text, orig, **opts):
467 474 label = opts.get('label', '')
468 475 attr = origattr
469 476
470 477 def mapcolor(val, attr):
471 478 if val == -1:
472 479 return origattr
473 480 elif val in passthrough:
474 481 return attr | val
475 482 elif val > 0x0f:
476 483 return (val & 0x70) | (attr & 0x8f)
477 484 else:
478 485 return (val & 0x07) | (attr & 0xf8)
479 486
480 487 # determine console attributes based on labels
481 488 for l in label.split():
482 489 style = _styles.get(l, '')
483 490 for effect in style.split():
484 491 attr = mapcolor(w32effects[effect], attr)
485 492
486 493 # hack to ensure regexp finds data
487 494 if not text.startswith('\033['):
488 495 text = '\033[m' + text
489 496
490 497 # Look for ANSI-like codes embedded in text
491 498 m = re.match(ansire, text)
492 499
493 500 try:
494 501 while m:
495 502 for sattr in m.group(1).split(';'):
496 503 if sattr:
497 504 attr = mapcolor(int(sattr), attr)
498 505 _kernel32.SetConsoleTextAttribute(stdout, attr)
499 506 orig(m.group(2), **opts)
500 507 m = re.match(ansire, m.group(3))
501 508 finally:
502 509 # Explicitly reset original attributes
503 510 _kernel32.SetConsoleTextAttribute(stdout, origattr)
@@ -1,5922 +1,5927 b''
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from node import hex, bin, nullid, nullrev, short
9 9 from lock import release
10 10 from i18n import _, gettext
11 11 import os, re, difflib, time, tempfile, errno
12 12 import hg, scmutil, util, revlog, extensions, copies, error, bookmarks
13 13 import patch, help, url, encoding, templatekw, discovery
14 14 import archival, changegroup, cmdutil, hbisect
15 15 import sshserver, hgweb, hgweb.server, commandserver
16 16 import merge as mergemod
17 17 import minirst, revset, fileset
18 18 import dagparser, context, simplemerge, graphmod
19 19 import random, setdiscovery, treediscovery, dagutil, pvec, localrepo
20 20 import phases, obsolete
21 21
22 22 table = {}
23 23
24 24 command = cmdutil.command(table)
25 25
26 26 # common command options
27 27
28 28 globalopts = [
29 29 ('R', 'repository', '',
30 30 _('repository root directory or name of overlay bundle file'),
31 31 _('REPO')),
32 32 ('', 'cwd', '',
33 33 _('change working directory'), _('DIR')),
34 34 ('y', 'noninteractive', None,
35 35 _('do not prompt, automatically pick the first choice for all prompts')),
36 36 ('q', 'quiet', None, _('suppress output')),
37 37 ('v', 'verbose', None, _('enable additional output')),
38 38 ('', 'config', [],
39 39 _('set/override config option (use \'section.name=value\')'),
40 40 _('CONFIG')),
41 41 ('', 'debug', None, _('enable debugging output')),
42 42 ('', 'debugger', None, _('start debugger')),
43 43 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
44 44 _('ENCODE')),
45 45 ('', 'encodingmode', encoding.encodingmode,
46 46 _('set the charset encoding mode'), _('MODE')),
47 47 ('', 'traceback', None, _('always print a traceback on exception')),
48 48 ('', 'time', None, _('time how long the command takes')),
49 49 ('', 'profile', None, _('print command execution profile')),
50 50 ('', 'version', None, _('output version information and exit')),
51 51 ('h', 'help', None, _('display help and exit')),
52 52 ]
53 53
54 54 dryrunopts = [('n', 'dry-run', None,
55 55 _('do not perform actions, just print output'))]
56 56
57 57 remoteopts = [
58 58 ('e', 'ssh', '',
59 59 _('specify ssh command to use'), _('CMD')),
60 60 ('', 'remotecmd', '',
61 61 _('specify hg command to run on the remote side'), _('CMD')),
62 62 ('', 'insecure', None,
63 63 _('do not verify server certificate (ignoring web.cacerts config)')),
64 64 ]
65 65
66 66 walkopts = [
67 67 ('I', 'include', [],
68 68 _('include names matching the given patterns'), _('PATTERN')),
69 69 ('X', 'exclude', [],
70 70 _('exclude names matching the given patterns'), _('PATTERN')),
71 71 ]
72 72
73 73 commitopts = [
74 74 ('m', 'message', '',
75 75 _('use text as commit message'), _('TEXT')),
76 76 ('l', 'logfile', '',
77 77 _('read commit message from file'), _('FILE')),
78 78 ]
79 79
80 80 commitopts2 = [
81 81 ('d', 'date', '',
82 82 _('record the specified date as commit date'), _('DATE')),
83 83 ('u', 'user', '',
84 84 _('record the specified user as committer'), _('USER')),
85 85 ]
86 86
87 87 templateopts = [
88 88 ('', 'style', '',
89 89 _('display using template map file'), _('STYLE')),
90 90 ('', 'template', '',
91 91 _('display with template'), _('TEMPLATE')),
92 92 ]
93 93
94 94 logopts = [
95 95 ('p', 'patch', None, _('show patch')),
96 96 ('g', 'git', None, _('use git extended diff format')),
97 97 ('l', 'limit', '',
98 98 _('limit number of changes displayed'), _('NUM')),
99 99 ('M', 'no-merges', None, _('do not show merges')),
100 100 ('', 'stat', None, _('output diffstat-style summary of changes')),
101 101 ('G', 'graph', None, _("show the revision DAG")),
102 102 ] + templateopts
103 103
104 104 diffopts = [
105 105 ('a', 'text', None, _('treat all files as text')),
106 106 ('g', 'git', None, _('use git extended diff format')),
107 107 ('', 'nodates', None, _('omit dates from diff headers'))
108 108 ]
109 109
110 110 diffwsopts = [
111 111 ('w', 'ignore-all-space', None,
112 112 _('ignore white space when comparing lines')),
113 113 ('b', 'ignore-space-change', None,
114 114 _('ignore changes in the amount of white space')),
115 115 ('B', 'ignore-blank-lines', None,
116 116 _('ignore changes whose lines are all blank')),
117 117 ]
118 118
119 119 diffopts2 = [
120 120 ('p', 'show-function', None, _('show which function each change is in')),
121 121 ('', 'reverse', None, _('produce a diff that undoes the changes')),
122 122 ] + diffwsopts + [
123 123 ('U', 'unified', '',
124 124 _('number of lines of context to show'), _('NUM')),
125 125 ('', 'stat', None, _('output diffstat-style summary of changes')),
126 126 ]
127 127
128 128 mergetoolopts = [
129 129 ('t', 'tool', '', _('specify merge tool')),
130 130 ]
131 131
132 132 similarityopts = [
133 133 ('s', 'similarity', '',
134 134 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
135 135 ]
136 136
137 137 subrepoopts = [
138 138 ('S', 'subrepos', None,
139 139 _('recurse into subrepositories'))
140 140 ]
141 141
142 142 # Commands start here, listed alphabetically
143 143
144 144 @command('^add',
145 145 walkopts + subrepoopts + dryrunopts,
146 146 _('[OPTION]... [FILE]...'))
147 147 def add(ui, repo, *pats, **opts):
148 148 """add the specified files on the next commit
149 149
150 150 Schedule files to be version controlled and added to the
151 151 repository.
152 152
153 153 The files will be added to the repository at the next commit. To
154 154 undo an add before that, see :hg:`forget`.
155 155
156 156 If no names are given, add all files to the repository.
157 157
158 158 .. container:: verbose
159 159
160 160 An example showing how new (unknown) files are added
161 161 automatically by :hg:`add`::
162 162
163 163 $ ls
164 164 foo.c
165 165 $ hg status
166 166 ? foo.c
167 167 $ hg add
168 168 adding foo.c
169 169 $ hg status
170 170 A foo.c
171 171
172 172 Returns 0 if all files are successfully added.
173 173 """
174 174
175 175 m = scmutil.match(repo[None], pats, opts)
176 176 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
177 177 opts.get('subrepos'), prefix="", explicitonly=False)
178 178 return rejected and 1 or 0
179 179
180 180 @command('addremove',
181 181 similarityopts + walkopts + dryrunopts,
182 182 _('[OPTION]... [FILE]...'))
183 183 def addremove(ui, repo, *pats, **opts):
184 184 """add all new files, delete all missing files
185 185
186 186 Add all new files and remove all missing files from the
187 187 repository.
188 188
189 189 New files are ignored if they match any of the patterns in
190 190 ``.hgignore``. As with add, these changes take effect at the next
191 191 commit.
192 192
193 193 Use the -s/--similarity option to detect renamed files. This
194 194 option takes a percentage between 0 (disabled) and 100 (files must
195 195 be identical) as its parameter. With a parameter greater than 0,
196 196 this compares every removed file with every added file and records
197 197 those similar enough as renames. Detecting renamed files this way
198 198 can be expensive. After using this option, :hg:`status -C` can be
199 199 used to check which files were identified as moved or renamed. If
200 200 not specified, -s/--similarity defaults to 100 and only renames of
201 201 identical files are detected.
202 202
203 203 Returns 0 if all files are successfully added.
204 204 """
205 205 try:
206 206 sim = float(opts.get('similarity') or 100)
207 207 except ValueError:
208 208 raise util.Abort(_('similarity must be a number'))
209 209 if sim < 0 or sim > 100:
210 210 raise util.Abort(_('similarity must be between 0 and 100'))
211 211 return scmutil.addremove(repo, pats, opts, similarity=sim / 100.0)
212 212
213 213 @command('^annotate|blame',
214 214 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
215 215 ('', 'follow', None,
216 216 _('follow copies/renames and list the filename (DEPRECATED)')),
217 217 ('', 'no-follow', None, _("don't follow copies and renames")),
218 218 ('a', 'text', None, _('treat all files as text')),
219 219 ('u', 'user', None, _('list the author (long with -v)')),
220 220 ('f', 'file', None, _('list the filename')),
221 221 ('d', 'date', None, _('list the date (short with -q)')),
222 222 ('n', 'number', None, _('list the revision number (default)')),
223 223 ('c', 'changeset', None, _('list the changeset')),
224 224 ('l', 'line-number', None, _('show line number at the first appearance'))
225 225 ] + diffwsopts + walkopts,
226 226 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'))
227 227 def annotate(ui, repo, *pats, **opts):
228 228 """show changeset information by line for each file
229 229
230 230 List changes in files, showing the revision id responsible for
231 231 each line
232 232
233 233 This command is useful for discovering when a change was made and
234 234 by whom.
235 235
236 236 Without the -a/--text option, annotate will avoid processing files
237 237 it detects as binary. With -a, annotate will annotate the file
238 238 anyway, although the results will probably be neither useful
239 239 nor desirable.
240 240
241 241 Returns 0 on success.
242 242 """
243 243 if opts.get('follow'):
244 244 # --follow is deprecated and now just an alias for -f/--file
245 245 # to mimic the behavior of Mercurial before version 1.5
246 246 opts['file'] = True
247 247
248 248 datefunc = ui.quiet and util.shortdate or util.datestr
249 249 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
250 250
251 251 if not pats:
252 252 raise util.Abort(_('at least one filename or pattern is required'))
253 253
254 254 hexfn = ui.debugflag and hex or short
255 255
256 256 opmap = [('user', ' ', lambda x: ui.shortuser(x[0].user())),
257 257 ('number', ' ', lambda x: str(x[0].rev())),
258 258 ('changeset', ' ', lambda x: hexfn(x[0].node())),
259 259 ('date', ' ', getdate),
260 260 ('file', ' ', lambda x: x[0].path()),
261 261 ('line_number', ':', lambda x: str(x[1])),
262 262 ]
263 263
264 264 if (not opts.get('user') and not opts.get('changeset')
265 265 and not opts.get('date') and not opts.get('file')):
266 266 opts['number'] = True
267 267
268 268 linenumber = opts.get('line_number') is not None
269 269 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
270 270 raise util.Abort(_('at least one of -n/-c is required for -l'))
271 271
272 272 funcmap = [(func, sep) for op, sep, func in opmap if opts.get(op)]
273 273 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
274 274
275 275 def bad(x, y):
276 276 raise util.Abort("%s: %s" % (x, y))
277 277
278 278 ctx = scmutil.revsingle(repo, opts.get('rev'))
279 279 m = scmutil.match(ctx, pats, opts)
280 280 m.bad = bad
281 281 follow = not opts.get('no_follow')
282 282 diffopts = patch.diffopts(ui, opts, section='annotate')
283 283 for abs in ctx.walk(m):
284 284 fctx = ctx[abs]
285 285 if not opts.get('text') and util.binary(fctx.data()):
286 286 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
287 287 continue
288 288
289 289 lines = fctx.annotate(follow=follow, linenumber=linenumber,
290 290 diffopts=diffopts)
291 291 pieces = []
292 292
293 293 for f, sep in funcmap:
294 294 l = [f(n) for n, dummy in lines]
295 295 if l:
296 296 sized = [(x, encoding.colwidth(x)) for x in l]
297 297 ml = max([w for x, w in sized])
298 298 pieces.append(["%s%s%s" % (sep, ' ' * (ml - w), x)
299 299 for x, w in sized])
300 300
301 301 if pieces:
302 302 for p, l in zip(zip(*pieces), lines):
303 303 ui.write("%s: %s" % ("".join(p), l[1]))
304 304
305 305 if lines and not lines[-1][1].endswith('\n'):
306 306 ui.write('\n')
307 307
308 308 @command('archive',
309 309 [('', 'no-decode', None, _('do not pass files through decoders')),
310 310 ('p', 'prefix', '', _('directory prefix for files in archive'),
311 311 _('PREFIX')),
312 312 ('r', 'rev', '', _('revision to distribute'), _('REV')),
313 313 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
314 314 ] + subrepoopts + walkopts,
315 315 _('[OPTION]... DEST'))
316 316 def archive(ui, repo, dest, **opts):
317 317 '''create an unversioned archive of a repository revision
318 318
319 319 By default, the revision used is the parent of the working
320 320 directory; use -r/--rev to specify a different revision.
321 321
322 322 The archive type is automatically detected based on file
323 323 extension (or override using -t/--type).
324 324
325 325 .. container:: verbose
326 326
327 327 Examples:
328 328
329 329 - create a zip file containing the 1.0 release::
330 330
331 331 hg archive -r 1.0 project-1.0.zip
332 332
333 333 - create a tarball excluding .hg files::
334 334
335 335 hg archive project.tar.gz -X ".hg*"
336 336
337 337 Valid types are:
338 338
339 339 :``files``: a directory full of files (default)
340 340 :``tar``: tar archive, uncompressed
341 341 :``tbz2``: tar archive, compressed using bzip2
342 342 :``tgz``: tar archive, compressed using gzip
343 343 :``uzip``: zip archive, uncompressed
344 344 :``zip``: zip archive, compressed using deflate
345 345
346 346 The exact name of the destination archive or directory is given
347 347 using a format string; see :hg:`help export` for details.
348 348
349 349 Each member added to an archive file has a directory prefix
350 350 prepended. Use -p/--prefix to specify a format string for the
351 351 prefix. The default is the basename of the archive, with suffixes
352 352 removed.
353 353
354 354 Returns 0 on success.
355 355 '''
356 356
357 357 ctx = scmutil.revsingle(repo, opts.get('rev'))
358 358 if not ctx:
359 359 raise util.Abort(_('no working directory: please specify a revision'))
360 360 node = ctx.node()
361 361 dest = cmdutil.makefilename(repo, dest, node)
362 362 if os.path.realpath(dest) == repo.root:
363 363 raise util.Abort(_('repository root cannot be destination'))
364 364
365 365 kind = opts.get('type') or archival.guesskind(dest) or 'files'
366 366 prefix = opts.get('prefix')
367 367
368 368 if dest == '-':
369 369 if kind == 'files':
370 370 raise util.Abort(_('cannot archive plain files to stdout'))
371 371 dest = cmdutil.makefileobj(repo, dest)
372 372 if not prefix:
373 373 prefix = os.path.basename(repo.root) + '-%h'
374 374
375 375 prefix = cmdutil.makefilename(repo, prefix, node)
376 376 matchfn = scmutil.match(ctx, [], opts)
377 377 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
378 378 matchfn, prefix, subrepos=opts.get('subrepos'))
379 379
380 380 @command('backout',
381 381 [('', 'merge', None, _('merge with old dirstate parent after backout')),
382 382 ('', 'parent', '',
383 383 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
384 384 ('r', 'rev', '', _('revision to backout'), _('REV')),
385 385 ] + mergetoolopts + walkopts + commitopts + commitopts2,
386 386 _('[OPTION]... [-r] REV'))
387 387 def backout(ui, repo, node=None, rev=None, **opts):
388 388 '''reverse effect of earlier changeset
389 389
390 390 Prepare a new changeset with the effect of REV undone in the
391 391 current working directory.
392 392
393 393 If REV is the parent of the working directory, then this new changeset
394 394 is committed automatically. Otherwise, hg needs to merge the
395 395 changes and the merged result is left uncommitted.
396 396
397 397 .. note::
398 398 backout cannot be used to fix either an unwanted or
399 399 incorrect merge.
400 400
401 401 .. container:: verbose
402 402
403 403 By default, the pending changeset will have one parent,
404 404 maintaining a linear history. With --merge, the pending
405 405 changeset will instead have two parents: the old parent of the
406 406 working directory and a new child of REV that simply undoes REV.
407 407
408 408 Before version 1.7, the behavior without --merge was equivalent
409 409 to specifying --merge followed by :hg:`update --clean .` to
410 410 cancel the merge and leave the child of REV as a head to be
411 411 merged separately.
412 412
413 413 See :hg:`help dates` for a list of formats valid for -d/--date.
414 414
415 415 Returns 0 on success.
416 416 '''
417 417 if rev and node:
418 418 raise util.Abort(_("please specify just one revision"))
419 419
420 420 if not rev:
421 421 rev = node
422 422
423 423 if not rev:
424 424 raise util.Abort(_("please specify a revision to backout"))
425 425
426 426 date = opts.get('date')
427 427 if date:
428 428 opts['date'] = util.parsedate(date)
429 429
430 430 cmdutil.bailifchanged(repo)
431 431 node = scmutil.revsingle(repo, rev).node()
432 432
433 433 op1, op2 = repo.dirstate.parents()
434 434 a = repo.changelog.ancestor(op1, node)
435 435 if a != node:
436 436 raise util.Abort(_('cannot backout change on a different branch'))
437 437
438 438 p1, p2 = repo.changelog.parents(node)
439 439 if p1 == nullid:
440 440 raise util.Abort(_('cannot backout a change with no parents'))
441 441 if p2 != nullid:
442 442 if not opts.get('parent'):
443 443 raise util.Abort(_('cannot backout a merge changeset'))
444 444 p = repo.lookup(opts['parent'])
445 445 if p not in (p1, p2):
446 446 raise util.Abort(_('%s is not a parent of %s') %
447 447 (short(p), short(node)))
448 448 parent = p
449 449 else:
450 450 if opts.get('parent'):
451 451 raise util.Abort(_('cannot use --parent on non-merge changeset'))
452 452 parent = p1
453 453
454 454 # the backout should appear on the same branch
455 455 wlock = repo.wlock()
456 456 try:
457 457 branch = repo.dirstate.branch()
458 458 hg.clean(repo, node, show_stats=False)
459 459 repo.dirstate.setbranch(branch)
460 460 revert_opts = opts.copy()
461 461 revert_opts['date'] = None
462 462 revert_opts['all'] = True
463 463 revert_opts['rev'] = hex(parent)
464 464 revert_opts['no_backup'] = None
465 465 revert(ui, repo, **revert_opts)
466 466 if not opts.get('merge') and op1 != node:
467 467 try:
468 468 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
469 469 return hg.update(repo, op1)
470 470 finally:
471 471 ui.setconfig('ui', 'forcemerge', '')
472 472
473 473 commit_opts = opts.copy()
474 474 commit_opts['addremove'] = False
475 475 if not commit_opts['message'] and not commit_opts['logfile']:
476 476 # we don't translate commit messages
477 477 commit_opts['message'] = "Backed out changeset %s" % short(node)
478 478 commit_opts['force_editor'] = True
479 479 commit(ui, repo, **commit_opts)
480 480 def nice(node):
481 481 return '%d:%s' % (repo.changelog.rev(node), short(node))
482 482 ui.status(_('changeset %s backs out changeset %s\n') %
483 483 (nice(repo.changelog.tip()), nice(node)))
484 484 if opts.get('merge') and op1 != node:
485 485 hg.clean(repo, op1, show_stats=False)
486 486 ui.status(_('merging with changeset %s\n')
487 487 % nice(repo.changelog.tip()))
488 488 try:
489 489 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
490 490 return hg.merge(repo, hex(repo.changelog.tip()))
491 491 finally:
492 492 ui.setconfig('ui', 'forcemerge', '')
493 493 finally:
494 494 wlock.release()
495 495 return 0
496 496
497 497 @command('bisect',
498 498 [('r', 'reset', False, _('reset bisect state')),
499 499 ('g', 'good', False, _('mark changeset good')),
500 500 ('b', 'bad', False, _('mark changeset bad')),
501 501 ('s', 'skip', False, _('skip testing changeset')),
502 502 ('e', 'extend', False, _('extend the bisect range')),
503 503 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
504 504 ('U', 'noupdate', False, _('do not update to target'))],
505 505 _("[-gbsr] [-U] [-c CMD] [REV]"))
506 506 def bisect(ui, repo, rev=None, extra=None, command=None,
507 507 reset=None, good=None, bad=None, skip=None, extend=None,
508 508 noupdate=None):
509 509 """subdivision search of changesets
510 510
511 511 This command helps to find changesets which introduce problems. To
512 512 use, mark the earliest changeset you know exhibits the problem as
513 513 bad, then mark the latest changeset which is free from the problem
514 514 as good. Bisect will update your working directory to a revision
515 515 for testing (unless the -U/--noupdate option is specified). Once
516 516 you have performed tests, mark the working directory as good or
517 517 bad, and bisect will either update to another candidate changeset
518 518 or announce that it has found the bad revision.
519 519
520 520 As a shortcut, you can also use the revision argument to mark a
521 521 revision as good or bad without checking it out first.
522 522
523 523 If you supply a command, it will be used for automatic bisection.
524 524 The environment variable HG_NODE will contain the ID of the
525 525 changeset being tested. The exit status of the command will be
526 526 used to mark revisions as good or bad: status 0 means good, 125
527 527 means to skip the revision, 127 (command not found) will abort the
528 528 bisection, and any other non-zero exit status means the revision
529 529 is bad.
530 530
531 531 .. container:: verbose
532 532
533 533 Some examples:
534 534
535 535 - start a bisection with known bad revision 12, and good revision 34::
536 536
537 537 hg bisect --bad 34
538 538 hg bisect --good 12
539 539
540 540 - advance the current bisection by marking current revision as good or
541 541 bad::
542 542
543 543 hg bisect --good
544 544 hg bisect --bad
545 545
546 546 - mark the current revision, or a known revision, to be skipped (e.g. if
547 547 that revision is not usable because of another issue)::
548 548
549 549 hg bisect --skip
550 550 hg bisect --skip 23
551 551
552 552 - forget the current bisection::
553 553
554 554 hg bisect --reset
555 555
556 556 - use 'make && make tests' to automatically find the first broken
557 557 revision::
558 558
559 559 hg bisect --reset
560 560 hg bisect --bad 34
561 561 hg bisect --good 12
562 562 hg bisect --command 'make && make tests'
563 563
564 564 - see all changesets whose states are already known in the current
565 565 bisection::
566 566
567 567 hg log -r "bisect(pruned)"
568 568
569 569 - see the changeset currently being bisected (especially useful
570 570 if running with -U/--noupdate)::
571 571
572 572 hg log -r "bisect(current)"
573 573
574 574 - see all changesets that took part in the current bisection::
575 575
576 576 hg log -r "bisect(range)"
577 577
578 578 - with the graphlog extension, you can even get a nice graph::
579 579
580 580 hg log --graph -r "bisect(range)"
581 581
582 582 See :hg:`help revsets` for more about the `bisect()` keyword.
583 583
584 584 Returns 0 on success.
585 585 """
586 586 def extendbisectrange(nodes, good):
587 587 # bisect is incomplete when it ends on a merge node and
588 588 # one of the parent was not checked.
589 589 parents = repo[nodes[0]].parents()
590 590 if len(parents) > 1:
591 591 side = good and state['bad'] or state['good']
592 592 num = len(set(i.node() for i in parents) & set(side))
593 593 if num == 1:
594 594 return parents[0].ancestor(parents[1])
595 595 return None
596 596
597 597 def print_result(nodes, good):
598 598 displayer = cmdutil.show_changeset(ui, repo, {})
599 599 if len(nodes) == 1:
600 600 # narrowed it down to a single revision
601 601 if good:
602 602 ui.write(_("The first good revision is:\n"))
603 603 else:
604 604 ui.write(_("The first bad revision is:\n"))
605 605 displayer.show(repo[nodes[0]])
606 606 extendnode = extendbisectrange(nodes, good)
607 607 if extendnode is not None:
608 608 ui.write(_('Not all ancestors of this changeset have been'
609 609 ' checked.\nUse bisect --extend to continue the '
610 610 'bisection from\nthe common ancestor, %s.\n')
611 611 % extendnode)
612 612 else:
613 613 # multiple possible revisions
614 614 if good:
615 615 ui.write(_("Due to skipped revisions, the first "
616 616 "good revision could be any of:\n"))
617 617 else:
618 618 ui.write(_("Due to skipped revisions, the first "
619 619 "bad revision could be any of:\n"))
620 620 for n in nodes:
621 621 displayer.show(repo[n])
622 622 displayer.close()
623 623
624 624 def check_state(state, interactive=True):
625 625 if not state['good'] or not state['bad']:
626 626 if (good or bad or skip or reset) and interactive:
627 627 return
628 628 if not state['good']:
629 629 raise util.Abort(_('cannot bisect (no known good revisions)'))
630 630 else:
631 631 raise util.Abort(_('cannot bisect (no known bad revisions)'))
632 632 return True
633 633
634 634 # backward compatibility
635 635 if rev in "good bad reset init".split():
636 636 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
637 637 cmd, rev, extra = rev, extra, None
638 638 if cmd == "good":
639 639 good = True
640 640 elif cmd == "bad":
641 641 bad = True
642 642 else:
643 643 reset = True
644 644 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
645 645 raise util.Abort(_('incompatible arguments'))
646 646
647 647 if reset:
648 648 p = repo.join("bisect.state")
649 649 if os.path.exists(p):
650 650 os.unlink(p)
651 651 return
652 652
653 653 state = hbisect.load_state(repo)
654 654
655 655 if command:
656 656 changesets = 1
657 657 try:
658 658 node = state['current'][0]
659 659 except LookupError:
660 660 if noupdate:
661 661 raise util.Abort(_('current bisect revision is unknown - '
662 662 'start a new bisect to fix'))
663 663 node, p2 = repo.dirstate.parents()
664 664 if p2 != nullid:
665 665 raise util.Abort(_('current bisect revision is a merge'))
666 666 try:
667 667 while changesets:
668 668 # update state
669 669 state['current'] = [node]
670 670 hbisect.save_state(repo, state)
671 671 status = util.system(command,
672 672 environ={'HG_NODE': hex(node)},
673 673 out=ui.fout)
674 674 if status == 125:
675 675 transition = "skip"
676 676 elif status == 0:
677 677 transition = "good"
678 678 # status < 0 means process was killed
679 679 elif status == 127:
680 680 raise util.Abort(_("failed to execute %s") % command)
681 681 elif status < 0:
682 682 raise util.Abort(_("%s killed") % command)
683 683 else:
684 684 transition = "bad"
685 685 ctx = scmutil.revsingle(repo, rev, node)
686 686 rev = None # clear for future iterations
687 687 state[transition].append(ctx.node())
688 688 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
689 689 check_state(state, interactive=False)
690 690 # bisect
691 691 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
692 692 # update to next check
693 693 node = nodes[0]
694 694 if not noupdate:
695 695 cmdutil.bailifchanged(repo)
696 696 hg.clean(repo, node, show_stats=False)
697 697 finally:
698 698 state['current'] = [node]
699 699 hbisect.save_state(repo, state)
700 700 print_result(nodes, good)
701 701 return
702 702
703 703 # update state
704 704
705 705 if rev:
706 706 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
707 707 else:
708 708 nodes = [repo.lookup('.')]
709 709
710 710 if good or bad or skip:
711 711 if good:
712 712 state['good'] += nodes
713 713 elif bad:
714 714 state['bad'] += nodes
715 715 elif skip:
716 716 state['skip'] += nodes
717 717 hbisect.save_state(repo, state)
718 718
719 719 if not check_state(state):
720 720 return
721 721
722 722 # actually bisect
723 723 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
724 724 if extend:
725 725 if not changesets:
726 726 extendnode = extendbisectrange(nodes, good)
727 727 if extendnode is not None:
728 728 ui.write(_("Extending search to changeset %d:%s\n"
729 729 % (extendnode.rev(), extendnode)))
730 730 state['current'] = [extendnode.node()]
731 731 hbisect.save_state(repo, state)
732 732 if noupdate:
733 733 return
734 734 cmdutil.bailifchanged(repo)
735 735 return hg.clean(repo, extendnode.node())
736 736 raise util.Abort(_("nothing to extend"))
737 737
738 738 if changesets == 0:
739 739 print_result(nodes, good)
740 740 else:
741 741 assert len(nodes) == 1 # only a single node can be tested next
742 742 node = nodes[0]
743 743 # compute the approximate number of remaining tests
744 744 tests, size = 0, 2
745 745 while size <= changesets:
746 746 tests, size = tests + 1, size * 2
747 747 rev = repo.changelog.rev(node)
748 748 ui.write(_("Testing changeset %d:%s "
749 749 "(%d changesets remaining, ~%d tests)\n")
750 750 % (rev, short(node), changesets, tests))
751 751 state['current'] = [node]
752 752 hbisect.save_state(repo, state)
753 753 if not noupdate:
754 754 cmdutil.bailifchanged(repo)
755 755 return hg.clean(repo, node)
756 756
757 757 @command('bookmarks',
758 758 [('f', 'force', False, _('force')),
759 759 ('r', 'rev', '', _('revision'), _('REV')),
760 760 ('d', 'delete', False, _('delete a given bookmark')),
761 761 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
762 762 ('i', 'inactive', False, _('mark a bookmark inactive'))],
763 763 _('hg bookmarks [-f] [-d] [-i] [-m NAME] [-r REV] [NAME]'))
764 764 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False,
765 765 rename=None, inactive=False):
766 766 '''track a line of development with movable markers
767 767
768 768 Bookmarks are pointers to certain commits that move when committing.
769 769 Bookmarks are local. They can be renamed, copied and deleted. It is
770 770 possible to use :hg:`merge NAME` to merge from a given bookmark, and
771 771 :hg:`update NAME` to update to a given bookmark.
772 772
773 773 You can use :hg:`bookmark NAME` to set a bookmark on the working
774 774 directory's parent revision with the given name. If you specify
775 775 a revision using -r REV (where REV may be an existing bookmark),
776 776 the bookmark is assigned to that revision.
777 777
778 778 Bookmarks can be pushed and pulled between repositories (see :hg:`help
779 779 push` and :hg:`help pull`). This requires both the local and remote
780 780 repositories to support bookmarks. For versions prior to 1.8, this means
781 781 the bookmarks extension must be enabled.
782 782
783 783 With -i/--inactive, the new bookmark will not be made the active
784 784 bookmark. If -r/--rev is given, the new bookmark will not be made
785 785 active even if -i/--inactive is not given. If no NAME is given, the
786 786 current active bookmark will be marked inactive.
787 787 '''
788 788 hexfn = ui.debugflag and hex or short
789 789 marks = repo._bookmarks
790 790 cur = repo.changectx('.').node()
791 791
792 792 def checkformat(mark):
793 793 if "\n" in mark:
794 794 raise util.Abort(_("bookmark name cannot contain newlines"))
795 795 mark = mark.strip()
796 796 if not mark:
797 797 raise util.Abort(_("bookmark names cannot consist entirely of "
798 798 "whitespace"))
799 799 return mark
800 800
801 801 def checkconflict(repo, mark, force=False):
802 802 if mark in marks and not force:
803 803 raise util.Abort(_("bookmark '%s' already exists "
804 804 "(use -f to force)") % mark)
805 805 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
806 806 and not force):
807 807 raise util.Abort(
808 808 _("a bookmark cannot have the name of an existing branch"))
809 809
810 810 if delete and rename:
811 811 raise util.Abort(_("--delete and --rename are incompatible"))
812 812 if delete and rev:
813 813 raise util.Abort(_("--rev is incompatible with --delete"))
814 814 if rename and rev:
815 815 raise util.Abort(_("--rev is incompatible with --rename"))
816 816 if mark is None and (delete or rev):
817 817 raise util.Abort(_("bookmark name required"))
818 818
819 819 if delete:
820 820 if mark not in marks:
821 821 raise util.Abort(_("bookmark '%s' does not exist") % mark)
822 822 if mark == repo._bookmarkcurrent:
823 823 bookmarks.setcurrent(repo, None)
824 824 del marks[mark]
825 825 bookmarks.write(repo)
826 826
827 827 elif rename:
828 828 if mark is None:
829 829 raise util.Abort(_("new bookmark name required"))
830 830 mark = checkformat(mark)
831 831 if rename not in marks:
832 832 raise util.Abort(_("bookmark '%s' does not exist") % rename)
833 833 checkconflict(repo, mark, force)
834 834 marks[mark] = marks[rename]
835 835 if repo._bookmarkcurrent == rename and not inactive:
836 836 bookmarks.setcurrent(repo, mark)
837 837 del marks[rename]
838 838 bookmarks.write(repo)
839 839
840 840 elif mark is not None:
841 841 mark = checkformat(mark)
842 842 if inactive and mark == repo._bookmarkcurrent:
843 843 bookmarks.setcurrent(repo, None)
844 844 return
845 845 checkconflict(repo, mark, force)
846 846 if rev:
847 847 marks[mark] = scmutil.revsingle(repo, rev).node()
848 848 else:
849 849 marks[mark] = cur
850 850 if not inactive and cur == marks[mark]:
851 851 bookmarks.setcurrent(repo, mark)
852 852 bookmarks.write(repo)
853 853 return
854 854
855 855 else: # mark is None
856 856 if len(marks) == 0:
857 857 ui.status(_("no bookmarks set\n"))
858 858 elif inactive:
859 859 if not repo._bookmarkcurrent:
860 860 ui.status(_("no active bookmark\n"))
861 861 else:
862 862 bookmarks.setcurrent(repo, None)
863 863 else:
864 864 for bmark, n in sorted(marks.iteritems()):
865 865 current = repo._bookmarkcurrent
866 866 if bmark == current and n == cur:
867 867 prefix, label = '*', 'bookmarks.current'
868 868 else:
869 869 prefix, label = ' ', ''
870 870
871 871 if ui.quiet:
872 872 ui.write("%s\n" % bmark, label=label)
873 873 else:
874 874 ui.write(" %s %-25s %d:%s\n" % (
875 875 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
876 876 label=label)
877 877
878 878 @command('branch',
879 879 [('f', 'force', None,
880 880 _('set branch name even if it shadows an existing branch')),
881 881 ('C', 'clean', None, _('reset branch name to parent branch name'))],
882 882 _('[-fC] [NAME]'))
883 883 def branch(ui, repo, label=None, **opts):
884 884 """set or show the current branch name
885 885
886 886 .. note::
887 887 Branch names are permanent and global. Use :hg:`bookmark` to create a
888 888 light-weight bookmark instead. See :hg:`help glossary` for more
889 889 information about named branches and bookmarks.
890 890
891 891 With no argument, show the current branch name. With one argument,
892 892 set the working directory branch name (the branch will not exist
893 893 in the repository until the next commit). Standard practice
894 894 recommends that primary development take place on the 'default'
895 895 branch.
896 896
897 897 Unless -f/--force is specified, branch will not let you set a
898 898 branch name that already exists, even if it's inactive.
899 899
900 900 Use -C/--clean to reset the working directory branch to that of
901 901 the parent of the working directory, negating a previous branch
902 902 change.
903 903
904 904 Use the command :hg:`update` to switch to an existing branch. Use
905 905 :hg:`commit --close-branch` to mark this branch as closed.
906 906
907 907 Returns 0 on success.
908 908 """
909 909 if not opts.get('clean') and not label:
910 910 ui.write("%s\n" % repo.dirstate.branch())
911 911 return
912 912
913 913 wlock = repo.wlock()
914 914 try:
915 915 if opts.get('clean'):
916 916 label = repo[None].p1().branch()
917 917 repo.dirstate.setbranch(label)
918 918 ui.status(_('reset working directory to branch %s\n') % label)
919 919 elif label:
920 920 if not opts.get('force') and label in repo.branchmap():
921 921 if label not in [p.branch() for p in repo.parents()]:
922 922 raise util.Abort(_('a branch of the same name already'
923 923 ' exists'),
924 924 # i18n: "it" refers to an existing branch
925 925 hint=_("use 'hg update' to switch to it"))
926 926 repo.dirstate.setbranch(label)
927 927 ui.status(_('marked working directory as branch %s\n') % label)
928 928 ui.status(_('(branches are permanent and global, '
929 929 'did you want a bookmark?)\n'))
930 930 finally:
931 931 wlock.release()
932 932
933 933 @command('branches',
934 934 [('a', 'active', False, _('show only branches that have unmerged heads')),
935 935 ('c', 'closed', False, _('show normal and closed branches'))],
936 936 _('[-ac]'))
937 937 def branches(ui, repo, active=False, closed=False):
938 938 """list repository named branches
939 939
940 940 List the repository's named branches, indicating which ones are
941 941 inactive. If -c/--closed is specified, also list branches which have
942 942 been marked closed (see :hg:`commit --close-branch`).
943 943
944 944 If -a/--active is specified, only show active branches. A branch
945 945 is considered active if it contains repository heads.
946 946
947 947 Use the command :hg:`update` to switch to an existing branch.
948 948
949 949 Returns 0.
950 950 """
951 951
952 952 hexfunc = ui.debugflag and hex or short
953 953
954 954 activebranches = set([repo[n].branch() for n in repo.heads()])
955 955 branches = []
956 956 for tag, heads in repo.branchmap().iteritems():
957 957 for h in reversed(heads):
958 958 ctx = repo[h]
959 959 isopen = not ctx.closesbranch()
960 960 if isopen:
961 961 tip = ctx
962 962 break
963 963 else:
964 964 tip = repo[heads[-1]]
965 965 isactive = tag in activebranches and isopen
966 966 branches.append((tip, isactive, isopen))
967 967 branches.sort(key=lambda i: (i[1], i[0].rev(), i[0].branch(), i[2]),
968 968 reverse=True)
969 969
970 970 for ctx, isactive, isopen in branches:
971 971 if (not active) or isactive:
972 972 if isactive:
973 973 label = 'branches.active'
974 974 notice = ''
975 975 elif not isopen:
976 976 if not closed:
977 977 continue
978 978 label = 'branches.closed'
979 979 notice = _(' (closed)')
980 980 else:
981 981 label = 'branches.inactive'
982 982 notice = _(' (inactive)')
983 983 if ctx.branch() == repo.dirstate.branch():
984 984 label = 'branches.current'
985 985 rev = str(ctx.rev()).rjust(31 - encoding.colwidth(ctx.branch()))
986 986 rev = ui.label('%s:%s' % (rev, hexfunc(ctx.node())),
987 987 'log.changeset changeset.%s' % ctx.phasestr())
988 988 tag = ui.label(ctx.branch(), label)
989 989 if ui.quiet:
990 990 ui.write("%s\n" % tag)
991 991 else:
992 992 ui.write("%s %s%s\n" % (tag, rev, notice))
993 993
994 994 @command('bundle',
995 995 [('f', 'force', None, _('run even when the destination is unrelated')),
996 996 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
997 997 _('REV')),
998 998 ('b', 'branch', [], _('a specific branch you would like to bundle'),
999 999 _('BRANCH')),
1000 1000 ('', 'base', [],
1001 1001 _('a base changeset assumed to be available at the destination'),
1002 1002 _('REV')),
1003 1003 ('a', 'all', None, _('bundle all changesets in the repository')),
1004 1004 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1005 1005 ] + remoteopts,
1006 1006 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1007 1007 def bundle(ui, repo, fname, dest=None, **opts):
1008 1008 """create a changegroup file
1009 1009
1010 1010 Generate a compressed changegroup file collecting changesets not
1011 1011 known to be in another repository.
1012 1012
1013 1013 If you omit the destination repository, then hg assumes the
1014 1014 destination will have all the nodes you specify with --base
1015 1015 parameters. To create a bundle containing all changesets, use
1016 1016 -a/--all (or --base null).
1017 1017
1018 1018 You can change compression method with the -t/--type option.
1019 1019 The available compression methods are: none, bzip2, and
1020 1020 gzip (by default, bundles are compressed using bzip2).
1021 1021
1022 1022 The bundle file can then be transferred using conventional means
1023 1023 and applied to another repository with the unbundle or pull
1024 1024 command. This is useful when direct push and pull are not
1025 1025 available or when exporting an entire repository is undesirable.
1026 1026
1027 1027 Applying bundles preserves all changeset contents including
1028 1028 permissions, copy/rename information, and revision history.
1029 1029
1030 1030 Returns 0 on success, 1 if no changes found.
1031 1031 """
1032 1032 revs = None
1033 1033 if 'rev' in opts:
1034 1034 revs = scmutil.revrange(repo, opts['rev'])
1035 1035
1036 1036 bundletype = opts.get('type', 'bzip2').lower()
1037 1037 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1038 1038 bundletype = btypes.get(bundletype)
1039 1039 if bundletype not in changegroup.bundletypes:
1040 1040 raise util.Abort(_('unknown bundle type specified with --type'))
1041 1041
1042 1042 if opts.get('all'):
1043 1043 base = ['null']
1044 1044 else:
1045 1045 base = scmutil.revrange(repo, opts.get('base'))
1046 1046 if base:
1047 1047 if dest:
1048 1048 raise util.Abort(_("--base is incompatible with specifying "
1049 1049 "a destination"))
1050 1050 common = [repo.lookup(rev) for rev in base]
1051 1051 heads = revs and map(repo.lookup, revs) or revs
1052 1052 cg = repo.getbundle('bundle', heads=heads, common=common)
1053 1053 outgoing = None
1054 1054 else:
1055 1055 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1056 1056 dest, branches = hg.parseurl(dest, opts.get('branch'))
1057 1057 other = hg.peer(repo, opts, dest)
1058 1058 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
1059 1059 heads = revs and map(repo.lookup, revs) or revs
1060 1060 outgoing = discovery.findcommonoutgoing(repo, other,
1061 1061 onlyheads=heads,
1062 1062 force=opts.get('force'),
1063 1063 portable=True)
1064 1064 cg = repo.getlocalbundle('bundle', outgoing)
1065 1065 if not cg:
1066 1066 scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
1067 1067 return 1
1068 1068
1069 1069 changegroup.writebundle(cg, fname, bundletype)
1070 1070
1071 1071 @command('cat',
1072 1072 [('o', 'output', '',
1073 1073 _('print output to file with formatted name'), _('FORMAT')),
1074 1074 ('r', 'rev', '', _('print the given revision'), _('REV')),
1075 1075 ('', 'decode', None, _('apply any matching decode filter')),
1076 1076 ] + walkopts,
1077 1077 _('[OPTION]... FILE...'))
1078 1078 def cat(ui, repo, file1, *pats, **opts):
1079 1079 """output the current or given revision of files
1080 1080
1081 1081 Print the specified files as they were at the given revision. If
1082 1082 no revision is given, the parent of the working directory is used,
1083 1083 or tip if no revision is checked out.
1084 1084
1085 1085 Output may be to a file, in which case the name of the file is
1086 1086 given using a format string. The formatting rules are the same as
1087 1087 for the export command, with the following additions:
1088 1088
1089 1089 :``%s``: basename of file being printed
1090 1090 :``%d``: dirname of file being printed, or '.' if in repository root
1091 1091 :``%p``: root-relative path name of file being printed
1092 1092
1093 1093 Returns 0 on success.
1094 1094 """
1095 1095 ctx = scmutil.revsingle(repo, opts.get('rev'))
1096 1096 err = 1
1097 1097 m = scmutil.match(ctx, (file1,) + pats, opts)
1098 1098 for abs in ctx.walk(m):
1099 1099 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1100 1100 pathname=abs)
1101 1101 data = ctx[abs].data()
1102 1102 if opts.get('decode'):
1103 1103 data = repo.wwritedata(abs, data)
1104 1104 fp.write(data)
1105 1105 fp.close()
1106 1106 err = 0
1107 1107 return err
1108 1108
1109 1109 @command('^clone',
1110 1110 [('U', 'noupdate', None,
1111 1111 _('the clone will include an empty working copy (only a repository)')),
1112 1112 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
1113 1113 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1114 1114 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1115 1115 ('', 'pull', None, _('use pull protocol to copy metadata')),
1116 1116 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1117 1117 ] + remoteopts,
1118 1118 _('[OPTION]... SOURCE [DEST]'))
1119 1119 def clone(ui, source, dest=None, **opts):
1120 1120 """make a copy of an existing repository
1121 1121
1122 1122 Create a copy of an existing repository in a new directory.
1123 1123
1124 1124 If no destination directory name is specified, it defaults to the
1125 1125 basename of the source.
1126 1126
1127 1127 The location of the source is added to the new repository's
1128 1128 ``.hg/hgrc`` file, as the default to be used for future pulls.
1129 1129
1130 1130 Only local paths and ``ssh://`` URLs are supported as
1131 1131 destinations. For ``ssh://`` destinations, no working directory or
1132 1132 ``.hg/hgrc`` will be created on the remote side.
1133 1133
1134 1134 To pull only a subset of changesets, specify one or more revisions
1135 1135 identifiers with -r/--rev or branches with -b/--branch. The
1136 1136 resulting clone will contain only the specified changesets and
1137 1137 their ancestors. These options (or 'clone src#rev dest') imply
1138 1138 --pull, even for local source repositories. Note that specifying a
1139 1139 tag will include the tagged changeset but not the changeset
1140 1140 containing the tag.
1141 1141
1142 1142 To check out a particular version, use -u/--update, or
1143 1143 -U/--noupdate to create a clone with no working directory.
1144 1144
1145 1145 .. container:: verbose
1146 1146
1147 1147 For efficiency, hardlinks are used for cloning whenever the
1148 1148 source and destination are on the same filesystem (note this
1149 1149 applies only to the repository data, not to the working
1150 1150 directory). Some filesystems, such as AFS, implement hardlinking
1151 1151 incorrectly, but do not report errors. In these cases, use the
1152 1152 --pull option to avoid hardlinking.
1153 1153
1154 1154 In some cases, you can clone repositories and the working
1155 1155 directory using full hardlinks with ::
1156 1156
1157 1157 $ cp -al REPO REPOCLONE
1158 1158
1159 1159 This is the fastest way to clone, but it is not always safe. The
1160 1160 operation is not atomic (making sure REPO is not modified during
1161 1161 the operation is up to you) and you have to make sure your
1162 1162 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1163 1163 so). Also, this is not compatible with certain extensions that
1164 1164 place their metadata under the .hg directory, such as mq.
1165 1165
1166 1166 Mercurial will update the working directory to the first applicable
1167 1167 revision from this list:
1168 1168
1169 1169 a) null if -U or the source repository has no changesets
1170 1170 b) if -u . and the source repository is local, the first parent of
1171 1171 the source repository's working directory
1172 1172 c) the changeset specified with -u (if a branch name, this means the
1173 1173 latest head of that branch)
1174 1174 d) the changeset specified with -r
1175 1175 e) the tipmost head specified with -b
1176 1176 f) the tipmost head specified with the url#branch source syntax
1177 1177 g) the tipmost head of the default branch
1178 1178 h) tip
1179 1179
1180 1180 Examples:
1181 1181
1182 1182 - clone a remote repository to a new directory named hg/::
1183 1183
1184 1184 hg clone http://selenic.com/hg
1185 1185
1186 1186 - create a lightweight local clone::
1187 1187
1188 1188 hg clone project/ project-feature/
1189 1189
1190 1190 - clone from an absolute path on an ssh server (note double-slash)::
1191 1191
1192 1192 hg clone ssh://user@server//home/projects/alpha/
1193 1193
1194 1194 - do a high-speed clone over a LAN while checking out a
1195 1195 specified version::
1196 1196
1197 1197 hg clone --uncompressed http://server/repo -u 1.5
1198 1198
1199 1199 - create a repository without changesets after a particular revision::
1200 1200
1201 1201 hg clone -r 04e544 experimental/ good/
1202 1202
1203 1203 - clone (and track) a particular named branch::
1204 1204
1205 1205 hg clone http://selenic.com/hg#stable
1206 1206
1207 1207 See :hg:`help urls` for details on specifying URLs.
1208 1208
1209 1209 Returns 0 on success.
1210 1210 """
1211 1211 if opts.get('noupdate') and opts.get('updaterev'):
1212 1212 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1213 1213
1214 1214 r = hg.clone(ui, opts, source, dest,
1215 1215 pull=opts.get('pull'),
1216 1216 stream=opts.get('uncompressed'),
1217 1217 rev=opts.get('rev'),
1218 1218 update=opts.get('updaterev') or not opts.get('noupdate'),
1219 1219 branch=opts.get('branch'))
1220 1220
1221 1221 return r is None
1222 1222
1223 1223 @command('^commit|ci',
1224 1224 [('A', 'addremove', None,
1225 1225 _('mark new/missing files as added/removed before committing')),
1226 1226 ('', 'close-branch', None,
1227 1227 _('mark a branch as closed, hiding it from the branch list')),
1228 1228 ('', 'amend', None, _('amend the parent of the working dir')),
1229 1229 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1230 1230 _('[OPTION]... [FILE]...'))
1231 1231 def commit(ui, repo, *pats, **opts):
1232 1232 """commit the specified files or all outstanding changes
1233 1233
1234 1234 Commit changes to the given files into the repository. Unlike a
1235 1235 centralized SCM, this operation is a local operation. See
1236 1236 :hg:`push` for a way to actively distribute your changes.
1237 1237
1238 1238 If a list of files is omitted, all changes reported by :hg:`status`
1239 1239 will be committed.
1240 1240
1241 1241 If you are committing the result of a merge, do not provide any
1242 1242 filenames or -I/-X filters.
1243 1243
1244 1244 If no commit message is specified, Mercurial starts your
1245 1245 configured editor where you can enter a message. In case your
1246 1246 commit fails, you will find a backup of your message in
1247 1247 ``.hg/last-message.txt``.
1248 1248
1249 1249 The --amend flag can be used to amend the parent of the
1250 1250 working directory with a new commit that contains the changes
1251 1251 in the parent in addition to those currently reported by :hg:`status`,
1252 1252 if there are any. The old commit is stored in a backup bundle in
1253 1253 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1254 1254 on how to restore it).
1255 1255
1256 1256 Message, user and date are taken from the amended commit unless
1257 1257 specified. When a message isn't specified on the command line,
1258 1258 the editor will open with the message of the amended commit.
1259 1259
1260 1260 It is not possible to amend public changesets (see :hg:`help phases`)
1261 1261 or changesets that have children.
1262 1262
1263 1263 See :hg:`help dates` for a list of formats valid for -d/--date.
1264 1264
1265 1265 Returns 0 on success, 1 if nothing changed.
1266 1266 """
1267 1267 if opts.get('subrepos'):
1268 1268 # Let --subrepos on the command line override config setting.
1269 1269 ui.setconfig('ui', 'commitsubrepos', True)
1270 1270
1271 1271 extra = {}
1272 1272 if opts.get('close_branch'):
1273 1273 if repo['.'].node() not in repo.branchheads():
1274 1274 # The topo heads set is included in the branch heads set of the
1275 1275 # current branch, so it's sufficient to test branchheads
1276 1276 raise util.Abort(_('can only close branch heads'))
1277 1277 extra['close'] = 1
1278 1278
1279 1279 branch = repo[None].branch()
1280 1280 bheads = repo.branchheads(branch)
1281 1281
1282 1282 if opts.get('amend'):
1283 1283 if ui.configbool('ui', 'commitsubrepos'):
1284 1284 raise util.Abort(_('cannot amend recursively'))
1285 1285
1286 1286 old = repo['.']
1287 1287 if old.phase() == phases.public:
1288 1288 raise util.Abort(_('cannot amend public changesets'))
1289 1289 if len(old.parents()) > 1:
1290 1290 raise util.Abort(_('cannot amend merge changesets'))
1291 1291 if len(repo[None].parents()) > 1:
1292 1292 raise util.Abort(_('cannot amend while merging'))
1293 1293 if old.children():
1294 1294 raise util.Abort(_('cannot amend changeset with children'))
1295 1295
1296 1296 e = cmdutil.commiteditor
1297 1297 if opts.get('force_editor'):
1298 1298 e = cmdutil.commitforceeditor
1299 1299
1300 1300 def commitfunc(ui, repo, message, match, opts):
1301 1301 editor = e
1302 1302 # message contains text from -m or -l, if it's empty,
1303 1303 # open the editor with the old message
1304 1304 if not message:
1305 1305 message = old.description()
1306 1306 editor = cmdutil.commitforceeditor
1307 1307 return repo.commit(message,
1308 1308 opts.get('user') or old.user(),
1309 1309 opts.get('date') or old.date(),
1310 1310 match,
1311 1311 editor=editor,
1312 1312 extra=extra)
1313 1313
1314 1314 current = repo._bookmarkcurrent
1315 1315 marks = old.bookmarks()
1316 1316 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1317 1317 if node == old.node():
1318 1318 ui.status(_("nothing changed\n"))
1319 1319 return 1
1320 1320 elif marks:
1321 1321 ui.debug('moving bookmarks %r from %s to %s\n' %
1322 1322 (marks, old.hex(), hex(node)))
1323 1323 for bm in marks:
1324 1324 repo._bookmarks[bm] = node
1325 1325 if bm == current:
1326 1326 bookmarks.setcurrent(repo, bm)
1327 1327 bookmarks.write(repo)
1328 1328 else:
1329 1329 e = cmdutil.commiteditor
1330 1330 if opts.get('force_editor'):
1331 1331 e = cmdutil.commitforceeditor
1332 1332
1333 1333 def commitfunc(ui, repo, message, match, opts):
1334 1334 return repo.commit(message, opts.get('user'), opts.get('date'),
1335 1335 match, editor=e, extra=extra)
1336 1336
1337 1337 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1338 1338
1339 1339 if not node:
1340 1340 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1341 1341 if stat[3]:
1342 1342 ui.status(_("nothing changed (%d missing files, see "
1343 1343 "'hg status')\n") % len(stat[3]))
1344 1344 else:
1345 1345 ui.status(_("nothing changed\n"))
1346 1346 return 1
1347 1347
1348 1348 ctx = repo[node]
1349 1349 parents = ctx.parents()
1350 1350
1351 1351 if (not opts.get('amend') and bheads and node not in bheads and not
1352 1352 [x for x in parents if x.node() in bheads and x.branch() == branch]):
1353 1353 ui.status(_('created new head\n'))
1354 1354 # The message is not printed for initial roots. For the other
1355 1355 # changesets, it is printed in the following situations:
1356 1356 #
1357 1357 # Par column: for the 2 parents with ...
1358 1358 # N: null or no parent
1359 1359 # B: parent is on another named branch
1360 1360 # C: parent is a regular non head changeset
1361 1361 # H: parent was a branch head of the current branch
1362 1362 # Msg column: whether we print "created new head" message
1363 1363 # In the following, it is assumed that there already exists some
1364 1364 # initial branch heads of the current branch, otherwise nothing is
1365 1365 # printed anyway.
1366 1366 #
1367 1367 # Par Msg Comment
1368 1368 # N N y additional topo root
1369 1369 #
1370 1370 # B N y additional branch root
1371 1371 # C N y additional topo head
1372 1372 # H N n usual case
1373 1373 #
1374 1374 # B B y weird additional branch root
1375 1375 # C B y branch merge
1376 1376 # H B n merge with named branch
1377 1377 #
1378 1378 # C C y additional head from merge
1379 1379 # C H n merge with a head
1380 1380 #
1381 1381 # H H n head merge: head count decreases
1382 1382
1383 1383 if not opts.get('close_branch'):
1384 1384 for r in parents:
1385 1385 if r.closesbranch() and r.branch() == branch:
1386 1386 ui.status(_('reopening closed branch head %d\n') % r)
1387 1387
1388 1388 if ui.debugflag:
1389 1389 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
1390 1390 elif ui.verbose:
1391 1391 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
1392 1392
1393 1393 @command('copy|cp',
1394 1394 [('A', 'after', None, _('record a copy that has already occurred')),
1395 1395 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1396 1396 ] + walkopts + dryrunopts,
1397 1397 _('[OPTION]... [SOURCE]... DEST'))
1398 1398 def copy(ui, repo, *pats, **opts):
1399 1399 """mark files as copied for the next commit
1400 1400
1401 1401 Mark dest as having copies of source files. If dest is a
1402 1402 directory, copies are put in that directory. If dest is a file,
1403 1403 the source must be a single file.
1404 1404
1405 1405 By default, this command copies the contents of files as they
1406 1406 exist in the working directory. If invoked with -A/--after, the
1407 1407 operation is recorded, but no copying is performed.
1408 1408
1409 1409 This command takes effect with the next commit. To undo a copy
1410 1410 before that, see :hg:`revert`.
1411 1411
1412 1412 Returns 0 on success, 1 if errors are encountered.
1413 1413 """
1414 1414 wlock = repo.wlock(False)
1415 1415 try:
1416 1416 return cmdutil.copy(ui, repo, pats, opts)
1417 1417 finally:
1418 1418 wlock.release()
1419 1419
1420 1420 @command('debugancestor', [], _('[INDEX] REV1 REV2'))
1421 1421 def debugancestor(ui, repo, *args):
1422 1422 """find the ancestor revision of two revisions in a given index"""
1423 1423 if len(args) == 3:
1424 1424 index, rev1, rev2 = args
1425 1425 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1426 1426 lookup = r.lookup
1427 1427 elif len(args) == 2:
1428 1428 if not repo:
1429 1429 raise util.Abort(_("there is no Mercurial repository here "
1430 1430 "(.hg not found)"))
1431 1431 rev1, rev2 = args
1432 1432 r = repo.changelog
1433 1433 lookup = repo.lookup
1434 1434 else:
1435 1435 raise util.Abort(_('either two or three arguments required'))
1436 1436 a = r.ancestor(lookup(rev1), lookup(rev2))
1437 1437 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1438 1438
1439 1439 @command('debugbuilddag',
1440 1440 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1441 1441 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1442 1442 ('n', 'new-file', None, _('add new file at each rev'))],
1443 1443 _('[OPTION]... [TEXT]'))
1444 1444 def debugbuilddag(ui, repo, text=None,
1445 1445 mergeable_file=False,
1446 1446 overwritten_file=False,
1447 1447 new_file=False):
1448 1448 """builds a repo with a given DAG from scratch in the current empty repo
1449 1449
1450 1450 The description of the DAG is read from stdin if not given on the
1451 1451 command line.
1452 1452
1453 1453 Elements:
1454 1454
1455 1455 - "+n" is a linear run of n nodes based on the current default parent
1456 1456 - "." is a single node based on the current default parent
1457 1457 - "$" resets the default parent to null (implied at the start);
1458 1458 otherwise the default parent is always the last node created
1459 1459 - "<p" sets the default parent to the backref p
1460 1460 - "*p" is a fork at parent p, which is a backref
1461 1461 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1462 1462 - "/p2" is a merge of the preceding node and p2
1463 1463 - ":tag" defines a local tag for the preceding node
1464 1464 - "@branch" sets the named branch for subsequent nodes
1465 1465 - "#...\\n" is a comment up to the end of the line
1466 1466
1467 1467 Whitespace between the above elements is ignored.
1468 1468
1469 1469 A backref is either
1470 1470
1471 1471 - a number n, which references the node curr-n, where curr is the current
1472 1472 node, or
1473 1473 - the name of a local tag you placed earlier using ":tag", or
1474 1474 - empty to denote the default parent.
1475 1475
1476 1476 All string valued-elements are either strictly alphanumeric, or must
1477 1477 be enclosed in double quotes ("..."), with "\\" as escape character.
1478 1478 """
1479 1479
1480 1480 if text is None:
1481 1481 ui.status(_("reading DAG from stdin\n"))
1482 1482 text = ui.fin.read()
1483 1483
1484 1484 cl = repo.changelog
1485 1485 if len(cl) > 0:
1486 1486 raise util.Abort(_('repository is not empty'))
1487 1487
1488 1488 # determine number of revs in DAG
1489 1489 total = 0
1490 1490 for type, data in dagparser.parsedag(text):
1491 1491 if type == 'n':
1492 1492 total += 1
1493 1493
1494 1494 if mergeable_file:
1495 1495 linesperrev = 2
1496 1496 # make a file with k lines per rev
1497 1497 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1498 1498 initialmergedlines.append("")
1499 1499
1500 1500 tags = []
1501 1501
1502 1502 lock = tr = None
1503 1503 try:
1504 1504 lock = repo.lock()
1505 1505 tr = repo.transaction("builddag")
1506 1506
1507 1507 at = -1
1508 1508 atbranch = 'default'
1509 1509 nodeids = []
1510 1510 id = 0
1511 1511 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1512 1512 for type, data in dagparser.parsedag(text):
1513 1513 if type == 'n':
1514 1514 ui.note('node %s\n' % str(data))
1515 1515 id, ps = data
1516 1516
1517 1517 files = []
1518 1518 fctxs = {}
1519 1519
1520 1520 p2 = None
1521 1521 if mergeable_file:
1522 1522 fn = "mf"
1523 1523 p1 = repo[ps[0]]
1524 1524 if len(ps) > 1:
1525 1525 p2 = repo[ps[1]]
1526 1526 pa = p1.ancestor(p2)
1527 1527 base, local, other = [x[fn].data() for x in pa, p1, p2]
1528 1528 m3 = simplemerge.Merge3Text(base, local, other)
1529 1529 ml = [l.strip() for l in m3.merge_lines()]
1530 1530 ml.append("")
1531 1531 elif at > 0:
1532 1532 ml = p1[fn].data().split("\n")
1533 1533 else:
1534 1534 ml = initialmergedlines
1535 1535 ml[id * linesperrev] += " r%i" % id
1536 1536 mergedtext = "\n".join(ml)
1537 1537 files.append(fn)
1538 1538 fctxs[fn] = context.memfilectx(fn, mergedtext)
1539 1539
1540 1540 if overwritten_file:
1541 1541 fn = "of"
1542 1542 files.append(fn)
1543 1543 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1544 1544
1545 1545 if new_file:
1546 1546 fn = "nf%i" % id
1547 1547 files.append(fn)
1548 1548 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1549 1549 if len(ps) > 1:
1550 1550 if not p2:
1551 1551 p2 = repo[ps[1]]
1552 1552 for fn in p2:
1553 1553 if fn.startswith("nf"):
1554 1554 files.append(fn)
1555 1555 fctxs[fn] = p2[fn]
1556 1556
1557 1557 def fctxfn(repo, cx, path):
1558 1558 return fctxs.get(path)
1559 1559
1560 1560 if len(ps) == 0 or ps[0] < 0:
1561 1561 pars = [None, None]
1562 1562 elif len(ps) == 1:
1563 1563 pars = [nodeids[ps[0]], None]
1564 1564 else:
1565 1565 pars = [nodeids[p] for p in ps]
1566 1566 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1567 1567 date=(id, 0),
1568 1568 user="debugbuilddag",
1569 1569 extra={'branch': atbranch})
1570 1570 nodeid = repo.commitctx(cx)
1571 1571 nodeids.append(nodeid)
1572 1572 at = id
1573 1573 elif type == 'l':
1574 1574 id, name = data
1575 1575 ui.note('tag %s\n' % name)
1576 1576 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1577 1577 elif type == 'a':
1578 1578 ui.note('branch %s\n' % data)
1579 1579 atbranch = data
1580 1580 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1581 1581 tr.close()
1582 1582
1583 1583 if tags:
1584 1584 repo.opener.write("localtags", "".join(tags))
1585 1585 finally:
1586 1586 ui.progress(_('building'), None)
1587 1587 release(tr, lock)
1588 1588
1589 1589 @command('debugbundle', [('a', 'all', None, _('show all details'))], _('FILE'))
1590 1590 def debugbundle(ui, bundlepath, all=None, **opts):
1591 1591 """lists the contents of a bundle"""
1592 1592 f = url.open(ui, bundlepath)
1593 1593 try:
1594 1594 gen = changegroup.readbundle(f, bundlepath)
1595 1595 if all:
1596 1596 ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
1597 1597
1598 1598 def showchunks(named):
1599 1599 ui.write("\n%s\n" % named)
1600 1600 chain = None
1601 1601 while True:
1602 1602 chunkdata = gen.deltachunk(chain)
1603 1603 if not chunkdata:
1604 1604 break
1605 1605 node = chunkdata['node']
1606 1606 p1 = chunkdata['p1']
1607 1607 p2 = chunkdata['p2']
1608 1608 cs = chunkdata['cs']
1609 1609 deltabase = chunkdata['deltabase']
1610 1610 delta = chunkdata['delta']
1611 1611 ui.write("%s %s %s %s %s %s\n" %
1612 1612 (hex(node), hex(p1), hex(p2),
1613 1613 hex(cs), hex(deltabase), len(delta)))
1614 1614 chain = node
1615 1615
1616 1616 chunkdata = gen.changelogheader()
1617 1617 showchunks("changelog")
1618 1618 chunkdata = gen.manifestheader()
1619 1619 showchunks("manifest")
1620 1620 while True:
1621 1621 chunkdata = gen.filelogheader()
1622 1622 if not chunkdata:
1623 1623 break
1624 1624 fname = chunkdata['filename']
1625 1625 showchunks(fname)
1626 1626 else:
1627 1627 chunkdata = gen.changelogheader()
1628 1628 chain = None
1629 1629 while True:
1630 1630 chunkdata = gen.deltachunk(chain)
1631 1631 if not chunkdata:
1632 1632 break
1633 1633 node = chunkdata['node']
1634 1634 ui.write("%s\n" % hex(node))
1635 1635 chain = node
1636 1636 finally:
1637 1637 f.close()
1638 1638
1639 1639 @command('debugcheckstate', [], '')
1640 1640 def debugcheckstate(ui, repo):
1641 1641 """validate the correctness of the current dirstate"""
1642 1642 parent1, parent2 = repo.dirstate.parents()
1643 1643 m1 = repo[parent1].manifest()
1644 1644 m2 = repo[parent2].manifest()
1645 1645 errors = 0
1646 1646 for f in repo.dirstate:
1647 1647 state = repo.dirstate[f]
1648 1648 if state in "nr" and f not in m1:
1649 1649 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1650 1650 errors += 1
1651 1651 if state in "a" and f in m1:
1652 1652 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1653 1653 errors += 1
1654 1654 if state in "m" and f not in m1 and f not in m2:
1655 1655 ui.warn(_("%s in state %s, but not in either manifest\n") %
1656 1656 (f, state))
1657 1657 errors += 1
1658 1658 for f in m1:
1659 1659 state = repo.dirstate[f]
1660 1660 if state not in "nrm":
1661 1661 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1662 1662 errors += 1
1663 1663 if errors:
1664 1664 error = _(".hg/dirstate inconsistent with current parent's manifest")
1665 1665 raise util.Abort(error)
1666 1666
1667 1667 @command('debugcommands', [], _('[COMMAND]'))
1668 1668 def debugcommands(ui, cmd='', *args):
1669 1669 """list all available commands and options"""
1670 1670 for cmd, vals in sorted(table.iteritems()):
1671 1671 cmd = cmd.split('|')[0].strip('^')
1672 1672 opts = ', '.join([i[1] for i in vals[1]])
1673 1673 ui.write('%s: %s\n' % (cmd, opts))
1674 1674
1675 1675 @command('debugcomplete',
1676 1676 [('o', 'options', None, _('show the command options'))],
1677 1677 _('[-o] CMD'))
1678 1678 def debugcomplete(ui, cmd='', **opts):
1679 1679 """returns the completion list associated with the given command"""
1680 1680
1681 1681 if opts.get('options'):
1682 1682 options = []
1683 1683 otables = [globalopts]
1684 1684 if cmd:
1685 1685 aliases, entry = cmdutil.findcmd(cmd, table, False)
1686 1686 otables.append(entry[1])
1687 1687 for t in otables:
1688 1688 for o in t:
1689 1689 if "(DEPRECATED)" in o[3]:
1690 1690 continue
1691 1691 if o[0]:
1692 1692 options.append('-%s' % o[0])
1693 1693 options.append('--%s' % o[1])
1694 1694 ui.write("%s\n" % "\n".join(options))
1695 1695 return
1696 1696
1697 1697 cmdlist = cmdutil.findpossible(cmd, table)
1698 1698 if ui.verbose:
1699 1699 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1700 1700 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1701 1701
1702 1702 @command('debugdag',
1703 1703 [('t', 'tags', None, _('use tags as labels')),
1704 1704 ('b', 'branches', None, _('annotate with branch names')),
1705 1705 ('', 'dots', None, _('use dots for runs')),
1706 1706 ('s', 'spaces', None, _('separate elements by spaces'))],
1707 1707 _('[OPTION]... [FILE [REV]...]'))
1708 1708 def debugdag(ui, repo, file_=None, *revs, **opts):
1709 1709 """format the changelog or an index DAG as a concise textual description
1710 1710
1711 1711 If you pass a revlog index, the revlog's DAG is emitted. If you list
1712 1712 revision numbers, they get labeled in the output as rN.
1713 1713
1714 1714 Otherwise, the changelog DAG of the current repo is emitted.
1715 1715 """
1716 1716 spaces = opts.get('spaces')
1717 1717 dots = opts.get('dots')
1718 1718 if file_:
1719 1719 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1720 1720 revs = set((int(r) for r in revs))
1721 1721 def events():
1722 1722 for r in rlog:
1723 1723 yield 'n', (r, list(set(p for p in rlog.parentrevs(r)
1724 1724 if p != -1)))
1725 1725 if r in revs:
1726 1726 yield 'l', (r, "r%i" % r)
1727 1727 elif repo:
1728 1728 cl = repo.changelog
1729 1729 tags = opts.get('tags')
1730 1730 branches = opts.get('branches')
1731 1731 if tags:
1732 1732 labels = {}
1733 1733 for l, n in repo.tags().items():
1734 1734 labels.setdefault(cl.rev(n), []).append(l)
1735 1735 def events():
1736 1736 b = "default"
1737 1737 for r in cl:
1738 1738 if branches:
1739 1739 newb = cl.read(cl.node(r))[5]['branch']
1740 1740 if newb != b:
1741 1741 yield 'a', newb
1742 1742 b = newb
1743 1743 yield 'n', (r, list(set(p for p in cl.parentrevs(r)
1744 1744 if p != -1)))
1745 1745 if tags:
1746 1746 ls = labels.get(r)
1747 1747 if ls:
1748 1748 for l in ls:
1749 1749 yield 'l', (r, l)
1750 1750 else:
1751 1751 raise util.Abort(_('need repo for changelog dag'))
1752 1752
1753 1753 for line in dagparser.dagtextlines(events(),
1754 1754 addspaces=spaces,
1755 1755 wraplabels=True,
1756 1756 wrapannotations=True,
1757 1757 wrapnonlinear=dots,
1758 1758 usedots=dots,
1759 1759 maxlinewidth=70):
1760 1760 ui.write(line)
1761 1761 ui.write("\n")
1762 1762
1763 1763 @command('debugdata',
1764 1764 [('c', 'changelog', False, _('open changelog')),
1765 1765 ('m', 'manifest', False, _('open manifest'))],
1766 1766 _('-c|-m|FILE REV'))
1767 1767 def debugdata(ui, repo, file_, rev = None, **opts):
1768 1768 """dump the contents of a data file revision"""
1769 1769 if opts.get('changelog') or opts.get('manifest'):
1770 1770 file_, rev = None, file_
1771 1771 elif rev is None:
1772 1772 raise error.CommandError('debugdata', _('invalid arguments'))
1773 1773 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
1774 1774 try:
1775 1775 ui.write(r.revision(r.lookup(rev)))
1776 1776 except KeyError:
1777 1777 raise util.Abort(_('invalid revision identifier %s') % rev)
1778 1778
1779 1779 @command('debugdate',
1780 1780 [('e', 'extended', None, _('try extended date formats'))],
1781 1781 _('[-e] DATE [RANGE]'))
1782 1782 def debugdate(ui, date, range=None, **opts):
1783 1783 """parse and display a date"""
1784 1784 if opts["extended"]:
1785 1785 d = util.parsedate(date, util.extendeddateformats)
1786 1786 else:
1787 1787 d = util.parsedate(date)
1788 1788 ui.write("internal: %s %s\n" % d)
1789 1789 ui.write("standard: %s\n" % util.datestr(d))
1790 1790 if range:
1791 1791 m = util.matchdate(range)
1792 1792 ui.write("match: %s\n" % m(d[0]))
1793 1793
1794 1794 @command('debugdiscovery',
1795 1795 [('', 'old', None, _('use old-style discovery')),
1796 1796 ('', 'nonheads', None,
1797 1797 _('use old-style discovery with non-heads included')),
1798 1798 ] + remoteopts,
1799 1799 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
1800 1800 def debugdiscovery(ui, repo, remoteurl="default", **opts):
1801 1801 """runs the changeset discovery protocol in isolation"""
1802 1802 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl),
1803 1803 opts.get('branch'))
1804 1804 remote = hg.peer(repo, opts, remoteurl)
1805 1805 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
1806 1806
1807 1807 # make sure tests are repeatable
1808 1808 random.seed(12323)
1809 1809
1810 1810 def doit(localheads, remoteheads, remote=remote):
1811 1811 if opts.get('old'):
1812 1812 if localheads:
1813 1813 raise util.Abort('cannot use localheads with old style '
1814 1814 'discovery')
1815 1815 if not util.safehasattr(remote, 'branches'):
1816 1816 # enable in-client legacy support
1817 1817 remote = localrepo.locallegacypeer(remote.local())
1818 1818 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
1819 1819 force=True)
1820 1820 common = set(common)
1821 1821 if not opts.get('nonheads'):
1822 1822 ui.write("unpruned common: %s\n" % " ".join([short(n)
1823 1823 for n in common]))
1824 1824 dag = dagutil.revlogdag(repo.changelog)
1825 1825 all = dag.ancestorset(dag.internalizeall(common))
1826 1826 common = dag.externalizeall(dag.headsetofconnecteds(all))
1827 1827 else:
1828 1828 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
1829 1829 common = set(common)
1830 1830 rheads = set(hds)
1831 1831 lheads = set(repo.heads())
1832 1832 ui.write("common heads: %s\n" % " ".join([short(n) for n in common]))
1833 1833 if lheads <= common:
1834 1834 ui.write("local is subset\n")
1835 1835 elif rheads <= common:
1836 1836 ui.write("remote is subset\n")
1837 1837
1838 1838 serverlogs = opts.get('serverlog')
1839 1839 if serverlogs:
1840 1840 for filename in serverlogs:
1841 1841 logfile = open(filename, 'r')
1842 1842 try:
1843 1843 line = logfile.readline()
1844 1844 while line:
1845 1845 parts = line.strip().split(';')
1846 1846 op = parts[1]
1847 1847 if op == 'cg':
1848 1848 pass
1849 1849 elif op == 'cgss':
1850 1850 doit(parts[2].split(' '), parts[3].split(' '))
1851 1851 elif op == 'unb':
1852 1852 doit(parts[3].split(' '), parts[2].split(' '))
1853 1853 line = logfile.readline()
1854 1854 finally:
1855 1855 logfile.close()
1856 1856
1857 1857 else:
1858 1858 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
1859 1859 opts.get('remote_head'))
1860 1860 localrevs = opts.get('local_head')
1861 1861 doit(localrevs, remoterevs)
1862 1862
1863 1863 @command('debugfileset',
1864 1864 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
1865 1865 _('[-r REV] FILESPEC'))
1866 1866 def debugfileset(ui, repo, expr, **opts):
1867 1867 '''parse and apply a fileset specification'''
1868 1868 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
1869 1869 if ui.verbose:
1870 1870 tree = fileset.parse(expr)[0]
1871 1871 ui.note(tree, "\n")
1872 1872
1873 1873 for f in fileset.getfileset(ctx, expr):
1874 1874 ui.write("%s\n" % f)
1875 1875
1876 1876 @command('debugfsinfo', [], _('[PATH]'))
1877 1877 def debugfsinfo(ui, path = "."):
1878 1878 """show information detected about current filesystem"""
1879 1879 util.writefile('.debugfsinfo', '')
1880 1880 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1881 1881 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1882 1882 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1883 1883 and 'yes' or 'no'))
1884 1884 os.unlink('.debugfsinfo')
1885 1885
1886 1886 @command('debuggetbundle',
1887 1887 [('H', 'head', [], _('id of head node'), _('ID')),
1888 1888 ('C', 'common', [], _('id of common node'), _('ID')),
1889 1889 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
1890 1890 _('REPO FILE [-H|-C ID]...'))
1891 1891 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1892 1892 """retrieves a bundle from a repo
1893 1893
1894 1894 Every ID must be a full-length hex node id string. Saves the bundle to the
1895 1895 given file.
1896 1896 """
1897 1897 repo = hg.peer(ui, opts, repopath)
1898 1898 if not repo.capable('getbundle'):
1899 1899 raise util.Abort("getbundle() not supported by target repository")
1900 1900 args = {}
1901 1901 if common:
1902 1902 args['common'] = [bin(s) for s in common]
1903 1903 if head:
1904 1904 args['heads'] = [bin(s) for s in head]
1905 1905 bundle = repo.getbundle('debug', **args)
1906 1906
1907 1907 bundletype = opts.get('type', 'bzip2').lower()
1908 1908 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1909 1909 bundletype = btypes.get(bundletype)
1910 1910 if bundletype not in changegroup.bundletypes:
1911 1911 raise util.Abort(_('unknown bundle type specified with --type'))
1912 1912 changegroup.writebundle(bundle, bundlepath, bundletype)
1913 1913
1914 1914 @command('debugignore', [], '')
1915 1915 def debugignore(ui, repo, *values, **opts):
1916 1916 """display the combined ignore pattern"""
1917 1917 ignore = repo.dirstate._ignore
1918 1918 includepat = getattr(ignore, 'includepat', None)
1919 1919 if includepat is not None:
1920 1920 ui.write("%s\n" % includepat)
1921 1921 else:
1922 1922 raise util.Abort(_("no ignore patterns found"))
1923 1923
1924 1924 @command('debugindex',
1925 1925 [('c', 'changelog', False, _('open changelog')),
1926 1926 ('m', 'manifest', False, _('open manifest')),
1927 1927 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1928 1928 _('[-f FORMAT] -c|-m|FILE'))
1929 1929 def debugindex(ui, repo, file_ = None, **opts):
1930 1930 """dump the contents of an index file"""
1931 1931 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
1932 1932 format = opts.get('format', 0)
1933 1933 if format not in (0, 1):
1934 1934 raise util.Abort(_("unknown format %d") % format)
1935 1935
1936 1936 generaldelta = r.version & revlog.REVLOGGENERALDELTA
1937 1937 if generaldelta:
1938 1938 basehdr = ' delta'
1939 1939 else:
1940 1940 basehdr = ' base'
1941 1941
1942 1942 if format == 0:
1943 1943 ui.write(" rev offset length " + basehdr + " linkrev"
1944 1944 " nodeid p1 p2\n")
1945 1945 elif format == 1:
1946 1946 ui.write(" rev flag offset length"
1947 1947 " size " + basehdr + " link p1 p2"
1948 1948 " nodeid\n")
1949 1949
1950 1950 for i in r:
1951 1951 node = r.node(i)
1952 1952 if generaldelta:
1953 1953 base = r.deltaparent(i)
1954 1954 else:
1955 1955 base = r.chainbase(i)
1956 1956 if format == 0:
1957 1957 try:
1958 1958 pp = r.parents(node)
1959 1959 except Exception:
1960 1960 pp = [nullid, nullid]
1961 1961 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1962 1962 i, r.start(i), r.length(i), base, r.linkrev(i),
1963 1963 short(node), short(pp[0]), short(pp[1])))
1964 1964 elif format == 1:
1965 1965 pr = r.parentrevs(i)
1966 1966 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1967 1967 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1968 1968 base, r.linkrev(i), pr[0], pr[1], short(node)))
1969 1969
1970 1970 @command('debugindexdot', [], _('FILE'))
1971 1971 def debugindexdot(ui, repo, file_):
1972 1972 """dump an index DAG as a graphviz dot file"""
1973 1973 r = None
1974 1974 if repo:
1975 1975 filelog = repo.file(file_)
1976 1976 if len(filelog):
1977 1977 r = filelog
1978 1978 if not r:
1979 1979 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1980 1980 ui.write("digraph G {\n")
1981 1981 for i in r:
1982 1982 node = r.node(i)
1983 1983 pp = r.parents(node)
1984 1984 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1985 1985 if pp[1] != nullid:
1986 1986 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1987 1987 ui.write("}\n")
1988 1988
1989 1989 @command('debuginstall', [], '')
1990 1990 def debuginstall(ui):
1991 1991 '''test Mercurial installation
1992 1992
1993 1993 Returns 0 on success.
1994 1994 '''
1995 1995
1996 1996 def writetemp(contents):
1997 1997 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1998 1998 f = os.fdopen(fd, "wb")
1999 1999 f.write(contents)
2000 2000 f.close()
2001 2001 return name
2002 2002
2003 2003 problems = 0
2004 2004
2005 2005 # encoding
2006 2006 ui.status(_("checking encoding (%s)...\n") % encoding.encoding)
2007 2007 try:
2008 2008 encoding.fromlocal("test")
2009 2009 except util.Abort, inst:
2010 2010 ui.write(" %s\n" % inst)
2011 2011 ui.write(_(" (check that your locale is properly set)\n"))
2012 2012 problems += 1
2013 2013
2014 2014 # Python lib
2015 2015 ui.status(_("checking Python lib (%s)...\n")
2016 2016 % os.path.dirname(os.__file__))
2017 2017
2018 2018 # compiled modules
2019 2019 ui.status(_("checking installed modules (%s)...\n")
2020 2020 % os.path.dirname(__file__))
2021 2021 try:
2022 2022 import bdiff, mpatch, base85, osutil
2023 2023 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
2024 2024 except Exception, inst:
2025 2025 ui.write(" %s\n" % inst)
2026 2026 ui.write(_(" One or more extensions could not be found"))
2027 2027 ui.write(_(" (check that you compiled the extensions)\n"))
2028 2028 problems += 1
2029 2029
2030 2030 # templates
2031 2031 import templater
2032 2032 p = templater.templatepath()
2033 2033 ui.status(_("checking templates (%s)...\n") % ' '.join(p))
2034 2034 try:
2035 2035 templater.templater(templater.templatepath("map-cmdline.default"))
2036 2036 except Exception, inst:
2037 2037 ui.write(" %s\n" % inst)
2038 2038 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
2039 2039 problems += 1
2040 2040
2041 2041 # editor
2042 2042 ui.status(_("checking commit editor...\n"))
2043 2043 editor = ui.geteditor()
2044 2044 cmdpath = util.findexe(editor) or util.findexe(editor.split()[0])
2045 2045 if not cmdpath:
2046 2046 if editor == 'vi':
2047 2047 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
2048 2048 ui.write(_(" (specify a commit editor in your configuration"
2049 2049 " file)\n"))
2050 2050 else:
2051 2051 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
2052 2052 ui.write(_(" (specify a commit editor in your configuration"
2053 2053 " file)\n"))
2054 2054 problems += 1
2055 2055
2056 2056 # check username
2057 2057 ui.status(_("checking username...\n"))
2058 2058 try:
2059 2059 ui.username()
2060 2060 except util.Abort, e:
2061 2061 ui.write(" %s\n" % e)
2062 2062 ui.write(_(" (specify a username in your configuration file)\n"))
2063 2063 problems += 1
2064 2064
2065 2065 if not problems:
2066 2066 ui.status(_("no problems detected\n"))
2067 2067 else:
2068 2068 ui.write(_("%s problems detected,"
2069 2069 " please check your install!\n") % problems)
2070 2070
2071 2071 return problems
2072 2072
2073 2073 @command('debugknown', [], _('REPO ID...'))
2074 2074 def debugknown(ui, repopath, *ids, **opts):
2075 2075 """test whether node ids are known to a repo
2076 2076
2077 2077 Every ID must be a full-length hex node id string. Returns a list of 0s
2078 2078 and 1s indicating unknown/known.
2079 2079 """
2080 2080 repo = hg.peer(ui, opts, repopath)
2081 2081 if not repo.capable('known'):
2082 2082 raise util.Abort("known() not supported by target repository")
2083 2083 flags = repo.known([bin(s) for s in ids])
2084 2084 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2085 2085
2086 2086 @command('debugobsolete', [] + commitopts2,
2087 2087 _('[OBSOLETED [REPLACEMENT] [REPL... ]'))
2088 2088 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
2089 2089 """create arbitrary obsolete marker"""
2090 2090 def parsenodeid(s):
2091 2091 try:
2092 2092 # We do not use revsingle/revrange functions here to accept
2093 2093 # arbitrary node identifiers, possibly not present in the
2094 2094 # local repository.
2095 2095 n = bin(s)
2096 2096 if len(n) != len(nullid):
2097 2097 raise TypeError()
2098 2098 return n
2099 2099 except TypeError:
2100 2100 raise util.Abort('changeset references must be full hexadecimal '
2101 2101 'node identifiers')
2102 2102
2103 2103 if precursor is not None:
2104 2104 metadata = {}
2105 2105 if 'date' in opts:
2106 2106 metadata['date'] = opts['date']
2107 2107 metadata['user'] = opts['user'] or ui.username()
2108 2108 succs = tuple(parsenodeid(succ) for succ in successors)
2109 2109 l = repo.lock()
2110 2110 try:
2111 2111 tr = repo.transaction('debugobsolete')
2112 2112 try:
2113 2113 repo.obsstore.create(tr, parsenodeid(precursor), succs, 0,
2114 2114 metadata)
2115 2115 tr.close()
2116 2116 finally:
2117 2117 tr.release()
2118 2118 finally:
2119 2119 l.release()
2120 2120 else:
2121 2121 for m in obsolete.allmarkers(repo):
2122 2122 ui.write(hex(m.precnode()))
2123 2123 for repl in m.succnodes():
2124 2124 ui.write(' ')
2125 2125 ui.write(hex(repl))
2126 2126 ui.write(' %X ' % m._data[2])
2127 2127 ui.write(m.metadata())
2128 2128 ui.write('\n')
2129 2129
2130 2130 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'))
2131 2131 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2132 2132 '''access the pushkey key/value protocol
2133 2133
2134 2134 With two args, list the keys in the given namespace.
2135 2135
2136 2136 With five args, set a key to new if it currently is set to old.
2137 2137 Reports success or failure.
2138 2138 '''
2139 2139
2140 2140 target = hg.peer(ui, {}, repopath)
2141 2141 if keyinfo:
2142 2142 key, old, new = keyinfo
2143 2143 r = target.pushkey(namespace, key, old, new)
2144 2144 ui.status(str(r) + '\n')
2145 2145 return not r
2146 2146 else:
2147 2147 for k, v in target.listkeys(namespace).iteritems():
2148 2148 ui.write("%s\t%s\n" % (k.encode('string-escape'),
2149 2149 v.encode('string-escape')))
2150 2150
2151 2151 @command('debugpvec', [], _('A B'))
2152 2152 def debugpvec(ui, repo, a, b=None):
2153 2153 ca = scmutil.revsingle(repo, a)
2154 2154 cb = scmutil.revsingle(repo, b)
2155 2155 pa = pvec.ctxpvec(ca)
2156 2156 pb = pvec.ctxpvec(cb)
2157 2157 if pa == pb:
2158 2158 rel = "="
2159 2159 elif pa > pb:
2160 2160 rel = ">"
2161 2161 elif pa < pb:
2162 2162 rel = "<"
2163 2163 elif pa | pb:
2164 2164 rel = "|"
2165 2165 ui.write(_("a: %s\n") % pa)
2166 2166 ui.write(_("b: %s\n") % pb)
2167 2167 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2168 2168 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
2169 2169 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
2170 2170 pa.distance(pb), rel))
2171 2171
2172 2172 @command('debugrebuildstate',
2173 2173 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
2174 2174 _('[-r REV] [REV]'))
2175 2175 def debugrebuildstate(ui, repo, rev="tip"):
2176 2176 """rebuild the dirstate as it would look like for the given revision"""
2177 2177 ctx = scmutil.revsingle(repo, rev)
2178 2178 wlock = repo.wlock()
2179 2179 try:
2180 2180 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
2181 2181 finally:
2182 2182 wlock.release()
2183 2183
2184 2184 @command('debugrename',
2185 2185 [('r', 'rev', '', _('revision to debug'), _('REV'))],
2186 2186 _('[-r REV] FILE'))
2187 2187 def debugrename(ui, repo, file1, *pats, **opts):
2188 2188 """dump rename information"""
2189 2189
2190 2190 ctx = scmutil.revsingle(repo, opts.get('rev'))
2191 2191 m = scmutil.match(ctx, (file1,) + pats, opts)
2192 2192 for abs in ctx.walk(m):
2193 2193 fctx = ctx[abs]
2194 2194 o = fctx.filelog().renamed(fctx.filenode())
2195 2195 rel = m.rel(abs)
2196 2196 if o:
2197 2197 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2198 2198 else:
2199 2199 ui.write(_("%s not renamed\n") % rel)
2200 2200
2201 2201 @command('debugrevlog',
2202 2202 [('c', 'changelog', False, _('open changelog')),
2203 2203 ('m', 'manifest', False, _('open manifest')),
2204 2204 ('d', 'dump', False, _('dump index data'))],
2205 2205 _('-c|-m|FILE'))
2206 2206 def debugrevlog(ui, repo, file_ = None, **opts):
2207 2207 """show data and statistics about a revlog"""
2208 2208 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
2209 2209
2210 2210 if opts.get("dump"):
2211 2211 numrevs = len(r)
2212 2212 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
2213 2213 " rawsize totalsize compression heads\n")
2214 2214 ts = 0
2215 2215 heads = set()
2216 2216 for rev in xrange(numrevs):
2217 2217 dbase = r.deltaparent(rev)
2218 2218 if dbase == -1:
2219 2219 dbase = rev
2220 2220 cbase = r.chainbase(rev)
2221 2221 p1, p2 = r.parentrevs(rev)
2222 2222 rs = r.rawsize(rev)
2223 2223 ts = ts + rs
2224 2224 heads -= set(r.parentrevs(rev))
2225 2225 heads.add(rev)
2226 2226 ui.write("%d %d %d %d %d %d %d %d %d %d %d %d %d\n" %
2227 2227 (rev, p1, p2, r.start(rev), r.end(rev),
2228 2228 r.start(dbase), r.start(cbase),
2229 2229 r.start(p1), r.start(p2),
2230 2230 rs, ts, ts / r.end(rev), len(heads)))
2231 2231 return 0
2232 2232
2233 2233 v = r.version
2234 2234 format = v & 0xFFFF
2235 2235 flags = []
2236 2236 gdelta = False
2237 2237 if v & revlog.REVLOGNGINLINEDATA:
2238 2238 flags.append('inline')
2239 2239 if v & revlog.REVLOGGENERALDELTA:
2240 2240 gdelta = True
2241 2241 flags.append('generaldelta')
2242 2242 if not flags:
2243 2243 flags = ['(none)']
2244 2244
2245 2245 nummerges = 0
2246 2246 numfull = 0
2247 2247 numprev = 0
2248 2248 nump1 = 0
2249 2249 nump2 = 0
2250 2250 numother = 0
2251 2251 nump1prev = 0
2252 2252 nump2prev = 0
2253 2253 chainlengths = []
2254 2254
2255 2255 datasize = [None, 0, 0L]
2256 2256 fullsize = [None, 0, 0L]
2257 2257 deltasize = [None, 0, 0L]
2258 2258
2259 2259 def addsize(size, l):
2260 2260 if l[0] is None or size < l[0]:
2261 2261 l[0] = size
2262 2262 if size > l[1]:
2263 2263 l[1] = size
2264 2264 l[2] += size
2265 2265
2266 2266 numrevs = len(r)
2267 2267 for rev in xrange(numrevs):
2268 2268 p1, p2 = r.parentrevs(rev)
2269 2269 delta = r.deltaparent(rev)
2270 2270 if format > 0:
2271 2271 addsize(r.rawsize(rev), datasize)
2272 2272 if p2 != nullrev:
2273 2273 nummerges += 1
2274 2274 size = r.length(rev)
2275 2275 if delta == nullrev:
2276 2276 chainlengths.append(0)
2277 2277 numfull += 1
2278 2278 addsize(size, fullsize)
2279 2279 else:
2280 2280 chainlengths.append(chainlengths[delta] + 1)
2281 2281 addsize(size, deltasize)
2282 2282 if delta == rev - 1:
2283 2283 numprev += 1
2284 2284 if delta == p1:
2285 2285 nump1prev += 1
2286 2286 elif delta == p2:
2287 2287 nump2prev += 1
2288 2288 elif delta == p1:
2289 2289 nump1 += 1
2290 2290 elif delta == p2:
2291 2291 nump2 += 1
2292 2292 elif delta != nullrev:
2293 2293 numother += 1
2294 2294
2295 2295 # Adjust size min value for empty cases
2296 2296 for size in (datasize, fullsize, deltasize):
2297 2297 if size[0] is None:
2298 2298 size[0] = 0
2299 2299
2300 2300 numdeltas = numrevs - numfull
2301 2301 numoprev = numprev - nump1prev - nump2prev
2302 2302 totalrawsize = datasize[2]
2303 2303 datasize[2] /= numrevs
2304 2304 fulltotal = fullsize[2]
2305 2305 fullsize[2] /= numfull
2306 2306 deltatotal = deltasize[2]
2307 2307 if numrevs - numfull > 0:
2308 2308 deltasize[2] /= numrevs - numfull
2309 2309 totalsize = fulltotal + deltatotal
2310 2310 avgchainlen = sum(chainlengths) / numrevs
2311 2311 compratio = totalrawsize / totalsize
2312 2312
2313 2313 basedfmtstr = '%%%dd\n'
2314 2314 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2315 2315
2316 2316 def dfmtstr(max):
2317 2317 return basedfmtstr % len(str(max))
2318 2318 def pcfmtstr(max, padding=0):
2319 2319 return basepcfmtstr % (len(str(max)), ' ' * padding)
2320 2320
2321 2321 def pcfmt(value, total):
2322 2322 return (value, 100 * float(value) / total)
2323 2323
2324 2324 ui.write('format : %d\n' % format)
2325 2325 ui.write('flags : %s\n' % ', '.join(flags))
2326 2326
2327 2327 ui.write('\n')
2328 2328 fmt = pcfmtstr(totalsize)
2329 2329 fmt2 = dfmtstr(totalsize)
2330 2330 ui.write('revisions : ' + fmt2 % numrevs)
2331 2331 ui.write(' merges : ' + fmt % pcfmt(nummerges, numrevs))
2332 2332 ui.write(' normal : ' + fmt % pcfmt(numrevs - nummerges, numrevs))
2333 2333 ui.write('revisions : ' + fmt2 % numrevs)
2334 2334 ui.write(' full : ' + fmt % pcfmt(numfull, numrevs))
2335 2335 ui.write(' deltas : ' + fmt % pcfmt(numdeltas, numrevs))
2336 2336 ui.write('revision size : ' + fmt2 % totalsize)
2337 2337 ui.write(' full : ' + fmt % pcfmt(fulltotal, totalsize))
2338 2338 ui.write(' deltas : ' + fmt % pcfmt(deltatotal, totalsize))
2339 2339
2340 2340 ui.write('\n')
2341 2341 fmt = dfmtstr(max(avgchainlen, compratio))
2342 2342 ui.write('avg chain length : ' + fmt % avgchainlen)
2343 2343 ui.write('compression ratio : ' + fmt % compratio)
2344 2344
2345 2345 if format > 0:
2346 2346 ui.write('\n')
2347 2347 ui.write('uncompressed data size (min/max/avg) : %d / %d / %d\n'
2348 2348 % tuple(datasize))
2349 2349 ui.write('full revision size (min/max/avg) : %d / %d / %d\n'
2350 2350 % tuple(fullsize))
2351 2351 ui.write('delta size (min/max/avg) : %d / %d / %d\n'
2352 2352 % tuple(deltasize))
2353 2353
2354 2354 if numdeltas > 0:
2355 2355 ui.write('\n')
2356 2356 fmt = pcfmtstr(numdeltas)
2357 2357 fmt2 = pcfmtstr(numdeltas, 4)
2358 2358 ui.write('deltas against prev : ' + fmt % pcfmt(numprev, numdeltas))
2359 2359 if numprev > 0:
2360 2360 ui.write(' where prev = p1 : ' + fmt2 % pcfmt(nump1prev,
2361 2361 numprev))
2362 2362 ui.write(' where prev = p2 : ' + fmt2 % pcfmt(nump2prev,
2363 2363 numprev))
2364 2364 ui.write(' other : ' + fmt2 % pcfmt(numoprev,
2365 2365 numprev))
2366 2366 if gdelta:
2367 2367 ui.write('deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas))
2368 2368 ui.write('deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas))
2369 2369 ui.write('deltas against other : ' + fmt % pcfmt(numother,
2370 2370 numdeltas))
2371 2371
2372 2372 @command('debugrevspec', [], ('REVSPEC'))
2373 2373 def debugrevspec(ui, repo, expr):
2374 2374 """parse and apply a revision specification
2375 2375
2376 2376 Use --verbose to print the parsed tree before and after aliases
2377 2377 expansion.
2378 2378 """
2379 2379 if ui.verbose:
2380 2380 tree = revset.parse(expr)[0]
2381 2381 ui.note(revset.prettyformat(tree), "\n")
2382 2382 newtree = revset.findaliases(ui, tree)
2383 2383 if newtree != tree:
2384 2384 ui.note(revset.prettyformat(newtree), "\n")
2385 2385 func = revset.match(ui, expr)
2386 2386 for c in func(repo, range(len(repo))):
2387 2387 ui.write("%s\n" % c)
2388 2388
2389 2389 @command('debugsetparents', [], _('REV1 [REV2]'))
2390 2390 def debugsetparents(ui, repo, rev1, rev2=None):
2391 2391 """manually set the parents of the current working directory
2392 2392
2393 2393 This is useful for writing repository conversion tools, but should
2394 2394 be used with care.
2395 2395
2396 2396 Returns 0 on success.
2397 2397 """
2398 2398
2399 2399 r1 = scmutil.revsingle(repo, rev1).node()
2400 2400 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2401 2401
2402 2402 wlock = repo.wlock()
2403 2403 try:
2404 2404 repo.setparents(r1, r2)
2405 2405 finally:
2406 2406 wlock.release()
2407 2407
2408 2408 @command('debugstate',
2409 2409 [('', 'nodates', None, _('do not display the saved mtime')),
2410 2410 ('', 'datesort', None, _('sort by saved mtime'))],
2411 2411 _('[OPTION]...'))
2412 2412 def debugstate(ui, repo, nodates=None, datesort=None):
2413 2413 """show the contents of the current dirstate"""
2414 2414 timestr = ""
2415 2415 showdate = not nodates
2416 2416 if datesort:
2417 2417 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2418 2418 else:
2419 2419 keyfunc = None # sort by filename
2420 2420 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2421 2421 if showdate:
2422 2422 if ent[3] == -1:
2423 2423 # Pad or slice to locale representation
2424 2424 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
2425 2425 time.localtime(0)))
2426 2426 timestr = 'unset'
2427 2427 timestr = (timestr[:locale_len] +
2428 2428 ' ' * (locale_len - len(timestr)))
2429 2429 else:
2430 2430 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2431 2431 time.localtime(ent[3]))
2432 2432 if ent[1] & 020000:
2433 2433 mode = 'lnk'
2434 2434 else:
2435 2435 mode = '%3o' % (ent[1] & 0777 & ~util.umask)
2436 2436 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2437 2437 for f in repo.dirstate.copies():
2438 2438 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2439 2439
2440 2440 @command('debugsub',
2441 2441 [('r', 'rev', '',
2442 2442 _('revision to check'), _('REV'))],
2443 2443 _('[-r REV] [REV]'))
2444 2444 def debugsub(ui, repo, rev=None):
2445 2445 ctx = scmutil.revsingle(repo, rev, None)
2446 2446 for k, v in sorted(ctx.substate.items()):
2447 2447 ui.write('path %s\n' % k)
2448 2448 ui.write(' source %s\n' % v[0])
2449 2449 ui.write(' revision %s\n' % v[1])
2450 2450
2451 2451 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'))
2452 2452 def debugwalk(ui, repo, *pats, **opts):
2453 2453 """show how files match on given patterns"""
2454 2454 m = scmutil.match(repo[None], pats, opts)
2455 2455 items = list(repo.walk(m))
2456 2456 if not items:
2457 2457 return
2458 2458 f = lambda fn: fn
2459 2459 if ui.configbool('ui', 'slash') and os.sep != '/':
2460 2460 f = lambda fn: util.normpath(fn)
2461 2461 fmt = 'f %%-%ds %%-%ds %%s' % (
2462 2462 max([len(abs) for abs in items]),
2463 2463 max([len(m.rel(abs)) for abs in items]))
2464 2464 for abs in items:
2465 2465 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
2466 2466 ui.write("%s\n" % line.rstrip())
2467 2467
2468 2468 @command('debugwireargs',
2469 2469 [('', 'three', '', 'three'),
2470 2470 ('', 'four', '', 'four'),
2471 2471 ('', 'five', '', 'five'),
2472 2472 ] + remoteopts,
2473 2473 _('REPO [OPTIONS]... [ONE [TWO]]'))
2474 2474 def debugwireargs(ui, repopath, *vals, **opts):
2475 2475 repo = hg.peer(ui, opts, repopath)
2476 2476 for opt in remoteopts:
2477 2477 del opts[opt[1]]
2478 2478 args = {}
2479 2479 for k, v in opts.iteritems():
2480 2480 if v:
2481 2481 args[k] = v
2482 2482 # run twice to check that we don't mess up the stream for the next command
2483 2483 res1 = repo.debugwireargs(*vals, **args)
2484 2484 res2 = repo.debugwireargs(*vals, **args)
2485 2485 ui.write("%s\n" % res1)
2486 2486 if res1 != res2:
2487 2487 ui.warn("%s\n" % res2)
2488 2488
2489 2489 @command('^diff',
2490 2490 [('r', 'rev', [], _('revision'), _('REV')),
2491 2491 ('c', 'change', '', _('change made by revision'), _('REV'))
2492 2492 ] + diffopts + diffopts2 + walkopts + subrepoopts,
2493 2493 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'))
2494 2494 def diff(ui, repo, *pats, **opts):
2495 2495 """diff repository (or selected files)
2496 2496
2497 2497 Show differences between revisions for the specified files.
2498 2498
2499 2499 Differences between files are shown using the unified diff format.
2500 2500
2501 2501 .. note::
2502 2502 diff may generate unexpected results for merges, as it will
2503 2503 default to comparing against the working directory's first
2504 2504 parent changeset if no revisions are specified.
2505 2505
2506 2506 When two revision arguments are given, then changes are shown
2507 2507 between those revisions. If only one revision is specified then
2508 2508 that revision is compared to the working directory, and, when no
2509 2509 revisions are specified, the working directory files are compared
2510 2510 to its parent.
2511 2511
2512 2512 Alternatively you can specify -c/--change with a revision to see
2513 2513 the changes in that changeset relative to its first parent.
2514 2514
2515 2515 Without the -a/--text option, diff will avoid generating diffs of
2516 2516 files it detects as binary. With -a, diff will generate a diff
2517 2517 anyway, probably with undesirable results.
2518 2518
2519 2519 Use the -g/--git option to generate diffs in the git extended diff
2520 2520 format. For more information, read :hg:`help diffs`.
2521 2521
2522 2522 .. container:: verbose
2523 2523
2524 2524 Examples:
2525 2525
2526 2526 - compare a file in the current working directory to its parent::
2527 2527
2528 2528 hg diff foo.c
2529 2529
2530 2530 - compare two historical versions of a directory, with rename info::
2531 2531
2532 2532 hg diff --git -r 1.0:1.2 lib/
2533 2533
2534 2534 - get change stats relative to the last change on some date::
2535 2535
2536 2536 hg diff --stat -r "date('may 2')"
2537 2537
2538 2538 - diff all newly-added files that contain a keyword::
2539 2539
2540 2540 hg diff "set:added() and grep(GNU)"
2541 2541
2542 2542 - compare a revision and its parents::
2543 2543
2544 2544 hg diff -c 9353 # compare against first parent
2545 2545 hg diff -r 9353^:9353 # same using revset syntax
2546 2546 hg diff -r 9353^2:9353 # compare against the second parent
2547 2547
2548 2548 Returns 0 on success.
2549 2549 """
2550 2550
2551 2551 revs = opts.get('rev')
2552 2552 change = opts.get('change')
2553 2553 stat = opts.get('stat')
2554 2554 reverse = opts.get('reverse')
2555 2555
2556 2556 if revs and change:
2557 2557 msg = _('cannot specify --rev and --change at the same time')
2558 2558 raise util.Abort(msg)
2559 2559 elif change:
2560 2560 node2 = scmutil.revsingle(repo, change, None).node()
2561 2561 node1 = repo[node2].p1().node()
2562 2562 else:
2563 2563 node1, node2 = scmutil.revpair(repo, revs)
2564 2564
2565 2565 if reverse:
2566 2566 node1, node2 = node2, node1
2567 2567
2568 2568 diffopts = patch.diffopts(ui, opts)
2569 2569 m = scmutil.match(repo[node2], pats, opts)
2570 2570 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2571 2571 listsubrepos=opts.get('subrepos'))
2572 2572
2573 2573 @command('^export',
2574 2574 [('o', 'output', '',
2575 2575 _('print output to file with formatted name'), _('FORMAT')),
2576 2576 ('', 'switch-parent', None, _('diff against the second parent')),
2577 2577 ('r', 'rev', [], _('revisions to export'), _('REV')),
2578 2578 ] + diffopts,
2579 2579 _('[OPTION]... [-o OUTFILESPEC] [-r] REV...'))
2580 2580 def export(ui, repo, *changesets, **opts):
2581 2581 """dump the header and diffs for one or more changesets
2582 2582
2583 2583 Print the changeset header and diffs for one or more revisions.
2584 2584
2585 2585 The information shown in the changeset header is: author, date,
2586 2586 branch name (if non-default), changeset hash, parent(s) and commit
2587 2587 comment.
2588 2588
2589 2589 .. note::
2590 2590 export may generate unexpected diff output for merge
2591 2591 changesets, as it will compare the merge changeset against its
2592 2592 first parent only.
2593 2593
2594 2594 Output may be to a file, in which case the name of the file is
2595 2595 given using a format string. The formatting rules are as follows:
2596 2596
2597 2597 :``%%``: literal "%" character
2598 2598 :``%H``: changeset hash (40 hexadecimal digits)
2599 2599 :``%N``: number of patches being generated
2600 2600 :``%R``: changeset revision number
2601 2601 :``%b``: basename of the exporting repository
2602 2602 :``%h``: short-form changeset hash (12 hexadecimal digits)
2603 2603 :``%m``: first line of the commit message (only alphanumeric characters)
2604 2604 :``%n``: zero-padded sequence number, starting at 1
2605 2605 :``%r``: zero-padded changeset revision number
2606 2606
2607 2607 Without the -a/--text option, export will avoid generating diffs
2608 2608 of files it detects as binary. With -a, export will generate a
2609 2609 diff anyway, probably with undesirable results.
2610 2610
2611 2611 Use the -g/--git option to generate diffs in the git extended diff
2612 2612 format. See :hg:`help diffs` for more information.
2613 2613
2614 2614 With the --switch-parent option, the diff will be against the
2615 2615 second parent. It can be useful to review a merge.
2616 2616
2617 2617 .. container:: verbose
2618 2618
2619 2619 Examples:
2620 2620
2621 2621 - use export and import to transplant a bugfix to the current
2622 2622 branch::
2623 2623
2624 2624 hg export -r 9353 | hg import -
2625 2625
2626 2626 - export all the changesets between two revisions to a file with
2627 2627 rename information::
2628 2628
2629 2629 hg export --git -r 123:150 > changes.txt
2630 2630
2631 2631 - split outgoing changes into a series of patches with
2632 2632 descriptive names::
2633 2633
2634 2634 hg export -r "outgoing()" -o "%n-%m.patch"
2635 2635
2636 2636 Returns 0 on success.
2637 2637 """
2638 2638 changesets += tuple(opts.get('rev', []))
2639 2639 revs = scmutil.revrange(repo, changesets)
2640 2640 if not revs:
2641 2641 raise util.Abort(_("export requires at least one changeset"))
2642 2642 if len(revs) > 1:
2643 2643 ui.note(_('exporting patches:\n'))
2644 2644 else:
2645 2645 ui.note(_('exporting patch:\n'))
2646 2646 cmdutil.export(repo, revs, template=opts.get('output'),
2647 2647 switch_parent=opts.get('switch_parent'),
2648 2648 opts=patch.diffopts(ui, opts))
2649 2649
2650 2650 @command('^forget', walkopts, _('[OPTION]... FILE...'))
2651 2651 def forget(ui, repo, *pats, **opts):
2652 2652 """forget the specified files on the next commit
2653 2653
2654 2654 Mark the specified files so they will no longer be tracked
2655 2655 after the next commit.
2656 2656
2657 2657 This only removes files from the current branch, not from the
2658 2658 entire project history, and it does not delete them from the
2659 2659 working directory.
2660 2660
2661 2661 To undo a forget before the next commit, see :hg:`add`.
2662 2662
2663 2663 .. container:: verbose
2664 2664
2665 2665 Examples:
2666 2666
2667 2667 - forget newly-added binary files::
2668 2668
2669 2669 hg forget "set:added() and binary()"
2670 2670
2671 2671 - forget files that would be excluded by .hgignore::
2672 2672
2673 2673 hg forget "set:hgignore()"
2674 2674
2675 2675 Returns 0 on success.
2676 2676 """
2677 2677
2678 2678 if not pats:
2679 2679 raise util.Abort(_('no files specified'))
2680 2680
2681 2681 m = scmutil.match(repo[None], pats, opts)
2682 2682 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
2683 2683 return rejected and 1 or 0
2684 2684
2685 2685 @command(
2686 2686 'graft',
2687 2687 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2688 2688 ('c', 'continue', False, _('resume interrupted graft')),
2689 2689 ('e', 'edit', False, _('invoke editor on commit messages')),
2690 2690 ('', 'log', None, _('append graft info to log message')),
2691 2691 ('D', 'currentdate', False,
2692 2692 _('record the current date as commit date')),
2693 2693 ('U', 'currentuser', False,
2694 2694 _('record the current user as committer'), _('DATE'))]
2695 2695 + commitopts2 + mergetoolopts + dryrunopts,
2696 2696 _('[OPTION]... [-r] REV...'))
2697 2697 def graft(ui, repo, *revs, **opts):
2698 2698 '''copy changes from other branches onto the current branch
2699 2699
2700 2700 This command uses Mercurial's merge logic to copy individual
2701 2701 changes from other branches without merging branches in the
2702 2702 history graph. This is sometimes known as 'backporting' or
2703 2703 'cherry-picking'. By default, graft will copy user, date, and
2704 2704 description from the source changesets.
2705 2705
2706 2706 Changesets that are ancestors of the current revision, that have
2707 2707 already been grafted, or that are merges will be skipped.
2708 2708
2709 2709 If --log is specified, log messages will have a comment appended
2710 2710 of the form::
2711 2711
2712 2712 (grafted from CHANGESETHASH)
2713 2713
2714 2714 If a graft merge results in conflicts, the graft process is
2715 2715 interrupted so that the current merge can be manually resolved.
2716 2716 Once all conflicts are addressed, the graft process can be
2717 2717 continued with the -c/--continue option.
2718 2718
2719 2719 .. note::
2720 2720 The -c/--continue option does not reapply earlier options.
2721 2721
2722 2722 .. container:: verbose
2723 2723
2724 2724 Examples:
2725 2725
2726 2726 - copy a single change to the stable branch and edit its description::
2727 2727
2728 2728 hg update stable
2729 2729 hg graft --edit 9393
2730 2730
2731 2731 - graft a range of changesets with one exception, updating dates::
2732 2732
2733 2733 hg graft -D "2085::2093 and not 2091"
2734 2734
2735 2735 - continue a graft after resolving conflicts::
2736 2736
2737 2737 hg graft -c
2738 2738
2739 2739 - show the source of a grafted changeset::
2740 2740
2741 2741 hg log --debug -r tip
2742 2742
2743 2743 Returns 0 on successful completion.
2744 2744 '''
2745 2745
2746 2746 revs = list(revs)
2747 2747 revs.extend(opts['rev'])
2748 2748
2749 2749 if not opts.get('user') and opts.get('currentuser'):
2750 2750 opts['user'] = ui.username()
2751 2751 if not opts.get('date') and opts.get('currentdate'):
2752 2752 opts['date'] = "%d %d" % util.makedate()
2753 2753
2754 2754 editor = None
2755 2755 if opts.get('edit'):
2756 2756 editor = cmdutil.commitforceeditor
2757 2757
2758 2758 cont = False
2759 2759 if opts['continue']:
2760 2760 cont = True
2761 2761 if revs:
2762 2762 raise util.Abort(_("can't specify --continue and revisions"))
2763 2763 # read in unfinished revisions
2764 2764 try:
2765 2765 nodes = repo.opener.read('graftstate').splitlines()
2766 2766 revs = [repo[node].rev() for node in nodes]
2767 2767 except IOError, inst:
2768 2768 if inst.errno != errno.ENOENT:
2769 2769 raise
2770 2770 raise util.Abort(_("no graft state found, can't continue"))
2771 2771 else:
2772 2772 cmdutil.bailifchanged(repo)
2773 2773 if not revs:
2774 2774 raise util.Abort(_('no revisions specified'))
2775 2775 revs = scmutil.revrange(repo, revs)
2776 2776
2777 2777 # check for merges
2778 2778 for rev in repo.revs('%ld and merge()', revs):
2779 2779 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
2780 2780 revs.remove(rev)
2781 2781 if not revs:
2782 2782 return -1
2783 2783
2784 2784 # check for ancestors of dest branch
2785 2785 for rev in repo.revs('::. and %ld', revs):
2786 2786 ui.warn(_('skipping ancestor revision %s\n') % rev)
2787 2787 revs.remove(rev)
2788 2788 if not revs:
2789 2789 return -1
2790 2790
2791 2791 # analyze revs for earlier grafts
2792 2792 ids = {}
2793 2793 for ctx in repo.set("%ld", revs):
2794 2794 ids[ctx.hex()] = ctx.rev()
2795 2795 n = ctx.extra().get('source')
2796 2796 if n:
2797 2797 ids[n] = ctx.rev()
2798 2798
2799 2799 # check ancestors for earlier grafts
2800 2800 ui.debug('scanning for duplicate grafts\n')
2801 2801 for ctx in repo.set("::. - ::%ld", revs):
2802 2802 n = ctx.extra().get('source')
2803 2803 if n in ids:
2804 2804 r = repo[n].rev()
2805 2805 if r in revs:
2806 2806 ui.warn(_('skipping already grafted revision %s\n') % r)
2807 2807 revs.remove(r)
2808 2808 elif ids[n] in revs:
2809 2809 ui.warn(_('skipping already grafted revision %s '
2810 2810 '(same origin %d)\n') % (ids[n], r))
2811 2811 revs.remove(ids[n])
2812 2812 elif ctx.hex() in ids:
2813 2813 r = ids[ctx.hex()]
2814 2814 ui.warn(_('skipping already grafted revision %s '
2815 2815 '(was grafted from %d)\n') % (r, ctx.rev()))
2816 2816 revs.remove(r)
2817 2817 if not revs:
2818 2818 return -1
2819 2819
2820 2820 wlock = repo.wlock()
2821 2821 try:
2822 2822 for pos, ctx in enumerate(repo.set("%ld", revs)):
2823 2823 current = repo['.']
2824 2824
2825 2825 ui.status(_('grafting revision %s\n') % ctx.rev())
2826 2826 if opts.get('dry_run'):
2827 2827 continue
2828 2828
2829 2829 # we don't merge the first commit when continuing
2830 2830 if not cont:
2831 2831 # perform the graft merge with p1(rev) as 'ancestor'
2832 2832 try:
2833 2833 # ui.forcemerge is an internal variable, do not document
2834 2834 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
2835 2835 stats = mergemod.update(repo, ctx.node(), True, True, False,
2836 2836 ctx.p1().node())
2837 2837 finally:
2838 2838 repo.ui.setconfig('ui', 'forcemerge', '')
2839 2839 # report any conflicts
2840 2840 if stats and stats[3] > 0:
2841 2841 # write out state for --continue
2842 2842 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2843 2843 repo.opener.write('graftstate', ''.join(nodelines))
2844 2844 raise util.Abort(
2845 2845 _("unresolved conflicts, can't continue"),
2846 2846 hint=_('use hg resolve and hg graft --continue'))
2847 2847 else:
2848 2848 cont = False
2849 2849
2850 2850 # drop the second merge parent
2851 2851 repo.setparents(current.node(), nullid)
2852 2852 repo.dirstate.write()
2853 2853 # fix up dirstate for copies and renames
2854 2854 cmdutil.duplicatecopies(repo, ctx.rev(), ctx.p1().rev())
2855 2855
2856 2856 # commit
2857 2857 source = ctx.extra().get('source')
2858 2858 if not source:
2859 2859 source = ctx.hex()
2860 2860 extra = {'source': source}
2861 2861 user = ctx.user()
2862 2862 if opts.get('user'):
2863 2863 user = opts['user']
2864 2864 date = ctx.date()
2865 2865 if opts.get('date'):
2866 2866 date = opts['date']
2867 2867 message = ctx.description()
2868 2868 if opts.get('log'):
2869 2869 message += '\n(grafted from %s)' % ctx.hex()
2870 2870 node = repo.commit(text=message, user=user,
2871 2871 date=date, extra=extra, editor=editor)
2872 2872 if node is None:
2873 2873 ui.status(_('graft for revision %s is empty\n') % ctx.rev())
2874 2874 finally:
2875 2875 wlock.release()
2876 2876
2877 2877 # remove state when we complete successfully
2878 2878 if not opts.get('dry_run') and os.path.exists(repo.join('graftstate')):
2879 2879 util.unlinkpath(repo.join('graftstate'))
2880 2880
2881 2881 return 0
2882 2882
2883 2883 @command('grep',
2884 2884 [('0', 'print0', None, _('end fields with NUL')),
2885 2885 ('', 'all', None, _('print all revisions that match')),
2886 2886 ('a', 'text', None, _('treat all files as text')),
2887 2887 ('f', 'follow', None,
2888 2888 _('follow changeset history,'
2889 2889 ' or file history across copies and renames')),
2890 2890 ('i', 'ignore-case', None, _('ignore case when matching')),
2891 2891 ('l', 'files-with-matches', None,
2892 2892 _('print only filenames and revisions that match')),
2893 2893 ('n', 'line-number', None, _('print matching line numbers')),
2894 2894 ('r', 'rev', [],
2895 2895 _('only search files changed within revision range'), _('REV')),
2896 2896 ('u', 'user', None, _('list the author (long with -v)')),
2897 2897 ('d', 'date', None, _('list the date (short with -q)')),
2898 2898 ] + walkopts,
2899 2899 _('[OPTION]... PATTERN [FILE]...'))
2900 2900 def grep(ui, repo, pattern, *pats, **opts):
2901 2901 """search for a pattern in specified files and revisions
2902 2902
2903 2903 Search revisions of files for a regular expression.
2904 2904
2905 2905 This command behaves differently than Unix grep. It only accepts
2906 2906 Python/Perl regexps. It searches repository history, not the
2907 2907 working directory. It always prints the revision number in which a
2908 2908 match appears.
2909 2909
2910 2910 By default, grep only prints output for the first revision of a
2911 2911 file in which it finds a match. To get it to print every revision
2912 2912 that contains a change in match status ("-" for a match that
2913 2913 becomes a non-match, or "+" for a non-match that becomes a match),
2914 2914 use the --all flag.
2915 2915
2916 2916 Returns 0 if a match is found, 1 otherwise.
2917 2917 """
2918 2918 reflags = re.M
2919 2919 if opts.get('ignore_case'):
2920 2920 reflags |= re.I
2921 2921 try:
2922 2922 regexp = re.compile(pattern, reflags)
2923 2923 except re.error, inst:
2924 2924 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2925 2925 return 1
2926 2926 sep, eol = ':', '\n'
2927 2927 if opts.get('print0'):
2928 2928 sep = eol = '\0'
2929 2929
2930 2930 getfile = util.lrucachefunc(repo.file)
2931 2931
2932 2932 def matchlines(body):
2933 2933 begin = 0
2934 2934 linenum = 0
2935 2935 while True:
2936 2936 match = regexp.search(body, begin)
2937 2937 if not match:
2938 2938 break
2939 2939 mstart, mend = match.span()
2940 2940 linenum += body.count('\n', begin, mstart) + 1
2941 2941 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2942 2942 begin = body.find('\n', mend) + 1 or len(body) + 1
2943 2943 lend = begin - 1
2944 2944 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2945 2945
2946 2946 class linestate(object):
2947 2947 def __init__(self, line, linenum, colstart, colend):
2948 2948 self.line = line
2949 2949 self.linenum = linenum
2950 2950 self.colstart = colstart
2951 2951 self.colend = colend
2952 2952
2953 2953 def __hash__(self):
2954 2954 return hash((self.linenum, self.line))
2955 2955
2956 2956 def __eq__(self, other):
2957 2957 return self.line == other.line
2958 2958
2959 2959 matches = {}
2960 2960 copies = {}
2961 2961 def grepbody(fn, rev, body):
2962 2962 matches[rev].setdefault(fn, [])
2963 2963 m = matches[rev][fn]
2964 2964 for lnum, cstart, cend, line in matchlines(body):
2965 2965 s = linestate(line, lnum, cstart, cend)
2966 2966 m.append(s)
2967 2967
2968 2968 def difflinestates(a, b):
2969 2969 sm = difflib.SequenceMatcher(None, a, b)
2970 2970 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2971 2971 if tag == 'insert':
2972 2972 for i in xrange(blo, bhi):
2973 2973 yield ('+', b[i])
2974 2974 elif tag == 'delete':
2975 2975 for i in xrange(alo, ahi):
2976 2976 yield ('-', a[i])
2977 2977 elif tag == 'replace':
2978 2978 for i in xrange(alo, ahi):
2979 2979 yield ('-', a[i])
2980 2980 for i in xrange(blo, bhi):
2981 2981 yield ('+', b[i])
2982 2982
2983 2983 def display(fn, ctx, pstates, states):
2984 2984 rev = ctx.rev()
2985 2985 datefunc = ui.quiet and util.shortdate or util.datestr
2986 2986 found = False
2987 2987 filerevmatches = {}
2988 2988 def binary():
2989 2989 flog = getfile(fn)
2990 2990 return util.binary(flog.read(ctx.filenode(fn)))
2991 2991
2992 2992 if opts.get('all'):
2993 2993 iter = difflinestates(pstates, states)
2994 2994 else:
2995 2995 iter = [('', l) for l in states]
2996 2996 for change, l in iter:
2997 cols = [fn, str(rev)]
2997 cols = [(fn, 'grep.filename'), (str(rev), 'grep.rev')]
2998 2998 before, match, after = None, None, None
2999
2999 3000 if opts.get('line_number'):
3000 cols.append(str(l.linenum))
3001 cols.append((str(l.linenum), 'grep.linenumber'))
3001 3002 if opts.get('all'):
3002 cols.append(change)
3003 cols.append((change, 'grep.change'))
3003 3004 if opts.get('user'):
3004 cols.append(ui.shortuser(ctx.user()))
3005 cols.append((ui.shortuser(ctx.user()), 'grep.user'))
3005 3006 if opts.get('date'):
3006 cols.append(datefunc(ctx.date()))
3007 cols.append((datefunc(ctx.date()), 'grep.date'))
3007 3008 if opts.get('files_with_matches'):
3008 3009 c = (fn, rev)
3009 3010 if c in filerevmatches:
3010 3011 continue
3011 3012 filerevmatches[c] = 1
3012 3013 else:
3013 3014 before = l.line[:l.colstart]
3014 3015 match = l.line[l.colstart:l.colend]
3015 3016 after = l.line[l.colend:]
3016 ui.write(sep.join(cols))
3017 for col, label in cols[:-1]:
3018 ui.write(col, label=label)
3019 ui.write(sep, label='grep.sep')
3020 ui.write(cols[-1][0], label=cols[-1][1])
3017 3021 if before is not None:
3022 ui.write(sep, label='grep.sep')
3018 3023 if not opts.get('text') and binary():
3019 ui.write(sep + " Binary file matches")
3024 ui.write(" Binary file matches")
3020 3025 else:
3021 ui.write(sep + before)
3026 ui.write(before)
3022 3027 ui.write(match, label='grep.match')
3023 3028 ui.write(after)
3024 3029 ui.write(eol)
3025 3030 found = True
3026 3031 return found
3027 3032
3028 3033 skip = {}
3029 3034 revfiles = {}
3030 3035 matchfn = scmutil.match(repo[None], pats, opts)
3031 3036 found = False
3032 3037 follow = opts.get('follow')
3033 3038
3034 3039 def prep(ctx, fns):
3035 3040 rev = ctx.rev()
3036 3041 pctx = ctx.p1()
3037 3042 parent = pctx.rev()
3038 3043 matches.setdefault(rev, {})
3039 3044 matches.setdefault(parent, {})
3040 3045 files = revfiles.setdefault(rev, [])
3041 3046 for fn in fns:
3042 3047 flog = getfile(fn)
3043 3048 try:
3044 3049 fnode = ctx.filenode(fn)
3045 3050 except error.LookupError:
3046 3051 continue
3047 3052
3048 3053 copied = flog.renamed(fnode)
3049 3054 copy = follow and copied and copied[0]
3050 3055 if copy:
3051 3056 copies.setdefault(rev, {})[fn] = copy
3052 3057 if fn in skip:
3053 3058 if copy:
3054 3059 skip[copy] = True
3055 3060 continue
3056 3061 files.append(fn)
3057 3062
3058 3063 if fn not in matches[rev]:
3059 3064 grepbody(fn, rev, flog.read(fnode))
3060 3065
3061 3066 pfn = copy or fn
3062 3067 if pfn not in matches[parent]:
3063 3068 try:
3064 3069 fnode = pctx.filenode(pfn)
3065 3070 grepbody(pfn, parent, flog.read(fnode))
3066 3071 except error.LookupError:
3067 3072 pass
3068 3073
3069 3074 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3070 3075 rev = ctx.rev()
3071 3076 parent = ctx.p1().rev()
3072 3077 for fn in sorted(revfiles.get(rev, [])):
3073 3078 states = matches[rev][fn]
3074 3079 copy = copies.get(rev, {}).get(fn)
3075 3080 if fn in skip:
3076 3081 if copy:
3077 3082 skip[copy] = True
3078 3083 continue
3079 3084 pstates = matches.get(parent, {}).get(copy or fn, [])
3080 3085 if pstates or states:
3081 3086 r = display(fn, ctx, pstates, states)
3082 3087 found = found or r
3083 3088 if r and not opts.get('all'):
3084 3089 skip[fn] = True
3085 3090 if copy:
3086 3091 skip[copy] = True
3087 3092 del matches[rev]
3088 3093 del revfiles[rev]
3089 3094
3090 3095 return not found
3091 3096
3092 3097 @command('heads',
3093 3098 [('r', 'rev', '',
3094 3099 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3095 3100 ('t', 'topo', False, _('show topological heads only')),
3096 3101 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3097 3102 ('c', 'closed', False, _('show normal and closed branch heads')),
3098 3103 ] + templateopts,
3099 3104 _('[-ct] [-r STARTREV] [REV]...'))
3100 3105 def heads(ui, repo, *branchrevs, **opts):
3101 3106 """show current repository heads or show branch heads
3102 3107
3103 3108 With no arguments, show all repository branch heads.
3104 3109
3105 3110 Repository "heads" are changesets with no child changesets. They are
3106 3111 where development generally takes place and are the usual targets
3107 3112 for update and merge operations. Branch heads are changesets that have
3108 3113 no child changeset on the same branch.
3109 3114
3110 3115 If one or more REVs are given, only branch heads on the branches
3111 3116 associated with the specified changesets are shown. This means
3112 3117 that you can use :hg:`heads foo` to see the heads on a branch
3113 3118 named ``foo``.
3114 3119
3115 3120 If -c/--closed is specified, also show branch heads marked closed
3116 3121 (see :hg:`commit --close-branch`).
3117 3122
3118 3123 If STARTREV is specified, only those heads that are descendants of
3119 3124 STARTREV will be displayed.
3120 3125
3121 3126 If -t/--topo is specified, named branch mechanics will be ignored and only
3122 3127 changesets without children will be shown.
3123 3128
3124 3129 Returns 0 if matching heads are found, 1 if not.
3125 3130 """
3126 3131
3127 3132 start = None
3128 3133 if 'rev' in opts:
3129 3134 start = scmutil.revsingle(repo, opts['rev'], None).node()
3130 3135
3131 3136 if opts.get('topo'):
3132 3137 heads = [repo[h] for h in repo.heads(start)]
3133 3138 else:
3134 3139 heads = []
3135 3140 for branch in repo.branchmap():
3136 3141 heads += repo.branchheads(branch, start, opts.get('closed'))
3137 3142 heads = [repo[h] for h in heads]
3138 3143
3139 3144 if branchrevs:
3140 3145 branches = set(repo[br].branch() for br in branchrevs)
3141 3146 heads = [h for h in heads if h.branch() in branches]
3142 3147
3143 3148 if opts.get('active') and branchrevs:
3144 3149 dagheads = repo.heads(start)
3145 3150 heads = [h for h in heads if h.node() in dagheads]
3146 3151
3147 3152 if branchrevs:
3148 3153 haveheads = set(h.branch() for h in heads)
3149 3154 if branches - haveheads:
3150 3155 headless = ', '.join(b for b in branches - haveheads)
3151 3156 msg = _('no open branch heads found on branches %s')
3152 3157 if opts.get('rev'):
3153 3158 msg += _(' (started at %s)') % opts['rev']
3154 3159 ui.warn((msg + '\n') % headless)
3155 3160
3156 3161 if not heads:
3157 3162 return 1
3158 3163
3159 3164 heads = sorted(heads, key=lambda x: -x.rev())
3160 3165 displayer = cmdutil.show_changeset(ui, repo, opts)
3161 3166 for ctx in heads:
3162 3167 displayer.show(ctx)
3163 3168 displayer.close()
3164 3169
3165 3170 @command('help',
3166 3171 [('e', 'extension', None, _('show only help for extensions')),
3167 3172 ('c', 'command', None, _('show only help for commands')),
3168 3173 ('k', 'keyword', '', _('show topics matching keyword')),
3169 3174 ],
3170 3175 _('[-ec] [TOPIC]'))
3171 3176 def help_(ui, name=None, unknowncmd=False, full=True, **opts):
3172 3177 """show help for a given topic or a help overview
3173 3178
3174 3179 With no arguments, print a list of commands with short help messages.
3175 3180
3176 3181 Given a topic, extension, or command name, print help for that
3177 3182 topic.
3178 3183
3179 3184 Returns 0 if successful.
3180 3185 """
3181 3186
3182 3187 textwidth = min(ui.termwidth(), 80) - 2
3183 3188
3184 3189 def helpcmd(name):
3185 3190 try:
3186 3191 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
3187 3192 except error.AmbiguousCommand, inst:
3188 3193 # py3k fix: except vars can't be used outside the scope of the
3189 3194 # except block, nor can be used inside a lambda. python issue4617
3190 3195 prefix = inst.args[0]
3191 3196 select = lambda c: c.lstrip('^').startswith(prefix)
3192 3197 rst = helplist(select)
3193 3198 return rst
3194 3199
3195 3200 rst = []
3196 3201
3197 3202 # check if it's an invalid alias and display its error if it is
3198 3203 if getattr(entry[0], 'badalias', False):
3199 3204 if not unknowncmd:
3200 3205 ui.pushbuffer()
3201 3206 entry[0](ui)
3202 3207 rst.append(ui.popbuffer())
3203 3208 return rst
3204 3209
3205 3210 # synopsis
3206 3211 if len(entry) > 2:
3207 3212 if entry[2].startswith('hg'):
3208 3213 rst.append("%s\n" % entry[2])
3209 3214 else:
3210 3215 rst.append('hg %s %s\n' % (aliases[0], entry[2]))
3211 3216 else:
3212 3217 rst.append('hg %s\n' % aliases[0])
3213 3218 # aliases
3214 3219 if full and not ui.quiet and len(aliases) > 1:
3215 3220 rst.append(_("\naliases: %s\n") % ', '.join(aliases[1:]))
3216 3221 rst.append('\n')
3217 3222
3218 3223 # description
3219 3224 doc = gettext(entry[0].__doc__)
3220 3225 if not doc:
3221 3226 doc = _("(no help text available)")
3222 3227 if util.safehasattr(entry[0], 'definition'): # aliased command
3223 3228 if entry[0].definition.startswith('!'): # shell alias
3224 3229 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
3225 3230 else:
3226 3231 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
3227 3232 doc = doc.splitlines(True)
3228 3233 if ui.quiet or not full:
3229 3234 rst.append(doc[0])
3230 3235 else:
3231 3236 rst.extend(doc)
3232 3237 rst.append('\n')
3233 3238
3234 3239 # check if this command shadows a non-trivial (multi-line)
3235 3240 # extension help text
3236 3241 try:
3237 3242 mod = extensions.find(name)
3238 3243 doc = gettext(mod.__doc__) or ''
3239 3244 if '\n' in doc.strip():
3240 3245 msg = _('use "hg help -e %s" to show help for '
3241 3246 'the %s extension') % (name, name)
3242 3247 rst.append('\n%s\n' % msg)
3243 3248 except KeyError:
3244 3249 pass
3245 3250
3246 3251 # options
3247 3252 if not ui.quiet and entry[1]:
3248 3253 rst.append('\n%s\n\n' % _("options:"))
3249 3254 rst.append(help.optrst(entry[1], ui.verbose))
3250 3255
3251 3256 if ui.verbose:
3252 3257 rst.append('\n%s\n\n' % _("global options:"))
3253 3258 rst.append(help.optrst(globalopts, ui.verbose))
3254 3259
3255 3260 if not ui.verbose:
3256 3261 if not full:
3257 3262 rst.append(_('\nuse "hg help %s" to show the full help text\n')
3258 3263 % name)
3259 3264 elif not ui.quiet:
3260 3265 rst.append(_('\nuse "hg -v help %s" to show more info\n')
3261 3266 % name)
3262 3267 return rst
3263 3268
3264 3269
3265 3270 def helplist(select=None):
3266 3271 # list of commands
3267 3272 if name == "shortlist":
3268 3273 header = _('basic commands:\n\n')
3269 3274 else:
3270 3275 header = _('list of commands:\n\n')
3271 3276
3272 3277 h = {}
3273 3278 cmds = {}
3274 3279 for c, e in table.iteritems():
3275 3280 f = c.split("|", 1)[0]
3276 3281 if select and not select(f):
3277 3282 continue
3278 3283 if (not select and name != 'shortlist' and
3279 3284 e[0].__module__ != __name__):
3280 3285 continue
3281 3286 if name == "shortlist" and not f.startswith("^"):
3282 3287 continue
3283 3288 f = f.lstrip("^")
3284 3289 if not ui.debugflag and f.startswith("debug"):
3285 3290 continue
3286 3291 doc = e[0].__doc__
3287 3292 if doc and 'DEPRECATED' in doc and not ui.verbose:
3288 3293 continue
3289 3294 doc = gettext(doc)
3290 3295 if not doc:
3291 3296 doc = _("(no help text available)")
3292 3297 h[f] = doc.splitlines()[0].rstrip()
3293 3298 cmds[f] = c.lstrip("^")
3294 3299
3295 3300 rst = []
3296 3301 if not h:
3297 3302 if not ui.quiet:
3298 3303 rst.append(_('no commands defined\n'))
3299 3304 return rst
3300 3305
3301 3306 if not ui.quiet:
3302 3307 rst.append(header)
3303 3308 fns = sorted(h)
3304 3309 for f in fns:
3305 3310 if ui.verbose:
3306 3311 commands = cmds[f].replace("|",", ")
3307 3312 rst.append(" :%s: %s\n" % (commands, h[f]))
3308 3313 else:
3309 3314 rst.append(' :%s: %s\n' % (f, h[f]))
3310 3315
3311 3316 if not name:
3312 3317 exts = help.listexts(_('enabled extensions:'), extensions.enabled())
3313 3318 if exts:
3314 3319 rst.append('\n')
3315 3320 rst.extend(exts)
3316 3321
3317 3322 rst.append(_("\nadditional help topics:\n\n"))
3318 3323 topics = []
3319 3324 for names, header, doc in help.helptable:
3320 3325 topics.append((names[0], header))
3321 3326 for t, desc in topics:
3322 3327 rst.append(" :%s: %s\n" % (t, desc))
3323 3328
3324 3329 optlist = []
3325 3330 if not ui.quiet:
3326 3331 if ui.verbose:
3327 3332 optlist.append((_("global options:"), globalopts))
3328 3333 if name == 'shortlist':
3329 3334 optlist.append((_('use "hg help" for the full list '
3330 3335 'of commands'), ()))
3331 3336 else:
3332 3337 if name == 'shortlist':
3333 3338 msg = _('use "hg help" for the full list of commands '
3334 3339 'or "hg -v" for details')
3335 3340 elif name and not full:
3336 3341 msg = _('use "hg help %s" to show the full help '
3337 3342 'text') % name
3338 3343 else:
3339 3344 msg = _('use "hg -v help%s" to show builtin aliases and '
3340 3345 'global options') % (name and " " + name or "")
3341 3346 optlist.append((msg, ()))
3342 3347
3343 3348 if optlist:
3344 3349 for title, options in optlist:
3345 3350 rst.append('\n%s\n' % title)
3346 3351 if options:
3347 3352 rst.append('\n%s\n' % help.optrst(options, ui.verbose))
3348 3353 return rst
3349 3354
3350 3355 def helptopic(name):
3351 3356 for names, header, doc in help.helptable:
3352 3357 if name in names:
3353 3358 break
3354 3359 else:
3355 3360 raise error.UnknownCommand(name)
3356 3361
3357 3362 rst = ["%s\n\n" % header]
3358 3363 # description
3359 3364 if not doc:
3360 3365 rst.append(" %s\n" % _("(no help text available)"))
3361 3366 if util.safehasattr(doc, '__call__'):
3362 3367 rst += [" %s\n" % l for l in doc().splitlines()]
3363 3368
3364 3369 try:
3365 3370 cmdutil.findcmd(name, table)
3366 3371 rst.append(_('\nuse "hg help -c %s" to see help for '
3367 3372 'the %s command\n') % (name, name))
3368 3373 except error.UnknownCommand:
3369 3374 pass
3370 3375 return rst
3371 3376
3372 3377 def helpext(name):
3373 3378 try:
3374 3379 mod = extensions.find(name)
3375 3380 doc = gettext(mod.__doc__) or _('no help text available')
3376 3381 except KeyError:
3377 3382 mod = None
3378 3383 doc = extensions.disabledext(name)
3379 3384 if not doc:
3380 3385 raise error.UnknownCommand(name)
3381 3386
3382 3387 if '\n' not in doc:
3383 3388 head, tail = doc, ""
3384 3389 else:
3385 3390 head, tail = doc.split('\n', 1)
3386 3391 rst = [_('%s extension - %s\n\n') % (name.split('.')[-1], head)]
3387 3392 if tail:
3388 3393 rst.extend(tail.splitlines(True))
3389 3394 rst.append('\n')
3390 3395
3391 3396 if mod:
3392 3397 try:
3393 3398 ct = mod.cmdtable
3394 3399 except AttributeError:
3395 3400 ct = {}
3396 3401 modcmds = set([c.split('|', 1)[0] for c in ct])
3397 3402 rst.extend(helplist(modcmds.__contains__))
3398 3403 else:
3399 3404 rst.append(_('use "hg help extensions" for information on enabling '
3400 3405 'extensions\n'))
3401 3406 return rst
3402 3407
3403 3408 def helpextcmd(name):
3404 3409 cmd, ext, mod = extensions.disabledcmd(ui, name,
3405 3410 ui.configbool('ui', 'strict'))
3406 3411 doc = gettext(mod.__doc__).splitlines()[0]
3407 3412
3408 3413 rst = help.listexts(_("'%s' is provided by the following "
3409 3414 "extension:") % cmd, {ext: doc}, indent=4)
3410 3415 rst.append('\n')
3411 3416 rst.append(_('use "hg help extensions" for information on enabling '
3412 3417 'extensions\n'))
3413 3418 return rst
3414 3419
3415 3420
3416 3421 rst = []
3417 3422 kw = opts.get('keyword')
3418 3423 if kw:
3419 3424 matches = help.topicmatch(kw)
3420 3425 for t, title in (('topics', _('Topics')),
3421 3426 ('commands', _('Commands')),
3422 3427 ('extensions', _('Extensions')),
3423 3428 ('extensioncommands', _('Extension Commands'))):
3424 3429 if matches[t]:
3425 3430 rst.append('%s:\n\n' % title)
3426 3431 rst.extend(minirst.maketable(sorted(matches[t]), 1))
3427 3432 rst.append('\n')
3428 3433 elif name and name != 'shortlist':
3429 3434 i = None
3430 3435 if unknowncmd:
3431 3436 queries = (helpextcmd,)
3432 3437 elif opts.get('extension'):
3433 3438 queries = (helpext,)
3434 3439 elif opts.get('command'):
3435 3440 queries = (helpcmd,)
3436 3441 else:
3437 3442 queries = (helptopic, helpcmd, helpext, helpextcmd)
3438 3443 for f in queries:
3439 3444 try:
3440 3445 rst = f(name)
3441 3446 i = None
3442 3447 break
3443 3448 except error.UnknownCommand, inst:
3444 3449 i = inst
3445 3450 if i:
3446 3451 raise i
3447 3452 else:
3448 3453 # program name
3449 3454 if not ui.quiet:
3450 3455 rst = [_("Mercurial Distributed SCM\n"), '\n']
3451 3456 rst.extend(helplist())
3452 3457
3453 3458 keep = ui.verbose and ['verbose'] or []
3454 3459 formatted, pruned = minirst.format(''.join(rst), textwidth, keep=keep)
3455 3460 ui.write(formatted)
3456 3461
3457 3462
3458 3463 @command('identify|id',
3459 3464 [('r', 'rev', '',
3460 3465 _('identify the specified revision'), _('REV')),
3461 3466 ('n', 'num', None, _('show local revision number')),
3462 3467 ('i', 'id', None, _('show global revision id')),
3463 3468 ('b', 'branch', None, _('show branch')),
3464 3469 ('t', 'tags', None, _('show tags')),
3465 3470 ('B', 'bookmarks', None, _('show bookmarks')),
3466 3471 ] + remoteopts,
3467 3472 _('[-nibtB] [-r REV] [SOURCE]'))
3468 3473 def identify(ui, repo, source=None, rev=None,
3469 3474 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3470 3475 """identify the working copy or specified revision
3471 3476
3472 3477 Print a summary identifying the repository state at REV using one or
3473 3478 two parent hash identifiers, followed by a "+" if the working
3474 3479 directory has uncommitted changes, the branch name (if not default),
3475 3480 a list of tags, and a list of bookmarks.
3476 3481
3477 3482 When REV is not given, print a summary of the current state of the
3478 3483 repository.
3479 3484
3480 3485 Specifying a path to a repository root or Mercurial bundle will
3481 3486 cause lookup to operate on that repository/bundle.
3482 3487
3483 3488 .. container:: verbose
3484 3489
3485 3490 Examples:
3486 3491
3487 3492 - generate a build identifier for the working directory::
3488 3493
3489 3494 hg id --id > build-id.dat
3490 3495
3491 3496 - find the revision corresponding to a tag::
3492 3497
3493 3498 hg id -n -r 1.3
3494 3499
3495 3500 - check the most recent revision of a remote repository::
3496 3501
3497 3502 hg id -r tip http://selenic.com/hg/
3498 3503
3499 3504 Returns 0 if successful.
3500 3505 """
3501 3506
3502 3507 if not repo and not source:
3503 3508 raise util.Abort(_("there is no Mercurial repository here "
3504 3509 "(.hg not found)"))
3505 3510
3506 3511 hexfunc = ui.debugflag and hex or short
3507 3512 default = not (num or id or branch or tags or bookmarks)
3508 3513 output = []
3509 3514 revs = []
3510 3515
3511 3516 if source:
3512 3517 source, branches = hg.parseurl(ui.expandpath(source))
3513 3518 peer = hg.peer(ui, opts, source)
3514 3519 repo = peer.local()
3515 3520 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3516 3521
3517 3522 if not repo:
3518 3523 if num or branch or tags:
3519 3524 raise util.Abort(
3520 3525 _("can't query remote revision number, branch, or tags"))
3521 3526 if not rev and revs:
3522 3527 rev = revs[0]
3523 3528 if not rev:
3524 3529 rev = "tip"
3525 3530
3526 3531 remoterev = peer.lookup(rev)
3527 3532 if default or id:
3528 3533 output = [hexfunc(remoterev)]
3529 3534
3530 3535 def getbms():
3531 3536 bms = []
3532 3537
3533 3538 if 'bookmarks' in peer.listkeys('namespaces'):
3534 3539 hexremoterev = hex(remoterev)
3535 3540 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3536 3541 if bmr == hexremoterev]
3537 3542
3538 3543 return bms
3539 3544
3540 3545 if bookmarks:
3541 3546 output.extend(getbms())
3542 3547 elif default and not ui.quiet:
3543 3548 # multiple bookmarks for a single parent separated by '/'
3544 3549 bm = '/'.join(getbms())
3545 3550 if bm:
3546 3551 output.append(bm)
3547 3552 else:
3548 3553 if not rev:
3549 3554 ctx = repo[None]
3550 3555 parents = ctx.parents()
3551 3556 changed = ""
3552 3557 if default or id or num:
3553 3558 if (util.any(repo.status())
3554 3559 or util.any(ctx.sub(s).dirty() for s in ctx.substate)):
3555 3560 changed = '+'
3556 3561 if default or id:
3557 3562 output = ["%s%s" %
3558 3563 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
3559 3564 if num:
3560 3565 output.append("%s%s" %
3561 3566 ('+'.join([str(p.rev()) for p in parents]), changed))
3562 3567 else:
3563 3568 ctx = scmutil.revsingle(repo, rev)
3564 3569 if default or id:
3565 3570 output = [hexfunc(ctx.node())]
3566 3571 if num:
3567 3572 output.append(str(ctx.rev()))
3568 3573
3569 3574 if default and not ui.quiet:
3570 3575 b = ctx.branch()
3571 3576 if b != 'default':
3572 3577 output.append("(%s)" % b)
3573 3578
3574 3579 # multiple tags for a single parent separated by '/'
3575 3580 t = '/'.join(ctx.tags())
3576 3581 if t:
3577 3582 output.append(t)
3578 3583
3579 3584 # multiple bookmarks for a single parent separated by '/'
3580 3585 bm = '/'.join(ctx.bookmarks())
3581 3586 if bm:
3582 3587 output.append(bm)
3583 3588 else:
3584 3589 if branch:
3585 3590 output.append(ctx.branch())
3586 3591
3587 3592 if tags:
3588 3593 output.extend(ctx.tags())
3589 3594
3590 3595 if bookmarks:
3591 3596 output.extend(ctx.bookmarks())
3592 3597
3593 3598 ui.write("%s\n" % ' '.join(output))
3594 3599
3595 3600 @command('import|patch',
3596 3601 [('p', 'strip', 1,
3597 3602 _('directory strip option for patch. This has the same '
3598 3603 'meaning as the corresponding patch option'), _('NUM')),
3599 3604 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3600 3605 ('e', 'edit', False, _('invoke editor on commit messages')),
3601 3606 ('f', 'force', None, _('skip check for outstanding uncommitted changes')),
3602 3607 ('', 'no-commit', None,
3603 3608 _("don't commit, just update the working directory")),
3604 3609 ('', 'bypass', None,
3605 3610 _("apply patch without touching the working directory")),
3606 3611 ('', 'exact', None,
3607 3612 _('apply patch to the nodes from which it was generated')),
3608 3613 ('', 'import-branch', None,
3609 3614 _('use any branch information in patch (implied by --exact)'))] +
3610 3615 commitopts + commitopts2 + similarityopts,
3611 3616 _('[OPTION]... PATCH...'))
3612 3617 def import_(ui, repo, patch1=None, *patches, **opts):
3613 3618 """import an ordered set of patches
3614 3619
3615 3620 Import a list of patches and commit them individually (unless
3616 3621 --no-commit is specified).
3617 3622
3618 3623 If there are outstanding changes in the working directory, import
3619 3624 will abort unless given the -f/--force flag.
3620 3625
3621 3626 You can import a patch straight from a mail message. Even patches
3622 3627 as attachments work (to use the body part, it must have type
3623 3628 text/plain or text/x-patch). From and Subject headers of email
3624 3629 message are used as default committer and commit message. All
3625 3630 text/plain body parts before first diff are added to commit
3626 3631 message.
3627 3632
3628 3633 If the imported patch was generated by :hg:`export`, user and
3629 3634 description from patch override values from message headers and
3630 3635 body. Values given on command line with -m/--message and -u/--user
3631 3636 override these.
3632 3637
3633 3638 If --exact is specified, import will set the working directory to
3634 3639 the parent of each patch before applying it, and will abort if the
3635 3640 resulting changeset has a different ID than the one recorded in
3636 3641 the patch. This may happen due to character set problems or other
3637 3642 deficiencies in the text patch format.
3638 3643
3639 3644 Use --bypass to apply and commit patches directly to the
3640 3645 repository, not touching the working directory. Without --exact,
3641 3646 patches will be applied on top of the working directory parent
3642 3647 revision.
3643 3648
3644 3649 With -s/--similarity, hg will attempt to discover renames and
3645 3650 copies in the patch in the same way as :hg:`addremove`.
3646 3651
3647 3652 To read a patch from standard input, use "-" as the patch name. If
3648 3653 a URL is specified, the patch will be downloaded from it.
3649 3654 See :hg:`help dates` for a list of formats valid for -d/--date.
3650 3655
3651 3656 .. container:: verbose
3652 3657
3653 3658 Examples:
3654 3659
3655 3660 - import a traditional patch from a website and detect renames::
3656 3661
3657 3662 hg import -s 80 http://example.com/bugfix.patch
3658 3663
3659 3664 - import a changeset from an hgweb server::
3660 3665
3661 3666 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
3662 3667
3663 3668 - import all the patches in an Unix-style mbox::
3664 3669
3665 3670 hg import incoming-patches.mbox
3666 3671
3667 3672 - attempt to exactly restore an exported changeset (not always
3668 3673 possible)::
3669 3674
3670 3675 hg import --exact proposed-fix.patch
3671 3676
3672 3677 Returns 0 on success.
3673 3678 """
3674 3679
3675 3680 if not patch1:
3676 3681 raise util.Abort(_('need at least one patch to import'))
3677 3682
3678 3683 patches = (patch1,) + patches
3679 3684
3680 3685 date = opts.get('date')
3681 3686 if date:
3682 3687 opts['date'] = util.parsedate(date)
3683 3688
3684 3689 editor = cmdutil.commiteditor
3685 3690 if opts.get('edit'):
3686 3691 editor = cmdutil.commitforceeditor
3687 3692
3688 3693 update = not opts.get('bypass')
3689 3694 if not update and opts.get('no_commit'):
3690 3695 raise util.Abort(_('cannot use --no-commit with --bypass'))
3691 3696 try:
3692 3697 sim = float(opts.get('similarity') or 0)
3693 3698 except ValueError:
3694 3699 raise util.Abort(_('similarity must be a number'))
3695 3700 if sim < 0 or sim > 100:
3696 3701 raise util.Abort(_('similarity must be between 0 and 100'))
3697 3702 if sim and not update:
3698 3703 raise util.Abort(_('cannot use --similarity with --bypass'))
3699 3704
3700 3705 if (opts.get('exact') or not opts.get('force')) and update:
3701 3706 cmdutil.bailifchanged(repo)
3702 3707
3703 3708 base = opts["base"]
3704 3709 strip = opts["strip"]
3705 3710 wlock = lock = tr = None
3706 3711 msgs = []
3707 3712
3708 3713 def checkexact(repo, n, nodeid):
3709 3714 if opts.get('exact') and hex(n) != nodeid:
3710 3715 repo.rollback()
3711 3716 raise util.Abort(_('patch is damaged or loses information'))
3712 3717
3713 3718 def tryone(ui, hunk, parents):
3714 3719 tmpname, message, user, date, branch, nodeid, p1, p2 = \
3715 3720 patch.extract(ui, hunk)
3716 3721
3717 3722 if not tmpname:
3718 3723 return (None, None)
3719 3724 msg = _('applied to working directory')
3720 3725
3721 3726 try:
3722 3727 cmdline_message = cmdutil.logmessage(ui, opts)
3723 3728 if cmdline_message:
3724 3729 # pickup the cmdline msg
3725 3730 message = cmdline_message
3726 3731 elif message:
3727 3732 # pickup the patch msg
3728 3733 message = message.strip()
3729 3734 else:
3730 3735 # launch the editor
3731 3736 message = None
3732 3737 ui.debug('message:\n%s\n' % message)
3733 3738
3734 3739 if len(parents) == 1:
3735 3740 parents.append(repo[nullid])
3736 3741 if opts.get('exact'):
3737 3742 if not nodeid or not p1:
3738 3743 raise util.Abort(_('not a Mercurial patch'))
3739 3744 p1 = repo[p1]
3740 3745 p2 = repo[p2 or nullid]
3741 3746 elif p2:
3742 3747 try:
3743 3748 p1 = repo[p1]
3744 3749 p2 = repo[p2]
3745 3750 # Without any options, consider p2 only if the
3746 3751 # patch is being applied on top of the recorded
3747 3752 # first parent.
3748 3753 if p1 != parents[0]:
3749 3754 p1 = parents[0]
3750 3755 p2 = repo[nullid]
3751 3756 except error.RepoError:
3752 3757 p1, p2 = parents
3753 3758 else:
3754 3759 p1, p2 = parents
3755 3760
3756 3761 n = None
3757 3762 if update:
3758 3763 if p1 != parents[0]:
3759 3764 hg.clean(repo, p1.node())
3760 3765 if p2 != parents[1]:
3761 3766 repo.setparents(p1.node(), p2.node())
3762 3767
3763 3768 if opts.get('exact') or opts.get('import_branch'):
3764 3769 repo.dirstate.setbranch(branch or 'default')
3765 3770
3766 3771 files = set()
3767 3772 patch.patch(ui, repo, tmpname, strip=strip, files=files,
3768 3773 eolmode=None, similarity=sim / 100.0)
3769 3774 files = list(files)
3770 3775 if opts.get('no_commit'):
3771 3776 if message:
3772 3777 msgs.append(message)
3773 3778 else:
3774 3779 if opts.get('exact') or p2:
3775 3780 # If you got here, you either use --force and know what
3776 3781 # you are doing or used --exact or a merge patch while
3777 3782 # being updated to its first parent.
3778 3783 m = None
3779 3784 else:
3780 3785 m = scmutil.matchfiles(repo, files or [])
3781 3786 n = repo.commit(message, opts.get('user') or user,
3782 3787 opts.get('date') or date, match=m,
3783 3788 editor=editor)
3784 3789 checkexact(repo, n, nodeid)
3785 3790 else:
3786 3791 if opts.get('exact') or opts.get('import_branch'):
3787 3792 branch = branch or 'default'
3788 3793 else:
3789 3794 branch = p1.branch()
3790 3795 store = patch.filestore()
3791 3796 try:
3792 3797 files = set()
3793 3798 try:
3794 3799 patch.patchrepo(ui, repo, p1, store, tmpname, strip,
3795 3800 files, eolmode=None)
3796 3801 except patch.PatchError, e:
3797 3802 raise util.Abort(str(e))
3798 3803 memctx = patch.makememctx(repo, (p1.node(), p2.node()),
3799 3804 message,
3800 3805 opts.get('user') or user,
3801 3806 opts.get('date') or date,
3802 3807 branch, files, store,
3803 3808 editor=cmdutil.commiteditor)
3804 3809 repo.savecommitmessage(memctx.description())
3805 3810 n = memctx.commit()
3806 3811 checkexact(repo, n, nodeid)
3807 3812 finally:
3808 3813 store.close()
3809 3814 if n:
3810 3815 # i18n: refers to a short changeset id
3811 3816 msg = _('created %s') % short(n)
3812 3817 return (msg, n)
3813 3818 finally:
3814 3819 os.unlink(tmpname)
3815 3820
3816 3821 try:
3817 3822 try:
3818 3823 wlock = repo.wlock()
3819 3824 if not opts.get('no_commit'):
3820 3825 lock = repo.lock()
3821 3826 tr = repo.transaction('import')
3822 3827 parents = repo.parents()
3823 3828 for patchurl in patches:
3824 3829 if patchurl == '-':
3825 3830 ui.status(_('applying patch from stdin\n'))
3826 3831 patchfile = ui.fin
3827 3832 patchurl = 'stdin' # for error message
3828 3833 else:
3829 3834 patchurl = os.path.join(base, patchurl)
3830 3835 ui.status(_('applying %s\n') % patchurl)
3831 3836 patchfile = url.open(ui, patchurl)
3832 3837
3833 3838 haspatch = False
3834 3839 for hunk in patch.split(patchfile):
3835 3840 (msg, node) = tryone(ui, hunk, parents)
3836 3841 if msg:
3837 3842 haspatch = True
3838 3843 ui.note(msg + '\n')
3839 3844 if update or opts.get('exact'):
3840 3845 parents = repo.parents()
3841 3846 else:
3842 3847 parents = [repo[node]]
3843 3848
3844 3849 if not haspatch:
3845 3850 raise util.Abort(_('%s: no diffs found') % patchurl)
3846 3851
3847 3852 if tr:
3848 3853 tr.close()
3849 3854 if msgs:
3850 3855 repo.savecommitmessage('\n* * *\n'.join(msgs))
3851 3856 except: # re-raises
3852 3857 # wlock.release() indirectly calls dirstate.write(): since
3853 3858 # we're crashing, we do not want to change the working dir
3854 3859 # parent after all, so make sure it writes nothing
3855 3860 repo.dirstate.invalidate()
3856 3861 raise
3857 3862 finally:
3858 3863 if tr:
3859 3864 tr.release()
3860 3865 release(lock, wlock)
3861 3866
3862 3867 @command('incoming|in',
3863 3868 [('f', 'force', None,
3864 3869 _('run even if remote repository is unrelated')),
3865 3870 ('n', 'newest-first', None, _('show newest record first')),
3866 3871 ('', 'bundle', '',
3867 3872 _('file to store the bundles into'), _('FILE')),
3868 3873 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3869 3874 ('B', 'bookmarks', False, _("compare bookmarks")),
3870 3875 ('b', 'branch', [],
3871 3876 _('a specific branch you would like to pull'), _('BRANCH')),
3872 3877 ] + logopts + remoteopts + subrepoopts,
3873 3878 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3874 3879 def incoming(ui, repo, source="default", **opts):
3875 3880 """show new changesets found in source
3876 3881
3877 3882 Show new changesets found in the specified path/URL or the default
3878 3883 pull location. These are the changesets that would have been pulled
3879 3884 if a pull at the time you issued this command.
3880 3885
3881 3886 For remote repository, using --bundle avoids downloading the
3882 3887 changesets twice if the incoming is followed by a pull.
3883 3888
3884 3889 See pull for valid source format details.
3885 3890
3886 3891 Returns 0 if there are incoming changes, 1 otherwise.
3887 3892 """
3888 3893 if opts.get('graph'):
3889 3894 cmdutil.checkunsupportedgraphflags([], opts)
3890 3895 def display(other, chlist, displayer):
3891 3896 revdag = cmdutil.graphrevs(other, chlist, opts)
3892 3897 showparents = [ctx.node() for ctx in repo[None].parents()]
3893 3898 cmdutil.displaygraph(ui, revdag, displayer, showparents,
3894 3899 graphmod.asciiedges)
3895 3900
3896 3901 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3897 3902 return 0
3898 3903
3899 3904 if opts.get('bundle') and opts.get('subrepos'):
3900 3905 raise util.Abort(_('cannot combine --bundle and --subrepos'))
3901 3906
3902 3907 if opts.get('bookmarks'):
3903 3908 source, branches = hg.parseurl(ui.expandpath(source),
3904 3909 opts.get('branch'))
3905 3910 other = hg.peer(repo, opts, source)
3906 3911 if 'bookmarks' not in other.listkeys('namespaces'):
3907 3912 ui.warn(_("remote doesn't support bookmarks\n"))
3908 3913 return 0
3909 3914 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3910 3915 return bookmarks.diff(ui, repo, other)
3911 3916
3912 3917 repo._subtoppath = ui.expandpath(source)
3913 3918 try:
3914 3919 return hg.incoming(ui, repo, source, opts)
3915 3920 finally:
3916 3921 del repo._subtoppath
3917 3922
3918 3923
3919 3924 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'))
3920 3925 def init(ui, dest=".", **opts):
3921 3926 """create a new repository in the given directory
3922 3927
3923 3928 Initialize a new repository in the given directory. If the given
3924 3929 directory does not exist, it will be created.
3925 3930
3926 3931 If no directory is given, the current directory is used.
3927 3932
3928 3933 It is possible to specify an ``ssh://`` URL as the destination.
3929 3934 See :hg:`help urls` for more information.
3930 3935
3931 3936 Returns 0 on success.
3932 3937 """
3933 3938 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3934 3939
3935 3940 @command('locate',
3936 3941 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3937 3942 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3938 3943 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3939 3944 ] + walkopts,
3940 3945 _('[OPTION]... [PATTERN]...'))
3941 3946 def locate(ui, repo, *pats, **opts):
3942 3947 """locate files matching specific patterns
3943 3948
3944 3949 Print files under Mercurial control in the working directory whose
3945 3950 names match the given patterns.
3946 3951
3947 3952 By default, this command searches all directories in the working
3948 3953 directory. To search just the current directory and its
3949 3954 subdirectories, use "--include .".
3950 3955
3951 3956 If no patterns are given to match, this command prints the names
3952 3957 of all files under Mercurial control in the working directory.
3953 3958
3954 3959 If you want to feed the output of this command into the "xargs"
3955 3960 command, use the -0 option to both this command and "xargs". This
3956 3961 will avoid the problem of "xargs" treating single filenames that
3957 3962 contain whitespace as multiple filenames.
3958 3963
3959 3964 Returns 0 if a match is found, 1 otherwise.
3960 3965 """
3961 3966 end = opts.get('print0') and '\0' or '\n'
3962 3967 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3963 3968
3964 3969 ret = 1
3965 3970 m = scmutil.match(repo[rev], pats, opts, default='relglob')
3966 3971 m.bad = lambda x, y: False
3967 3972 for abs in repo[rev].walk(m):
3968 3973 if not rev and abs not in repo.dirstate:
3969 3974 continue
3970 3975 if opts.get('fullpath'):
3971 3976 ui.write(repo.wjoin(abs), end)
3972 3977 else:
3973 3978 ui.write(((pats and m.rel(abs)) or abs), end)
3974 3979 ret = 0
3975 3980
3976 3981 return ret
3977 3982
3978 3983 @command('^log|history',
3979 3984 [('f', 'follow', None,
3980 3985 _('follow changeset history, or file history across copies and renames')),
3981 3986 ('', 'follow-first', None,
3982 3987 _('only follow the first parent of merge changesets (DEPRECATED)')),
3983 3988 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3984 3989 ('C', 'copies', None, _('show copied files')),
3985 3990 ('k', 'keyword', [],
3986 3991 _('do case-insensitive search for a given text'), _('TEXT')),
3987 3992 ('r', 'rev', [], _('show the specified revision or range'), _('REV')),
3988 3993 ('', 'removed', None, _('include revisions where files were removed')),
3989 3994 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3990 3995 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3991 3996 ('', 'only-branch', [],
3992 3997 _('show only changesets within the given named branch (DEPRECATED)'),
3993 3998 _('BRANCH')),
3994 3999 ('b', 'branch', [],
3995 4000 _('show changesets within the given named branch'), _('BRANCH')),
3996 4001 ('P', 'prune', [],
3997 4002 _('do not display revision or any of its ancestors'), _('REV')),
3998 4003 ('', 'hidden', False, _('show hidden changesets (DEPRECATED)')),
3999 4004 ] + logopts + walkopts,
4000 4005 _('[OPTION]... [FILE]'))
4001 4006 def log(ui, repo, *pats, **opts):
4002 4007 """show revision history of entire repository or files
4003 4008
4004 4009 Print the revision history of the specified files or the entire
4005 4010 project.
4006 4011
4007 4012 If no revision range is specified, the default is ``tip:0`` unless
4008 4013 --follow is set, in which case the working directory parent is
4009 4014 used as the starting revision.
4010 4015
4011 4016 File history is shown without following rename or copy history of
4012 4017 files. Use -f/--follow with a filename to follow history across
4013 4018 renames and copies. --follow without a filename will only show
4014 4019 ancestors or descendants of the starting revision.
4015 4020
4016 4021 By default this command prints revision number and changeset id,
4017 4022 tags, non-trivial parents, user, date and time, and a summary for
4018 4023 each commit. When the -v/--verbose switch is used, the list of
4019 4024 changed files and full commit message are shown.
4020 4025
4021 4026 .. note::
4022 4027 log -p/--patch may generate unexpected diff output for merge
4023 4028 changesets, as it will only compare the merge changeset against
4024 4029 its first parent. Also, only files different from BOTH parents
4025 4030 will appear in files:.
4026 4031
4027 4032 .. note::
4028 4033 for performance reasons, log FILE may omit duplicate changes
4029 4034 made on branches and will not show deletions. To see all
4030 4035 changes including duplicates and deletions, use the --removed
4031 4036 switch.
4032 4037
4033 4038 .. container:: verbose
4034 4039
4035 4040 Some examples:
4036 4041
4037 4042 - changesets with full descriptions and file lists::
4038 4043
4039 4044 hg log -v
4040 4045
4041 4046 - changesets ancestral to the working directory::
4042 4047
4043 4048 hg log -f
4044 4049
4045 4050 - last 10 commits on the current branch::
4046 4051
4047 4052 hg log -l 10 -b .
4048 4053
4049 4054 - changesets showing all modifications of a file, including removals::
4050 4055
4051 4056 hg log --removed file.c
4052 4057
4053 4058 - all changesets that touch a directory, with diffs, excluding merges::
4054 4059
4055 4060 hg log -Mp lib/
4056 4061
4057 4062 - all revision numbers that match a keyword::
4058 4063
4059 4064 hg log -k bug --template "{rev}\\n"
4060 4065
4061 4066 - check if a given changeset is included is a tagged release::
4062 4067
4063 4068 hg log -r "a21ccf and ancestor(1.9)"
4064 4069
4065 4070 - find all changesets by some user in a date range::
4066 4071
4067 4072 hg log -k alice -d "may 2008 to jul 2008"
4068 4073
4069 4074 - summary of all changesets after the last tag::
4070 4075
4071 4076 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4072 4077
4073 4078 See :hg:`help dates` for a list of formats valid for -d/--date.
4074 4079
4075 4080 See :hg:`help revisions` and :hg:`help revsets` for more about
4076 4081 specifying revisions.
4077 4082
4078 4083 See :hg:`help templates` for more about pre-packaged styles and
4079 4084 specifying custom templates.
4080 4085
4081 4086 Returns 0 on success.
4082 4087 """
4083 4088 if opts.get('graph'):
4084 4089 return cmdutil.graphlog(ui, repo, *pats, **opts)
4085 4090
4086 4091 matchfn = scmutil.match(repo[None], pats, opts)
4087 4092 limit = cmdutil.loglimit(opts)
4088 4093 count = 0
4089 4094
4090 4095 getrenamed, endrev = None, None
4091 4096 if opts.get('copies'):
4092 4097 if opts.get('rev'):
4093 4098 endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1
4094 4099 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
4095 4100
4096 4101 df = False
4097 4102 if opts.get("date"):
4098 4103 df = util.matchdate(opts["date"])
4099 4104
4100 4105 branches = opts.get('branch', []) + opts.get('only_branch', [])
4101 4106 opts['branch'] = [repo.lookupbranch(b) for b in branches]
4102 4107
4103 4108 displayer = cmdutil.show_changeset(ui, repo, opts, True)
4104 4109 def prep(ctx, fns):
4105 4110 rev = ctx.rev()
4106 4111 parents = [p for p in repo.changelog.parentrevs(rev)
4107 4112 if p != nullrev]
4108 4113 if opts.get('no_merges') and len(parents) == 2:
4109 4114 return
4110 4115 if opts.get('only_merges') and len(parents) != 2:
4111 4116 return
4112 4117 if opts.get('branch') and ctx.branch() not in opts['branch']:
4113 4118 return
4114 4119 if not opts.get('hidden') and ctx.hidden():
4115 4120 return
4116 4121 if df and not df(ctx.date()[0]):
4117 4122 return
4118 4123
4119 4124 lower = encoding.lower
4120 4125 if opts.get('user'):
4121 4126 luser = lower(ctx.user())
4122 4127 for k in [lower(x) for x in opts['user']]:
4123 4128 if (k in luser):
4124 4129 break
4125 4130 else:
4126 4131 return
4127 4132 if opts.get('keyword'):
4128 4133 luser = lower(ctx.user())
4129 4134 ldesc = lower(ctx.description())
4130 4135 lfiles = lower(" ".join(ctx.files()))
4131 4136 for k in [lower(x) for x in opts['keyword']]:
4132 4137 if (k in luser or k in ldesc or k in lfiles):
4133 4138 break
4134 4139 else:
4135 4140 return
4136 4141
4137 4142 copies = None
4138 4143 if getrenamed is not None and rev:
4139 4144 copies = []
4140 4145 for fn in ctx.files():
4141 4146 rename = getrenamed(fn, rev)
4142 4147 if rename:
4143 4148 copies.append((fn, rename[0]))
4144 4149
4145 4150 revmatchfn = None
4146 4151 if opts.get('patch') or opts.get('stat'):
4147 4152 if opts.get('follow') or opts.get('follow_first'):
4148 4153 # note: this might be wrong when following through merges
4149 4154 revmatchfn = scmutil.match(repo[None], fns, default='path')
4150 4155 else:
4151 4156 revmatchfn = matchfn
4152 4157
4153 4158 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
4154 4159
4155 4160 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
4156 4161 if count == limit:
4157 4162 break
4158 4163 if displayer.flush(ctx.rev()):
4159 4164 count += 1
4160 4165 displayer.close()
4161 4166
4162 4167 @command('manifest',
4163 4168 [('r', 'rev', '', _('revision to display'), _('REV')),
4164 4169 ('', 'all', False, _("list files from all revisions"))],
4165 4170 _('[-r REV]'))
4166 4171 def manifest(ui, repo, node=None, rev=None, **opts):
4167 4172 """output the current or given revision of the project manifest
4168 4173
4169 4174 Print a list of version controlled files for the given revision.
4170 4175 If no revision is given, the first parent of the working directory
4171 4176 is used, or the null revision if no revision is checked out.
4172 4177
4173 4178 With -v, print file permissions, symlink and executable bits.
4174 4179 With --debug, print file revision hashes.
4175 4180
4176 4181 If option --all is specified, the list of all files from all revisions
4177 4182 is printed. This includes deleted and renamed files.
4178 4183
4179 4184 Returns 0 on success.
4180 4185 """
4181 4186 if opts.get('all'):
4182 4187 if rev or node:
4183 4188 raise util.Abort(_("can't specify a revision with --all"))
4184 4189
4185 4190 res = []
4186 4191 prefix = "data/"
4187 4192 suffix = ".i"
4188 4193 plen = len(prefix)
4189 4194 slen = len(suffix)
4190 4195 lock = repo.lock()
4191 4196 try:
4192 4197 for fn, b, size in repo.store.datafiles():
4193 4198 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
4194 4199 res.append(fn[plen:-slen])
4195 4200 finally:
4196 4201 lock.release()
4197 4202 for f in res:
4198 4203 ui.write("%s\n" % f)
4199 4204 return
4200 4205
4201 4206 if rev and node:
4202 4207 raise util.Abort(_("please specify just one revision"))
4203 4208
4204 4209 if not node:
4205 4210 node = rev
4206 4211
4207 4212 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
4208 4213 ctx = scmutil.revsingle(repo, node)
4209 4214 for f in ctx:
4210 4215 if ui.debugflag:
4211 4216 ui.write("%40s " % hex(ctx.manifest()[f]))
4212 4217 if ui.verbose:
4213 4218 ui.write(decor[ctx.flags(f)])
4214 4219 ui.write("%s\n" % f)
4215 4220
4216 4221 @command('^merge',
4217 4222 [('f', 'force', None, _('force a merge with outstanding changes')),
4218 4223 ('r', 'rev', '', _('revision to merge'), _('REV')),
4219 4224 ('P', 'preview', None,
4220 4225 _('review revisions to merge (no merge is performed)'))
4221 4226 ] + mergetoolopts,
4222 4227 _('[-P] [-f] [[-r] REV]'))
4223 4228 def merge(ui, repo, node=None, **opts):
4224 4229 """merge working directory with another revision
4225 4230
4226 4231 The current working directory is updated with all changes made in
4227 4232 the requested revision since the last common predecessor revision.
4228 4233
4229 4234 Files that changed between either parent are marked as changed for
4230 4235 the next commit and a commit must be performed before any further
4231 4236 updates to the repository are allowed. The next commit will have
4232 4237 two parents.
4233 4238
4234 4239 ``--tool`` can be used to specify the merge tool used for file
4235 4240 merges. It overrides the HGMERGE environment variable and your
4236 4241 configuration files. See :hg:`help merge-tools` for options.
4237 4242
4238 4243 If no revision is specified, the working directory's parent is a
4239 4244 head revision, and the current branch contains exactly one other
4240 4245 head, the other head is merged with by default. Otherwise, an
4241 4246 explicit revision with which to merge with must be provided.
4242 4247
4243 4248 :hg:`resolve` must be used to resolve unresolved files.
4244 4249
4245 4250 To undo an uncommitted merge, use :hg:`update --clean .` which
4246 4251 will check out a clean copy of the original merge parent, losing
4247 4252 all changes.
4248 4253
4249 4254 Returns 0 on success, 1 if there are unresolved files.
4250 4255 """
4251 4256
4252 4257 if opts.get('rev') and node:
4253 4258 raise util.Abort(_("please specify just one revision"))
4254 4259 if not node:
4255 4260 node = opts.get('rev')
4256 4261
4257 4262 if node:
4258 4263 node = scmutil.revsingle(repo, node).node()
4259 4264
4260 4265 if not node and repo._bookmarkcurrent:
4261 4266 bmheads = repo.bookmarkheads(repo._bookmarkcurrent)
4262 4267 curhead = repo[repo._bookmarkcurrent]
4263 4268 if len(bmheads) == 2:
4264 4269 if curhead == bmheads[0]:
4265 4270 node = bmheads[1]
4266 4271 else:
4267 4272 node = bmheads[0]
4268 4273 elif len(bmheads) > 2:
4269 4274 raise util.Abort(_("multiple matching bookmarks to merge - "
4270 4275 "please merge with an explicit rev or bookmark"),
4271 4276 hint=_("run 'hg heads' to see all heads"))
4272 4277 elif len(bmheads) <= 1:
4273 4278 raise util.Abort(_("no matching bookmark to merge - "
4274 4279 "please merge with an explicit rev or bookmark"),
4275 4280 hint=_("run 'hg heads' to see all heads"))
4276 4281
4277 4282 if not node and not repo._bookmarkcurrent:
4278 4283 branch = repo[None].branch()
4279 4284 bheads = repo.branchheads(branch)
4280 4285 nbhs = [bh for bh in bheads if not repo[bh].bookmarks()]
4281 4286
4282 4287 if len(nbhs) > 2:
4283 4288 raise util.Abort(_("branch '%s' has %d heads - "
4284 4289 "please merge with an explicit rev")
4285 4290 % (branch, len(bheads)),
4286 4291 hint=_("run 'hg heads .' to see heads"))
4287 4292
4288 4293 parent = repo.dirstate.p1()
4289 4294 if len(nbhs) <= 1:
4290 4295 if len(bheads) > 1:
4291 4296 raise util.Abort(_("heads are bookmarked - "
4292 4297 "please merge with an explicit rev"),
4293 4298 hint=_("run 'hg heads' to see all heads"))
4294 4299 if len(repo.heads()) > 1:
4295 4300 raise util.Abort(_("branch '%s' has one head - "
4296 4301 "please merge with an explicit rev")
4297 4302 % branch,
4298 4303 hint=_("run 'hg heads' to see all heads"))
4299 4304 msg, hint = _('nothing to merge'), None
4300 4305 if parent != repo.lookup(branch):
4301 4306 hint = _("use 'hg update' instead")
4302 4307 raise util.Abort(msg, hint=hint)
4303 4308
4304 4309 if parent not in bheads:
4305 4310 raise util.Abort(_('working directory not at a head revision'),
4306 4311 hint=_("use 'hg update' or merge with an "
4307 4312 "explicit revision"))
4308 4313 if parent == nbhs[0]:
4309 4314 node = nbhs[-1]
4310 4315 else:
4311 4316 node = nbhs[0]
4312 4317
4313 4318 if opts.get('preview'):
4314 4319 # find nodes that are ancestors of p2 but not of p1
4315 4320 p1 = repo.lookup('.')
4316 4321 p2 = repo.lookup(node)
4317 4322 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4318 4323
4319 4324 displayer = cmdutil.show_changeset(ui, repo, opts)
4320 4325 for node in nodes:
4321 4326 displayer.show(repo[node])
4322 4327 displayer.close()
4323 4328 return 0
4324 4329
4325 4330 try:
4326 4331 # ui.forcemerge is an internal variable, do not document
4327 4332 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4328 4333 return hg.merge(repo, node, force=opts.get('force'))
4329 4334 finally:
4330 4335 ui.setconfig('ui', 'forcemerge', '')
4331 4336
4332 4337 @command('outgoing|out',
4333 4338 [('f', 'force', None, _('run even when the destination is unrelated')),
4334 4339 ('r', 'rev', [],
4335 4340 _('a changeset intended to be included in the destination'), _('REV')),
4336 4341 ('n', 'newest-first', None, _('show newest record first')),
4337 4342 ('B', 'bookmarks', False, _('compare bookmarks')),
4338 4343 ('b', 'branch', [], _('a specific branch you would like to push'),
4339 4344 _('BRANCH')),
4340 4345 ] + logopts + remoteopts + subrepoopts,
4341 4346 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4342 4347 def outgoing(ui, repo, dest=None, **opts):
4343 4348 """show changesets not found in the destination
4344 4349
4345 4350 Show changesets not found in the specified destination repository
4346 4351 or the default push location. These are the changesets that would
4347 4352 be pushed if a push was requested.
4348 4353
4349 4354 See pull for details of valid destination formats.
4350 4355
4351 4356 Returns 0 if there are outgoing changes, 1 otherwise.
4352 4357 """
4353 4358 if opts.get('graph'):
4354 4359 cmdutil.checkunsupportedgraphflags([], opts)
4355 4360 o = hg._outgoing(ui, repo, dest, opts)
4356 4361 if o is None:
4357 4362 return
4358 4363
4359 4364 revdag = cmdutil.graphrevs(repo, o, opts)
4360 4365 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4361 4366 showparents = [ctx.node() for ctx in repo[None].parents()]
4362 4367 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4363 4368 graphmod.asciiedges)
4364 4369 return 0
4365 4370
4366 4371 if opts.get('bookmarks'):
4367 4372 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4368 4373 dest, branches = hg.parseurl(dest, opts.get('branch'))
4369 4374 other = hg.peer(repo, opts, dest)
4370 4375 if 'bookmarks' not in other.listkeys('namespaces'):
4371 4376 ui.warn(_("remote doesn't support bookmarks\n"))
4372 4377 return 0
4373 4378 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4374 4379 return bookmarks.diff(ui, other, repo)
4375 4380
4376 4381 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4377 4382 try:
4378 4383 return hg.outgoing(ui, repo, dest, opts)
4379 4384 finally:
4380 4385 del repo._subtoppath
4381 4386
4382 4387 @command('parents',
4383 4388 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4384 4389 ] + templateopts,
4385 4390 _('[-r REV] [FILE]'))
4386 4391 def parents(ui, repo, file_=None, **opts):
4387 4392 """show the parents of the working directory or revision
4388 4393
4389 4394 Print the working directory's parent revisions. If a revision is
4390 4395 given via -r/--rev, the parent of that revision will be printed.
4391 4396 If a file argument is given, the revision in which the file was
4392 4397 last changed (before the working directory revision or the
4393 4398 argument to --rev if given) is printed.
4394 4399
4395 4400 Returns 0 on success.
4396 4401 """
4397 4402
4398 4403 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4399 4404
4400 4405 if file_:
4401 4406 m = scmutil.match(ctx, (file_,), opts)
4402 4407 if m.anypats() or len(m.files()) != 1:
4403 4408 raise util.Abort(_('can only specify an explicit filename'))
4404 4409 file_ = m.files()[0]
4405 4410 filenodes = []
4406 4411 for cp in ctx.parents():
4407 4412 if not cp:
4408 4413 continue
4409 4414 try:
4410 4415 filenodes.append(cp.filenode(file_))
4411 4416 except error.LookupError:
4412 4417 pass
4413 4418 if not filenodes:
4414 4419 raise util.Abort(_("'%s' not found in manifest!") % file_)
4415 4420 fl = repo.file(file_)
4416 4421 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
4417 4422 else:
4418 4423 p = [cp.node() for cp in ctx.parents()]
4419 4424
4420 4425 displayer = cmdutil.show_changeset(ui, repo, opts)
4421 4426 for n in p:
4422 4427 if n != nullid:
4423 4428 displayer.show(repo[n])
4424 4429 displayer.close()
4425 4430
4426 4431 @command('paths', [], _('[NAME]'))
4427 4432 def paths(ui, repo, search=None):
4428 4433 """show aliases for remote repositories
4429 4434
4430 4435 Show definition of symbolic path name NAME. If no name is given,
4431 4436 show definition of all available names.
4432 4437
4433 4438 Option -q/--quiet suppresses all output when searching for NAME
4434 4439 and shows only the path names when listing all definitions.
4435 4440
4436 4441 Path names are defined in the [paths] section of your
4437 4442 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4438 4443 repository, ``.hg/hgrc`` is used, too.
4439 4444
4440 4445 The path names ``default`` and ``default-push`` have a special
4441 4446 meaning. When performing a push or pull operation, they are used
4442 4447 as fallbacks if no location is specified on the command-line.
4443 4448 When ``default-push`` is set, it will be used for push and
4444 4449 ``default`` will be used for pull; otherwise ``default`` is used
4445 4450 as the fallback for both. When cloning a repository, the clone
4446 4451 source is written as ``default`` in ``.hg/hgrc``. Note that
4447 4452 ``default`` and ``default-push`` apply to all inbound (e.g.
4448 4453 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
4449 4454 :hg:`bundle`) operations.
4450 4455
4451 4456 See :hg:`help urls` for more information.
4452 4457
4453 4458 Returns 0 on success.
4454 4459 """
4455 4460 if search:
4456 4461 for name, path in ui.configitems("paths"):
4457 4462 if name == search:
4458 4463 ui.status("%s\n" % util.hidepassword(path))
4459 4464 return
4460 4465 if not ui.quiet:
4461 4466 ui.warn(_("not found!\n"))
4462 4467 return 1
4463 4468 else:
4464 4469 for name, path in ui.configitems("paths"):
4465 4470 if ui.quiet:
4466 4471 ui.write("%s\n" % name)
4467 4472 else:
4468 4473 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
4469 4474
4470 4475 @command('^phase',
4471 4476 [('p', 'public', False, _('set changeset phase to public')),
4472 4477 ('d', 'draft', False, _('set changeset phase to draft')),
4473 4478 ('s', 'secret', False, _('set changeset phase to secret')),
4474 4479 ('f', 'force', False, _('allow to move boundary backward')),
4475 4480 ('r', 'rev', [], _('target revision'), _('REV')),
4476 4481 ],
4477 4482 _('[-p|-d|-s] [-f] [-r] REV...'))
4478 4483 def phase(ui, repo, *revs, **opts):
4479 4484 """set or show the current phase name
4480 4485
4481 4486 With no argument, show the phase name of specified revisions.
4482 4487
4483 4488 With one of -p/--public, -d/--draft or -s/--secret, change the
4484 4489 phase value of the specified revisions.
4485 4490
4486 4491 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
4487 4492 lower phase to an higher phase. Phases are ordered as follows::
4488 4493
4489 4494 public < draft < secret
4490 4495
4491 4496 Return 0 on success, 1 if no phases were changed or some could not
4492 4497 be changed.
4493 4498 """
4494 4499 # search for a unique phase argument
4495 4500 targetphase = None
4496 4501 for idx, name in enumerate(phases.phasenames):
4497 4502 if opts[name]:
4498 4503 if targetphase is not None:
4499 4504 raise util.Abort(_('only one phase can be specified'))
4500 4505 targetphase = idx
4501 4506
4502 4507 # look for specified revision
4503 4508 revs = list(revs)
4504 4509 revs.extend(opts['rev'])
4505 4510 if not revs:
4506 4511 raise util.Abort(_('no revisions specified'))
4507 4512
4508 4513 revs = scmutil.revrange(repo, revs)
4509 4514
4510 4515 lock = None
4511 4516 ret = 0
4512 4517 if targetphase is None:
4513 4518 # display
4514 4519 for r in revs:
4515 4520 ctx = repo[r]
4516 4521 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4517 4522 else:
4518 4523 lock = repo.lock()
4519 4524 try:
4520 4525 # set phase
4521 4526 if not revs:
4522 4527 raise util.Abort(_('empty revision set'))
4523 4528 nodes = [repo[r].node() for r in revs]
4524 4529 olddata = repo._phasecache.getphaserevs(repo)[:]
4525 4530 phases.advanceboundary(repo, targetphase, nodes)
4526 4531 if opts['force']:
4527 4532 phases.retractboundary(repo, targetphase, nodes)
4528 4533 finally:
4529 4534 lock.release()
4530 4535 newdata = repo._phasecache.getphaserevs(repo)
4531 4536 changes = sum(o != newdata[i] for i, o in enumerate(olddata))
4532 4537 rejected = [n for n in nodes
4533 4538 if newdata[repo[n].rev()] < targetphase]
4534 4539 if rejected:
4535 4540 ui.warn(_('cannot move %i changesets to a more permissive '
4536 4541 'phase, use --force\n') % len(rejected))
4537 4542 ret = 1
4538 4543 if changes:
4539 4544 msg = _('phase changed for %i changesets\n') % changes
4540 4545 if ret:
4541 4546 ui.status(msg)
4542 4547 else:
4543 4548 ui.note(msg)
4544 4549 else:
4545 4550 ui.warn(_('no phases changed\n'))
4546 4551 ret = 1
4547 4552 return ret
4548 4553
4549 4554 def postincoming(ui, repo, modheads, optupdate, checkout):
4550 4555 if modheads == 0:
4551 4556 return
4552 4557 if optupdate:
4553 4558 movemarkfrom = repo['.'].node()
4554 4559 try:
4555 4560 ret = hg.update(repo, checkout)
4556 4561 except util.Abort, inst:
4557 4562 ui.warn(_("not updating: %s\n") % str(inst))
4558 4563 return 0
4559 4564 if not ret and not checkout:
4560 4565 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
4561 4566 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
4562 4567 return ret
4563 4568 if modheads > 1:
4564 4569 currentbranchheads = len(repo.branchheads())
4565 4570 if currentbranchheads == modheads:
4566 4571 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4567 4572 elif currentbranchheads > 1:
4568 4573 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4569 4574 "merge)\n"))
4570 4575 else:
4571 4576 ui.status(_("(run 'hg heads' to see heads)\n"))
4572 4577 else:
4573 4578 ui.status(_("(run 'hg update' to get a working copy)\n"))
4574 4579
4575 4580 @command('^pull',
4576 4581 [('u', 'update', None,
4577 4582 _('update to new branch head if changesets were pulled')),
4578 4583 ('f', 'force', None, _('run even when remote repository is unrelated')),
4579 4584 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4580 4585 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4581 4586 ('b', 'branch', [], _('a specific branch you would like to pull'),
4582 4587 _('BRANCH')),
4583 4588 ] + remoteopts,
4584 4589 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
4585 4590 def pull(ui, repo, source="default", **opts):
4586 4591 """pull changes from the specified source
4587 4592
4588 4593 Pull changes from a remote repository to a local one.
4589 4594
4590 4595 This finds all changes from the repository at the specified path
4591 4596 or URL and adds them to a local repository (the current one unless
4592 4597 -R is specified). By default, this does not update the copy of the
4593 4598 project in the working directory.
4594 4599
4595 4600 Use :hg:`incoming` if you want to see what would have been added
4596 4601 by a pull at the time you issued this command. If you then decide
4597 4602 to add those changes to the repository, you should use :hg:`pull
4598 4603 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4599 4604
4600 4605 If SOURCE is omitted, the 'default' path will be used.
4601 4606 See :hg:`help urls` for more information.
4602 4607
4603 4608 Returns 0 on success, 1 if an update had unresolved files.
4604 4609 """
4605 4610 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4606 4611 other = hg.peer(repo, opts, source)
4607 4612 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4608 4613 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
4609 4614
4610 4615 if opts.get('bookmark'):
4611 4616 if not revs:
4612 4617 revs = []
4613 4618 rb = other.listkeys('bookmarks')
4614 4619 for b in opts['bookmark']:
4615 4620 if b not in rb:
4616 4621 raise util.Abort(_('remote bookmark %s not found!') % b)
4617 4622 revs.append(rb[b])
4618 4623
4619 4624 if revs:
4620 4625 try:
4621 4626 revs = [other.lookup(rev) for rev in revs]
4622 4627 except error.CapabilityError:
4623 4628 err = _("other repository doesn't support revision lookup, "
4624 4629 "so a rev cannot be specified.")
4625 4630 raise util.Abort(err)
4626 4631
4627 4632 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
4628 4633 bookmarks.updatefromremote(ui, repo, other, source)
4629 4634 if checkout:
4630 4635 checkout = str(repo.changelog.rev(other.lookup(checkout)))
4631 4636 repo._subtoppath = source
4632 4637 try:
4633 4638 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
4634 4639
4635 4640 finally:
4636 4641 del repo._subtoppath
4637 4642
4638 4643 # update specified bookmarks
4639 4644 if opts.get('bookmark'):
4640 4645 for b in opts['bookmark']:
4641 4646 # explicit pull overrides local bookmark if any
4642 4647 ui.status(_("importing bookmark %s\n") % b)
4643 4648 repo._bookmarks[b] = repo[rb[b]].node()
4644 4649 bookmarks.write(repo)
4645 4650
4646 4651 return ret
4647 4652
4648 4653 @command('^push',
4649 4654 [('f', 'force', None, _('force push')),
4650 4655 ('r', 'rev', [],
4651 4656 _('a changeset intended to be included in the destination'),
4652 4657 _('REV')),
4653 4658 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4654 4659 ('b', 'branch', [],
4655 4660 _('a specific branch you would like to push'), _('BRANCH')),
4656 4661 ('', 'new-branch', False, _('allow pushing a new branch')),
4657 4662 ] + remoteopts,
4658 4663 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4659 4664 def push(ui, repo, dest=None, **opts):
4660 4665 """push changes to the specified destination
4661 4666
4662 4667 Push changesets from the local repository to the specified
4663 4668 destination.
4664 4669
4665 4670 This operation is symmetrical to pull: it is identical to a pull
4666 4671 in the destination repository from the current one.
4667 4672
4668 4673 By default, push will not allow creation of new heads at the
4669 4674 destination, since multiple heads would make it unclear which head
4670 4675 to use. In this situation, it is recommended to pull and merge
4671 4676 before pushing.
4672 4677
4673 4678 Use --new-branch if you want to allow push to create a new named
4674 4679 branch that is not present at the destination. This allows you to
4675 4680 only create a new branch without forcing other changes.
4676 4681
4677 4682 Use -f/--force to override the default behavior and push all
4678 4683 changesets on all branches.
4679 4684
4680 4685 If -r/--rev is used, the specified revision and all its ancestors
4681 4686 will be pushed to the remote repository.
4682 4687
4683 4688 If -B/--bookmark is used, the specified bookmarked revision, its
4684 4689 ancestors, and the bookmark will be pushed to the remote
4685 4690 repository.
4686 4691
4687 4692 Please see :hg:`help urls` for important details about ``ssh://``
4688 4693 URLs. If DESTINATION is omitted, a default path will be used.
4689 4694
4690 4695 Returns 0 if push was successful, 1 if nothing to push.
4691 4696 """
4692 4697
4693 4698 if opts.get('bookmark'):
4694 4699 for b in opts['bookmark']:
4695 4700 # translate -B options to -r so changesets get pushed
4696 4701 if b in repo._bookmarks:
4697 4702 opts.setdefault('rev', []).append(b)
4698 4703 else:
4699 4704 # if we try to push a deleted bookmark, translate it to null
4700 4705 # this lets simultaneous -r, -b options continue working
4701 4706 opts.setdefault('rev', []).append("null")
4702 4707
4703 4708 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4704 4709 dest, branches = hg.parseurl(dest, opts.get('branch'))
4705 4710 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4706 4711 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4707 4712 other = hg.peer(repo, opts, dest)
4708 4713 if revs:
4709 4714 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
4710 4715
4711 4716 repo._subtoppath = dest
4712 4717 try:
4713 4718 # push subrepos depth-first for coherent ordering
4714 4719 c = repo['']
4715 4720 subs = c.substate # only repos that are committed
4716 4721 for s in sorted(subs):
4717 4722 if c.sub(s).push(opts) == 0:
4718 4723 return False
4719 4724 finally:
4720 4725 del repo._subtoppath
4721 4726 result = repo.push(other, opts.get('force'), revs=revs,
4722 4727 newbranch=opts.get('new_branch'))
4723 4728
4724 4729 result = not result
4725 4730
4726 4731 if opts.get('bookmark'):
4727 4732 rb = other.listkeys('bookmarks')
4728 4733 for b in opts['bookmark']:
4729 4734 # explicit push overrides remote bookmark if any
4730 4735 if b in repo._bookmarks:
4731 4736 ui.status(_("exporting bookmark %s\n") % b)
4732 4737 new = repo[b].hex()
4733 4738 elif b in rb:
4734 4739 ui.status(_("deleting remote bookmark %s\n") % b)
4735 4740 new = '' # delete
4736 4741 else:
4737 4742 ui.warn(_('bookmark %s does not exist on the local '
4738 4743 'or remote repository!\n') % b)
4739 4744 return 2
4740 4745 old = rb.get(b, '')
4741 4746 r = other.pushkey('bookmarks', b, old, new)
4742 4747 if not r:
4743 4748 ui.warn(_('updating bookmark %s failed!\n') % b)
4744 4749 if not result:
4745 4750 result = 2
4746 4751
4747 4752 return result
4748 4753
4749 4754 @command('recover', [])
4750 4755 def recover(ui, repo):
4751 4756 """roll back an interrupted transaction
4752 4757
4753 4758 Recover from an interrupted commit or pull.
4754 4759
4755 4760 This command tries to fix the repository status after an
4756 4761 interrupted operation. It should only be necessary when Mercurial
4757 4762 suggests it.
4758 4763
4759 4764 Returns 0 if successful, 1 if nothing to recover or verify fails.
4760 4765 """
4761 4766 if repo.recover():
4762 4767 return hg.verify(repo)
4763 4768 return 1
4764 4769
4765 4770 @command('^remove|rm',
4766 4771 [('A', 'after', None, _('record delete for missing files')),
4767 4772 ('f', 'force', None,
4768 4773 _('remove (and delete) file even if added or modified')),
4769 4774 ] + walkopts,
4770 4775 _('[OPTION]... FILE...'))
4771 4776 def remove(ui, repo, *pats, **opts):
4772 4777 """remove the specified files on the next commit
4773 4778
4774 4779 Schedule the indicated files for removal from the current branch.
4775 4780
4776 4781 This command schedules the files to be removed at the next commit.
4777 4782 To undo a remove before that, see :hg:`revert`. To undo added
4778 4783 files, see :hg:`forget`.
4779 4784
4780 4785 .. container:: verbose
4781 4786
4782 4787 -A/--after can be used to remove only files that have already
4783 4788 been deleted, -f/--force can be used to force deletion, and -Af
4784 4789 can be used to remove files from the next revision without
4785 4790 deleting them from the working directory.
4786 4791
4787 4792 The following table details the behavior of remove for different
4788 4793 file states (columns) and option combinations (rows). The file
4789 4794 states are Added [A], Clean [C], Modified [M] and Missing [!]
4790 4795 (as reported by :hg:`status`). The actions are Warn, Remove
4791 4796 (from branch) and Delete (from disk):
4792 4797
4793 4798 ======= == == == ==
4794 4799 A C M !
4795 4800 ======= == == == ==
4796 4801 none W RD W R
4797 4802 -f R RD RD R
4798 4803 -A W W W R
4799 4804 -Af R R R R
4800 4805 ======= == == == ==
4801 4806
4802 4807 Note that remove never deletes files in Added [A] state from the
4803 4808 working directory, not even if option --force is specified.
4804 4809
4805 4810 Returns 0 on success, 1 if any warnings encountered.
4806 4811 """
4807 4812
4808 4813 ret = 0
4809 4814 after, force = opts.get('after'), opts.get('force')
4810 4815 if not pats and not after:
4811 4816 raise util.Abort(_('no files specified'))
4812 4817
4813 4818 m = scmutil.match(repo[None], pats, opts)
4814 4819 s = repo.status(match=m, clean=True)
4815 4820 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
4816 4821
4817 4822 for f in m.files():
4818 4823 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
4819 4824 if os.path.exists(m.rel(f)):
4820 4825 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
4821 4826 ret = 1
4822 4827
4823 4828 if force:
4824 4829 list = modified + deleted + clean + added
4825 4830 elif after:
4826 4831 list = deleted
4827 4832 for f in modified + added + clean:
4828 4833 ui.warn(_('not removing %s: file still exists (use -f'
4829 4834 ' to force removal)\n') % m.rel(f))
4830 4835 ret = 1
4831 4836 else:
4832 4837 list = deleted + clean
4833 4838 for f in modified:
4834 4839 ui.warn(_('not removing %s: file is modified (use -f'
4835 4840 ' to force removal)\n') % m.rel(f))
4836 4841 ret = 1
4837 4842 for f in added:
4838 4843 ui.warn(_('not removing %s: file has been marked for add'
4839 4844 ' (use forget to undo)\n') % m.rel(f))
4840 4845 ret = 1
4841 4846
4842 4847 for f in sorted(list):
4843 4848 if ui.verbose or not m.exact(f):
4844 4849 ui.status(_('removing %s\n') % m.rel(f))
4845 4850
4846 4851 wlock = repo.wlock()
4847 4852 try:
4848 4853 if not after:
4849 4854 for f in list:
4850 4855 if f in added:
4851 4856 continue # we never unlink added files on remove
4852 4857 try:
4853 4858 util.unlinkpath(repo.wjoin(f))
4854 4859 except OSError, inst:
4855 4860 if inst.errno != errno.ENOENT:
4856 4861 raise
4857 4862 repo[None].forget(list)
4858 4863 finally:
4859 4864 wlock.release()
4860 4865
4861 4866 return ret
4862 4867
4863 4868 @command('rename|move|mv',
4864 4869 [('A', 'after', None, _('record a rename that has already occurred')),
4865 4870 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4866 4871 ] + walkopts + dryrunopts,
4867 4872 _('[OPTION]... SOURCE... DEST'))
4868 4873 def rename(ui, repo, *pats, **opts):
4869 4874 """rename files; equivalent of copy + remove
4870 4875
4871 4876 Mark dest as copies of sources; mark sources for deletion. If dest
4872 4877 is a directory, copies are put in that directory. If dest is a
4873 4878 file, there can only be one source.
4874 4879
4875 4880 By default, this command copies the contents of files as they
4876 4881 exist in the working directory. If invoked with -A/--after, the
4877 4882 operation is recorded, but no copying is performed.
4878 4883
4879 4884 This command takes effect at the next commit. To undo a rename
4880 4885 before that, see :hg:`revert`.
4881 4886
4882 4887 Returns 0 on success, 1 if errors are encountered.
4883 4888 """
4884 4889 wlock = repo.wlock(False)
4885 4890 try:
4886 4891 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4887 4892 finally:
4888 4893 wlock.release()
4889 4894
4890 4895 @command('resolve',
4891 4896 [('a', 'all', None, _('select all unresolved files')),
4892 4897 ('l', 'list', None, _('list state of files needing merge')),
4893 4898 ('m', 'mark', None, _('mark files as resolved')),
4894 4899 ('u', 'unmark', None, _('mark files as unresolved')),
4895 4900 ('n', 'no-status', None, _('hide status prefix'))]
4896 4901 + mergetoolopts + walkopts,
4897 4902 _('[OPTION]... [FILE]...'))
4898 4903 def resolve(ui, repo, *pats, **opts):
4899 4904 """redo merges or set/view the merge status of files
4900 4905
4901 4906 Merges with unresolved conflicts are often the result of
4902 4907 non-interactive merging using the ``internal:merge`` configuration
4903 4908 setting, or a command-line merge tool like ``diff3``. The resolve
4904 4909 command is used to manage the files involved in a merge, after
4905 4910 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4906 4911 working directory must have two parents). See :hg:`help
4907 4912 merge-tools` for information on configuring merge tools.
4908 4913
4909 4914 The resolve command can be used in the following ways:
4910 4915
4911 4916 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4912 4917 files, discarding any previous merge attempts. Re-merging is not
4913 4918 performed for files already marked as resolved. Use ``--all/-a``
4914 4919 to select all unresolved files. ``--tool`` can be used to specify
4915 4920 the merge tool used for the given files. It overrides the HGMERGE
4916 4921 environment variable and your configuration files. Previous file
4917 4922 contents are saved with a ``.orig`` suffix.
4918 4923
4919 4924 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4920 4925 (e.g. after having manually fixed-up the files). The default is
4921 4926 to mark all unresolved files.
4922 4927
4923 4928 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4924 4929 default is to mark all resolved files.
4925 4930
4926 4931 - :hg:`resolve -l`: list files which had or still have conflicts.
4927 4932 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4928 4933
4929 4934 Note that Mercurial will not let you commit files with unresolved
4930 4935 merge conflicts. You must use :hg:`resolve -m ...` before you can
4931 4936 commit after a conflicting merge.
4932 4937
4933 4938 Returns 0 on success, 1 if any files fail a resolve attempt.
4934 4939 """
4935 4940
4936 4941 all, mark, unmark, show, nostatus = \
4937 4942 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
4938 4943
4939 4944 if (show and (mark or unmark)) or (mark and unmark):
4940 4945 raise util.Abort(_("too many options specified"))
4941 4946 if pats and all:
4942 4947 raise util.Abort(_("can't specify --all and patterns"))
4943 4948 if not (all or pats or show or mark or unmark):
4944 4949 raise util.Abort(_('no files or directories specified; '
4945 4950 'use --all to remerge all files'))
4946 4951
4947 4952 ms = mergemod.mergestate(repo)
4948 4953 m = scmutil.match(repo[None], pats, opts)
4949 4954 ret = 0
4950 4955
4951 4956 for f in ms:
4952 4957 if m(f):
4953 4958 if show:
4954 4959 if nostatus:
4955 4960 ui.write("%s\n" % f)
4956 4961 else:
4957 4962 ui.write("%s %s\n" % (ms[f].upper(), f),
4958 4963 label='resolve.' +
4959 4964 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
4960 4965 elif mark:
4961 4966 ms.mark(f, "r")
4962 4967 elif unmark:
4963 4968 ms.mark(f, "u")
4964 4969 else:
4965 4970 wctx = repo[None]
4966 4971 mctx = wctx.parents()[-1]
4967 4972
4968 4973 # backup pre-resolve (merge uses .orig for its own purposes)
4969 4974 a = repo.wjoin(f)
4970 4975 util.copyfile(a, a + ".resolve")
4971 4976
4972 4977 try:
4973 4978 # resolve file
4974 4979 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4975 4980 if ms.resolve(f, wctx, mctx):
4976 4981 ret = 1
4977 4982 finally:
4978 4983 ui.setconfig('ui', 'forcemerge', '')
4979 4984 ms.commit()
4980 4985
4981 4986 # replace filemerge's .orig file with our resolve file
4982 4987 util.rename(a + ".resolve", a + ".orig")
4983 4988
4984 4989 ms.commit()
4985 4990 return ret
4986 4991
4987 4992 @command('revert',
4988 4993 [('a', 'all', None, _('revert all changes when no arguments given')),
4989 4994 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4990 4995 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4991 4996 ('C', 'no-backup', None, _('do not save backup copies of files')),
4992 4997 ] + walkopts + dryrunopts,
4993 4998 _('[OPTION]... [-r REV] [NAME]...'))
4994 4999 def revert(ui, repo, *pats, **opts):
4995 5000 """restore files to their checkout state
4996 5001
4997 5002 .. note::
4998 5003
4999 5004 To check out earlier revisions, you should use :hg:`update REV`.
5000 5005 To cancel an uncommitted merge (and lose your changes), use
5001 5006 :hg:`update --clean .`.
5002 5007
5003 5008 With no revision specified, revert the specified files or directories
5004 5009 to the contents they had in the parent of the working directory.
5005 5010 This restores the contents of files to an unmodified
5006 5011 state and unschedules adds, removes, copies, and renames. If the
5007 5012 working directory has two parents, you must explicitly specify a
5008 5013 revision.
5009 5014
5010 5015 Using the -r/--rev or -d/--date options, revert the given files or
5011 5016 directories to their states as of a specific revision. Because
5012 5017 revert does not change the working directory parents, this will
5013 5018 cause these files to appear modified. This can be helpful to "back
5014 5019 out" some or all of an earlier change. See :hg:`backout` for a
5015 5020 related method.
5016 5021
5017 5022 Modified files are saved with a .orig suffix before reverting.
5018 5023 To disable these backups, use --no-backup.
5019 5024
5020 5025 See :hg:`help dates` for a list of formats valid for -d/--date.
5021 5026
5022 5027 Returns 0 on success.
5023 5028 """
5024 5029
5025 5030 if opts.get("date"):
5026 5031 if opts.get("rev"):
5027 5032 raise util.Abort(_("you can't specify a revision and a date"))
5028 5033 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5029 5034
5030 5035 parent, p2 = repo.dirstate.parents()
5031 5036 if not opts.get('rev') and p2 != nullid:
5032 5037 # revert after merge is a trap for new users (issue2915)
5033 5038 raise util.Abort(_('uncommitted merge with no revision specified'),
5034 5039 hint=_('use "hg update" or see "hg help revert"'))
5035 5040
5036 5041 ctx = scmutil.revsingle(repo, opts.get('rev'))
5037 5042
5038 5043 if not pats and not opts.get('all'):
5039 5044 msg = _("no files or directories specified")
5040 5045 if p2 != nullid:
5041 5046 hint = _("uncommitted merge, use --all to discard all changes,"
5042 5047 " or 'hg update -C .' to abort the merge")
5043 5048 raise util.Abort(msg, hint=hint)
5044 5049 dirty = util.any(repo.status())
5045 5050 node = ctx.node()
5046 5051 if node != parent:
5047 5052 if dirty:
5048 5053 hint = _("uncommitted changes, use --all to discard all"
5049 5054 " changes, or 'hg update %s' to update") % ctx.rev()
5050 5055 else:
5051 5056 hint = _("use --all to revert all files,"
5052 5057 " or 'hg update %s' to update") % ctx.rev()
5053 5058 elif dirty:
5054 5059 hint = _("uncommitted changes, use --all to discard all changes")
5055 5060 else:
5056 5061 hint = _("use --all to revert all files")
5057 5062 raise util.Abort(msg, hint=hint)
5058 5063
5059 5064 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
5060 5065
5061 5066 @command('rollback', dryrunopts +
5062 5067 [('f', 'force', False, _('ignore safety measures'))])
5063 5068 def rollback(ui, repo, **opts):
5064 5069 """roll back the last transaction (dangerous)
5065 5070
5066 5071 This command should be used with care. There is only one level of
5067 5072 rollback, and there is no way to undo a rollback. It will also
5068 5073 restore the dirstate at the time of the last transaction, losing
5069 5074 any dirstate changes since that time. This command does not alter
5070 5075 the working directory.
5071 5076
5072 5077 Transactions are used to encapsulate the effects of all commands
5073 5078 that create new changesets or propagate existing changesets into a
5074 5079 repository.
5075 5080
5076 5081 .. container:: verbose
5077 5082
5078 5083 For example, the following commands are transactional, and their
5079 5084 effects can be rolled back:
5080 5085
5081 5086 - commit
5082 5087 - import
5083 5088 - pull
5084 5089 - push (with this repository as the destination)
5085 5090 - unbundle
5086 5091
5087 5092 To avoid permanent data loss, rollback will refuse to rollback a
5088 5093 commit transaction if it isn't checked out. Use --force to
5089 5094 override this protection.
5090 5095
5091 5096 This command is not intended for use on public repositories. Once
5092 5097 changes are visible for pull by other users, rolling a transaction
5093 5098 back locally is ineffective (someone else may already have pulled
5094 5099 the changes). Furthermore, a race is possible with readers of the
5095 5100 repository; for example an in-progress pull from the repository
5096 5101 may fail if a rollback is performed.
5097 5102
5098 5103 Returns 0 on success, 1 if no rollback data is available.
5099 5104 """
5100 5105 return repo.rollback(dryrun=opts.get('dry_run'),
5101 5106 force=opts.get('force'))
5102 5107
5103 5108 @command('root', [])
5104 5109 def root(ui, repo):
5105 5110 """print the root (top) of the current working directory
5106 5111
5107 5112 Print the root directory of the current repository.
5108 5113
5109 5114 Returns 0 on success.
5110 5115 """
5111 5116 ui.write(repo.root + "\n")
5112 5117
5113 5118 @command('^serve',
5114 5119 [('A', 'accesslog', '', _('name of access log file to write to'),
5115 5120 _('FILE')),
5116 5121 ('d', 'daemon', None, _('run server in background')),
5117 5122 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('NUM')),
5118 5123 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5119 5124 # use string type, then we can check if something was passed
5120 5125 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5121 5126 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5122 5127 _('ADDR')),
5123 5128 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5124 5129 _('PREFIX')),
5125 5130 ('n', 'name', '',
5126 5131 _('name to show in web pages (default: working directory)'), _('NAME')),
5127 5132 ('', 'web-conf', '',
5128 5133 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
5129 5134 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5130 5135 _('FILE')),
5131 5136 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5132 5137 ('', 'stdio', None, _('for remote clients')),
5133 5138 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
5134 5139 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5135 5140 ('', 'style', '', _('template style to use'), _('STYLE')),
5136 5141 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5137 5142 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
5138 5143 _('[OPTION]...'))
5139 5144 def serve(ui, repo, **opts):
5140 5145 """start stand-alone webserver
5141 5146
5142 5147 Start a local HTTP repository browser and pull server. You can use
5143 5148 this for ad-hoc sharing and browsing of repositories. It is
5144 5149 recommended to use a real web server to serve a repository for
5145 5150 longer periods of time.
5146 5151
5147 5152 Please note that the server does not implement access control.
5148 5153 This means that, by default, anybody can read from the server and
5149 5154 nobody can write to it by default. Set the ``web.allow_push``
5150 5155 option to ``*`` to allow everybody to push to the server. You
5151 5156 should use a real web server if you need to authenticate users.
5152 5157
5153 5158 By default, the server logs accesses to stdout and errors to
5154 5159 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5155 5160 files.
5156 5161
5157 5162 To have the server choose a free port number to listen on, specify
5158 5163 a port number of 0; in this case, the server will print the port
5159 5164 number it uses.
5160 5165
5161 5166 Returns 0 on success.
5162 5167 """
5163 5168
5164 5169 if opts["stdio"] and opts["cmdserver"]:
5165 5170 raise util.Abort(_("cannot use --stdio with --cmdserver"))
5166 5171
5167 5172 def checkrepo():
5168 5173 if repo is None:
5169 5174 raise error.RepoError(_("there is no Mercurial repository here"
5170 5175 " (.hg not found)"))
5171 5176
5172 5177 if opts["stdio"]:
5173 5178 checkrepo()
5174 5179 s = sshserver.sshserver(ui, repo)
5175 5180 s.serve_forever()
5176 5181
5177 5182 if opts["cmdserver"]:
5178 5183 checkrepo()
5179 5184 s = commandserver.server(ui, repo, opts["cmdserver"])
5180 5185 return s.serve()
5181 5186
5182 5187 # this way we can check if something was given in the command-line
5183 5188 if opts.get('port'):
5184 5189 opts['port'] = util.getport(opts.get('port'))
5185 5190
5186 5191 baseui = repo and repo.baseui or ui
5187 5192 optlist = ("name templates style address port prefix ipv6"
5188 5193 " accesslog errorlog certificate encoding")
5189 5194 for o in optlist.split():
5190 5195 val = opts.get(o, '')
5191 5196 if val in (None, ''): # should check against default options instead
5192 5197 continue
5193 5198 baseui.setconfig("web", o, val)
5194 5199 if repo and repo.ui != baseui:
5195 5200 repo.ui.setconfig("web", o, val)
5196 5201
5197 5202 o = opts.get('web_conf') or opts.get('webdir_conf')
5198 5203 if not o:
5199 5204 if not repo:
5200 5205 raise error.RepoError(_("there is no Mercurial repository"
5201 5206 " here (.hg not found)"))
5202 5207 o = repo.root
5203 5208
5204 5209 app = hgweb.hgweb(o, baseui=ui)
5205 5210
5206 5211 class service(object):
5207 5212 def init(self):
5208 5213 util.setsignalhandler()
5209 5214 self.httpd = hgweb.server.create_server(ui, app)
5210 5215
5211 5216 if opts['port'] and not ui.verbose:
5212 5217 return
5213 5218
5214 5219 if self.httpd.prefix:
5215 5220 prefix = self.httpd.prefix.strip('/') + '/'
5216 5221 else:
5217 5222 prefix = ''
5218 5223
5219 5224 port = ':%d' % self.httpd.port
5220 5225 if port == ':80':
5221 5226 port = ''
5222 5227
5223 5228 bindaddr = self.httpd.addr
5224 5229 if bindaddr == '0.0.0.0':
5225 5230 bindaddr = '*'
5226 5231 elif ':' in bindaddr: # IPv6
5227 5232 bindaddr = '[%s]' % bindaddr
5228 5233
5229 5234 fqaddr = self.httpd.fqaddr
5230 5235 if ':' in fqaddr:
5231 5236 fqaddr = '[%s]' % fqaddr
5232 5237 if opts['port']:
5233 5238 write = ui.status
5234 5239 else:
5235 5240 write = ui.write
5236 5241 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
5237 5242 (fqaddr, port, prefix, bindaddr, self.httpd.port))
5238 5243
5239 5244 def run(self):
5240 5245 self.httpd.serve_forever()
5241 5246
5242 5247 service = service()
5243 5248
5244 5249 cmdutil.service(opts, initfn=service.init, runfn=service.run)
5245 5250
5246 5251 @command('showconfig|debugconfig',
5247 5252 [('u', 'untrusted', None, _('show untrusted configuration options'))],
5248 5253 _('[-u] [NAME]...'))
5249 5254 def showconfig(ui, repo, *values, **opts):
5250 5255 """show combined config settings from all hgrc files
5251 5256
5252 5257 With no arguments, print names and values of all config items.
5253 5258
5254 5259 With one argument of the form section.name, print just the value
5255 5260 of that config item.
5256 5261
5257 5262 With multiple arguments, print names and values of all config
5258 5263 items with matching section names.
5259 5264
5260 5265 With --debug, the source (filename and line number) is printed
5261 5266 for each config item.
5262 5267
5263 5268 Returns 0 on success.
5264 5269 """
5265 5270
5266 5271 for f in scmutil.rcpath():
5267 5272 ui.debug('read config from: %s\n' % f)
5268 5273 untrusted = bool(opts.get('untrusted'))
5269 5274 if values:
5270 5275 sections = [v for v in values if '.' not in v]
5271 5276 items = [v for v in values if '.' in v]
5272 5277 if len(items) > 1 or items and sections:
5273 5278 raise util.Abort(_('only one config item permitted'))
5274 5279 for section, name, value in ui.walkconfig(untrusted=untrusted):
5275 5280 value = str(value).replace('\n', '\\n')
5276 5281 sectname = section + '.' + name
5277 5282 if values:
5278 5283 for v in values:
5279 5284 if v == section:
5280 5285 ui.debug('%s: ' %
5281 5286 ui.configsource(section, name, untrusted))
5282 5287 ui.write('%s=%s\n' % (sectname, value))
5283 5288 elif v == sectname:
5284 5289 ui.debug('%s: ' %
5285 5290 ui.configsource(section, name, untrusted))
5286 5291 ui.write(value, '\n')
5287 5292 else:
5288 5293 ui.debug('%s: ' %
5289 5294 ui.configsource(section, name, untrusted))
5290 5295 ui.write('%s=%s\n' % (sectname, value))
5291 5296
5292 5297 @command('^status|st',
5293 5298 [('A', 'all', None, _('show status of all files')),
5294 5299 ('m', 'modified', None, _('show only modified files')),
5295 5300 ('a', 'added', None, _('show only added files')),
5296 5301 ('r', 'removed', None, _('show only removed files')),
5297 5302 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5298 5303 ('c', 'clean', None, _('show only files without changes')),
5299 5304 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5300 5305 ('i', 'ignored', None, _('show only ignored files')),
5301 5306 ('n', 'no-status', None, _('hide status prefix')),
5302 5307 ('C', 'copies', None, _('show source of copied files')),
5303 5308 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5304 5309 ('', 'rev', [], _('show difference from revision'), _('REV')),
5305 5310 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5306 5311 ] + walkopts + subrepoopts,
5307 5312 _('[OPTION]... [FILE]...'))
5308 5313 def status(ui, repo, *pats, **opts):
5309 5314 """show changed files in the working directory
5310 5315
5311 5316 Show status of files in the repository. If names are given, only
5312 5317 files that match are shown. Files that are clean or ignored or
5313 5318 the source of a copy/move operation, are not listed unless
5314 5319 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5315 5320 Unless options described with "show only ..." are given, the
5316 5321 options -mardu are used.
5317 5322
5318 5323 Option -q/--quiet hides untracked (unknown and ignored) files
5319 5324 unless explicitly requested with -u/--unknown or -i/--ignored.
5320 5325
5321 5326 .. note::
5322 5327 status may appear to disagree with diff if permissions have
5323 5328 changed or a merge has occurred. The standard diff format does
5324 5329 not report permission changes and diff only reports changes
5325 5330 relative to one merge parent.
5326 5331
5327 5332 If one revision is given, it is used as the base revision.
5328 5333 If two revisions are given, the differences between them are
5329 5334 shown. The --change option can also be used as a shortcut to list
5330 5335 the changed files of a revision from its first parent.
5331 5336
5332 5337 The codes used to show the status of files are::
5333 5338
5334 5339 M = modified
5335 5340 A = added
5336 5341 R = removed
5337 5342 C = clean
5338 5343 ! = missing (deleted by non-hg command, but still tracked)
5339 5344 ? = not tracked
5340 5345 I = ignored
5341 5346 = origin of the previous file listed as A (added)
5342 5347
5343 5348 .. container:: verbose
5344 5349
5345 5350 Examples:
5346 5351
5347 5352 - show changes in the working directory relative to a
5348 5353 changeset::
5349 5354
5350 5355 hg status --rev 9353
5351 5356
5352 5357 - show all changes including copies in an existing changeset::
5353 5358
5354 5359 hg status --copies --change 9353
5355 5360
5356 5361 - get a NUL separated list of added files, suitable for xargs::
5357 5362
5358 5363 hg status -an0
5359 5364
5360 5365 Returns 0 on success.
5361 5366 """
5362 5367
5363 5368 revs = opts.get('rev')
5364 5369 change = opts.get('change')
5365 5370
5366 5371 if revs and change:
5367 5372 msg = _('cannot specify --rev and --change at the same time')
5368 5373 raise util.Abort(msg)
5369 5374 elif change:
5370 5375 node2 = scmutil.revsingle(repo, change, None).node()
5371 5376 node1 = repo[node2].p1().node()
5372 5377 else:
5373 5378 node1, node2 = scmutil.revpair(repo, revs)
5374 5379
5375 5380 cwd = (pats and repo.getcwd()) or ''
5376 5381 end = opts.get('print0') and '\0' or '\n'
5377 5382 copy = {}
5378 5383 states = 'modified added removed deleted unknown ignored clean'.split()
5379 5384 show = [k for k in states if opts.get(k)]
5380 5385 if opts.get('all'):
5381 5386 show += ui.quiet and (states[:4] + ['clean']) or states
5382 5387 if not show:
5383 5388 show = ui.quiet and states[:4] or states[:5]
5384 5389
5385 5390 stat = repo.status(node1, node2, scmutil.match(repo[node2], pats, opts),
5386 5391 'ignored' in show, 'clean' in show, 'unknown' in show,
5387 5392 opts.get('subrepos'))
5388 5393 changestates = zip(states, 'MAR!?IC', stat)
5389 5394
5390 5395 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
5391 5396 copy = copies.pathcopies(repo[node1], repo[node2])
5392 5397
5393 5398 fm = ui.formatter('status', opts)
5394 5399 format = '%s %s' + end
5395 5400 if opts.get('no_status'):
5396 5401 format = '%.0s%s' + end
5397 5402
5398 5403 for state, char, files in changestates:
5399 5404 if state in show:
5400 5405 label = 'status.' + state
5401 5406 for f in files:
5402 5407 fm.startitem()
5403 5408 fm.write("status path", format, char,
5404 5409 repo.pathto(f, cwd), label=label)
5405 5410 if f in copy:
5406 5411 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
5407 5412 label='status.copied')
5408 5413 fm.end()
5409 5414
5410 5415 @command('^summary|sum',
5411 5416 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5412 5417 def summary(ui, repo, **opts):
5413 5418 """summarize working directory state
5414 5419
5415 5420 This generates a brief summary of the working directory state,
5416 5421 including parents, branch, commit status, and available updates.
5417 5422
5418 5423 With the --remote option, this will check the default paths for
5419 5424 incoming and outgoing changes. This can be time-consuming.
5420 5425
5421 5426 Returns 0 on success.
5422 5427 """
5423 5428
5424 5429 ctx = repo[None]
5425 5430 parents = ctx.parents()
5426 5431 pnode = parents[0].node()
5427 5432 marks = []
5428 5433
5429 5434 for p in parents:
5430 5435 # label with log.changeset (instead of log.parent) since this
5431 5436 # shows a working directory parent *changeset*:
5432 5437 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5433 5438 label='log.changeset changeset.%s' % p.phasestr())
5434 5439 ui.write(' '.join(p.tags()), label='log.tag')
5435 5440 if p.bookmarks():
5436 5441 marks.extend(p.bookmarks())
5437 5442 if p.rev() == -1:
5438 5443 if not len(repo):
5439 5444 ui.write(_(' (empty repository)'))
5440 5445 else:
5441 5446 ui.write(_(' (no revision checked out)'))
5442 5447 ui.write('\n')
5443 5448 if p.description():
5444 5449 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5445 5450 label='log.summary')
5446 5451
5447 5452 branch = ctx.branch()
5448 5453 bheads = repo.branchheads(branch)
5449 5454 m = _('branch: %s\n') % branch
5450 5455 if branch != 'default':
5451 5456 ui.write(m, label='log.branch')
5452 5457 else:
5453 5458 ui.status(m, label='log.branch')
5454 5459
5455 5460 if marks:
5456 5461 current = repo._bookmarkcurrent
5457 5462 ui.write(_('bookmarks:'), label='log.bookmark')
5458 5463 if current is not None:
5459 5464 try:
5460 5465 marks.remove(current)
5461 5466 ui.write(' *' + current, label='bookmarks.current')
5462 5467 except ValueError:
5463 5468 # current bookmark not in parent ctx marks
5464 5469 pass
5465 5470 for m in marks:
5466 5471 ui.write(' ' + m, label='log.bookmark')
5467 5472 ui.write('\n', label='log.bookmark')
5468 5473
5469 5474 st = list(repo.status(unknown=True))[:6]
5470 5475
5471 5476 c = repo.dirstate.copies()
5472 5477 copied, renamed = [], []
5473 5478 for d, s in c.iteritems():
5474 5479 if s in st[2]:
5475 5480 st[2].remove(s)
5476 5481 renamed.append(d)
5477 5482 else:
5478 5483 copied.append(d)
5479 5484 if d in st[1]:
5480 5485 st[1].remove(d)
5481 5486 st.insert(3, renamed)
5482 5487 st.insert(4, copied)
5483 5488
5484 5489 ms = mergemod.mergestate(repo)
5485 5490 st.append([f for f in ms if ms[f] == 'u'])
5486 5491
5487 5492 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5488 5493 st.append(subs)
5489 5494
5490 5495 labels = [ui.label(_('%d modified'), 'status.modified'),
5491 5496 ui.label(_('%d added'), 'status.added'),
5492 5497 ui.label(_('%d removed'), 'status.removed'),
5493 5498 ui.label(_('%d renamed'), 'status.copied'),
5494 5499 ui.label(_('%d copied'), 'status.copied'),
5495 5500 ui.label(_('%d deleted'), 'status.deleted'),
5496 5501 ui.label(_('%d unknown'), 'status.unknown'),
5497 5502 ui.label(_('%d ignored'), 'status.ignored'),
5498 5503 ui.label(_('%d unresolved'), 'resolve.unresolved'),
5499 5504 ui.label(_('%d subrepos'), 'status.modified')]
5500 5505 t = []
5501 5506 for s, l in zip(st, labels):
5502 5507 if s:
5503 5508 t.append(l % len(s))
5504 5509
5505 5510 t = ', '.join(t)
5506 5511 cleanworkdir = False
5507 5512
5508 5513 if len(parents) > 1:
5509 5514 t += _(' (merge)')
5510 5515 elif branch != parents[0].branch():
5511 5516 t += _(' (new branch)')
5512 5517 elif (parents[0].closesbranch() and
5513 5518 pnode in repo.branchheads(branch, closed=True)):
5514 5519 t += _(' (head closed)')
5515 5520 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
5516 5521 t += _(' (clean)')
5517 5522 cleanworkdir = True
5518 5523 elif pnode not in bheads:
5519 5524 t += _(' (new branch head)')
5520 5525
5521 5526 if cleanworkdir:
5522 5527 ui.status(_('commit: %s\n') % t.strip())
5523 5528 else:
5524 5529 ui.write(_('commit: %s\n') % t.strip())
5525 5530
5526 5531 # all ancestors of branch heads - all ancestors of parent = new csets
5527 5532 new = [0] * len(repo)
5528 5533 cl = repo.changelog
5529 5534 for a in [cl.rev(n) for n in bheads]:
5530 5535 new[a] = 1
5531 5536 for a in cl.ancestors([cl.rev(n) for n in bheads]):
5532 5537 new[a] = 1
5533 5538 for a in [p.rev() for p in parents]:
5534 5539 if a >= 0:
5535 5540 new[a] = 0
5536 5541 for a in cl.ancestors([p.rev() for p in parents]):
5537 5542 new[a] = 0
5538 5543 new = sum(new)
5539 5544
5540 5545 if new == 0:
5541 5546 ui.status(_('update: (current)\n'))
5542 5547 elif pnode not in bheads:
5543 5548 ui.write(_('update: %d new changesets (update)\n') % new)
5544 5549 else:
5545 5550 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5546 5551 (new, len(bheads)))
5547 5552
5548 5553 if opts.get('remote'):
5549 5554 t = []
5550 5555 source, branches = hg.parseurl(ui.expandpath('default'))
5551 5556 other = hg.peer(repo, {}, source)
5552 5557 revs, checkout = hg.addbranchrevs(repo, other, branches,
5553 5558 opts.get('rev'))
5554 5559 ui.debug('comparing with %s\n' % util.hidepassword(source))
5555 5560 repo.ui.pushbuffer()
5556 5561 commoninc = discovery.findcommonincoming(repo, other)
5557 5562 _common, incoming, _rheads = commoninc
5558 5563 repo.ui.popbuffer()
5559 5564 if incoming:
5560 5565 t.append(_('1 or more incoming'))
5561 5566
5562 5567 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5563 5568 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5564 5569 if source != dest:
5565 5570 other = hg.peer(repo, {}, dest)
5566 5571 commoninc = None
5567 5572 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5568 5573 repo.ui.pushbuffer()
5569 5574 outgoing = discovery.findcommonoutgoing(repo, other,
5570 5575 commoninc=commoninc)
5571 5576 repo.ui.popbuffer()
5572 5577 o = outgoing.missing
5573 5578 if o:
5574 5579 t.append(_('%d outgoing') % len(o))
5575 5580 if 'bookmarks' in other.listkeys('namespaces'):
5576 5581 lmarks = repo.listkeys('bookmarks')
5577 5582 rmarks = other.listkeys('bookmarks')
5578 5583 diff = set(rmarks) - set(lmarks)
5579 5584 if len(diff) > 0:
5580 5585 t.append(_('%d incoming bookmarks') % len(diff))
5581 5586 diff = set(lmarks) - set(rmarks)
5582 5587 if len(diff) > 0:
5583 5588 t.append(_('%d outgoing bookmarks') % len(diff))
5584 5589
5585 5590 if t:
5586 5591 ui.write(_('remote: %s\n') % (', '.join(t)))
5587 5592 else:
5588 5593 ui.status(_('remote: (synced)\n'))
5589 5594
5590 5595 @command('tag',
5591 5596 [('f', 'force', None, _('force tag')),
5592 5597 ('l', 'local', None, _('make the tag local')),
5593 5598 ('r', 'rev', '', _('revision to tag'), _('REV')),
5594 5599 ('', 'remove', None, _('remove a tag')),
5595 5600 # -l/--local is already there, commitopts cannot be used
5596 5601 ('e', 'edit', None, _('edit commit message')),
5597 5602 ('m', 'message', '', _('use <text> as commit message'), _('TEXT')),
5598 5603 ] + commitopts2,
5599 5604 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5600 5605 def tag(ui, repo, name1, *names, **opts):
5601 5606 """add one or more tags for the current or given revision
5602 5607
5603 5608 Name a particular revision using <name>.
5604 5609
5605 5610 Tags are used to name particular revisions of the repository and are
5606 5611 very useful to compare different revisions, to go back to significant
5607 5612 earlier versions or to mark branch points as releases, etc. Changing
5608 5613 an existing tag is normally disallowed; use -f/--force to override.
5609 5614
5610 5615 If no revision is given, the parent of the working directory is
5611 5616 used, or tip if no revision is checked out.
5612 5617
5613 5618 To facilitate version control, distribution, and merging of tags,
5614 5619 they are stored as a file named ".hgtags" which is managed similarly
5615 5620 to other project files and can be hand-edited if necessary. This
5616 5621 also means that tagging creates a new commit. The file
5617 5622 ".hg/localtags" is used for local tags (not shared among
5618 5623 repositories).
5619 5624
5620 5625 Tag commits are usually made at the head of a branch. If the parent
5621 5626 of the working directory is not a branch head, :hg:`tag` aborts; use
5622 5627 -f/--force to force the tag commit to be based on a non-head
5623 5628 changeset.
5624 5629
5625 5630 See :hg:`help dates` for a list of formats valid for -d/--date.
5626 5631
5627 5632 Since tag names have priority over branch names during revision
5628 5633 lookup, using an existing branch name as a tag name is discouraged.
5629 5634
5630 5635 Returns 0 on success.
5631 5636 """
5632 5637 wlock = lock = None
5633 5638 try:
5634 5639 wlock = repo.wlock()
5635 5640 lock = repo.lock()
5636 5641 rev_ = "."
5637 5642 names = [t.strip() for t in (name1,) + names]
5638 5643 if len(names) != len(set(names)):
5639 5644 raise util.Abort(_('tag names must be unique'))
5640 5645 for n in names:
5641 5646 if n in ['tip', '.', 'null']:
5642 5647 raise util.Abort(_("the name '%s' is reserved") % n)
5643 5648 if not n:
5644 5649 raise util.Abort(_('tag names cannot consist entirely of '
5645 5650 'whitespace'))
5646 5651 if opts.get('rev') and opts.get('remove'):
5647 5652 raise util.Abort(_("--rev and --remove are incompatible"))
5648 5653 if opts.get('rev'):
5649 5654 rev_ = opts['rev']
5650 5655 message = opts.get('message')
5651 5656 if opts.get('remove'):
5652 5657 expectedtype = opts.get('local') and 'local' or 'global'
5653 5658 for n in names:
5654 5659 if not repo.tagtype(n):
5655 5660 raise util.Abort(_("tag '%s' does not exist") % n)
5656 5661 if repo.tagtype(n) != expectedtype:
5657 5662 if expectedtype == 'global':
5658 5663 raise util.Abort(_("tag '%s' is not a global tag") % n)
5659 5664 else:
5660 5665 raise util.Abort(_("tag '%s' is not a local tag") % n)
5661 5666 rev_ = nullid
5662 5667 if not message:
5663 5668 # we don't translate commit messages
5664 5669 message = 'Removed tag %s' % ', '.join(names)
5665 5670 elif not opts.get('force'):
5666 5671 for n in names:
5667 5672 if n in repo.tags():
5668 5673 raise util.Abort(_("tag '%s' already exists "
5669 5674 "(use -f to force)") % n)
5670 5675 if not opts.get('local'):
5671 5676 p1, p2 = repo.dirstate.parents()
5672 5677 if p2 != nullid:
5673 5678 raise util.Abort(_('uncommitted merge'))
5674 5679 bheads = repo.branchheads()
5675 5680 if not opts.get('force') and bheads and p1 not in bheads:
5676 5681 raise util.Abort(_('not at a branch head (use -f to force)'))
5677 5682 r = scmutil.revsingle(repo, rev_).node()
5678 5683
5679 5684 if not message:
5680 5685 # we don't translate commit messages
5681 5686 message = ('Added tag %s for changeset %s' %
5682 5687 (', '.join(names), short(r)))
5683 5688
5684 5689 date = opts.get('date')
5685 5690 if date:
5686 5691 date = util.parsedate(date)
5687 5692
5688 5693 if opts.get('edit'):
5689 5694 message = ui.edit(message, ui.username())
5690 5695
5691 5696 # don't allow tagging the null rev
5692 5697 if (not opts.get('remove') and
5693 5698 scmutil.revsingle(repo, rev_).rev() == nullrev):
5694 5699 raise util.Abort(_("null revision specified"))
5695 5700
5696 5701 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
5697 5702 finally:
5698 5703 release(lock, wlock)
5699 5704
5700 5705 @command('tags', [], '')
5701 5706 def tags(ui, repo):
5702 5707 """list repository tags
5703 5708
5704 5709 This lists both regular and local tags. When the -v/--verbose
5705 5710 switch is used, a third column "local" is printed for local tags.
5706 5711
5707 5712 Returns 0 on success.
5708 5713 """
5709 5714
5710 5715 hexfunc = ui.debugflag and hex or short
5711 5716 tagtype = ""
5712 5717
5713 5718 for t, n in reversed(repo.tagslist()):
5714 5719 if ui.quiet:
5715 5720 ui.write("%s\n" % t, label='tags.normal')
5716 5721 continue
5717 5722
5718 5723 hn = hexfunc(n)
5719 5724 r = "%5d:%s" % (repo.changelog.rev(n), hn)
5720 5725 rev = ui.label(r, 'log.changeset changeset.%s' % repo[n].phasestr())
5721 5726 spaces = " " * (30 - encoding.colwidth(t))
5722 5727
5723 5728 tag = ui.label(t, 'tags.normal')
5724 5729 if ui.verbose:
5725 5730 if repo.tagtype(t) == 'local':
5726 5731 tagtype = " local"
5727 5732 tag = ui.label(t, 'tags.local')
5728 5733 else:
5729 5734 tagtype = ""
5730 5735 ui.write("%s%s %s%s\n" % (tag, spaces, rev, tagtype))
5731 5736
5732 5737 @command('tip',
5733 5738 [('p', 'patch', None, _('show patch')),
5734 5739 ('g', 'git', None, _('use git extended diff format')),
5735 5740 ] + templateopts,
5736 5741 _('[-p] [-g]'))
5737 5742 def tip(ui, repo, **opts):
5738 5743 """show the tip revision
5739 5744
5740 5745 The tip revision (usually just called the tip) is the changeset
5741 5746 most recently added to the repository (and therefore the most
5742 5747 recently changed head).
5743 5748
5744 5749 If you have just made a commit, that commit will be the tip. If
5745 5750 you have just pulled changes from another repository, the tip of
5746 5751 that repository becomes the current tip. The "tip" tag is special
5747 5752 and cannot be renamed or assigned to a different changeset.
5748 5753
5749 5754 Returns 0 on success.
5750 5755 """
5751 5756 displayer = cmdutil.show_changeset(ui, repo, opts)
5752 5757 displayer.show(repo[len(repo) - 1])
5753 5758 displayer.close()
5754 5759
5755 5760 @command('unbundle',
5756 5761 [('u', 'update', None,
5757 5762 _('update to new branch head if changesets were unbundled'))],
5758 5763 _('[-u] FILE...'))
5759 5764 def unbundle(ui, repo, fname1, *fnames, **opts):
5760 5765 """apply one or more changegroup files
5761 5766
5762 5767 Apply one or more compressed changegroup files generated by the
5763 5768 bundle command.
5764 5769
5765 5770 Returns 0 on success, 1 if an update has unresolved files.
5766 5771 """
5767 5772 fnames = (fname1,) + fnames
5768 5773
5769 5774 lock = repo.lock()
5770 5775 wc = repo['.']
5771 5776 try:
5772 5777 for fname in fnames:
5773 5778 f = url.open(ui, fname)
5774 5779 gen = changegroup.readbundle(f, fname)
5775 5780 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
5776 5781 finally:
5777 5782 lock.release()
5778 5783 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
5779 5784 return postincoming(ui, repo, modheads, opts.get('update'), None)
5780 5785
5781 5786 @command('^update|up|checkout|co',
5782 5787 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5783 5788 ('c', 'check', None,
5784 5789 _('update across branches if no uncommitted changes')),
5785 5790 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5786 5791 ('r', 'rev', '', _('revision'), _('REV'))],
5787 5792 _('[-c] [-C] [-d DATE] [[-r] REV]'))
5788 5793 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
5789 5794 """update working directory (or switch revisions)
5790 5795
5791 5796 Update the repository's working directory to the specified
5792 5797 changeset. If no changeset is specified, update to the tip of the
5793 5798 current named branch and move the current bookmark (see :hg:`help
5794 5799 bookmarks`).
5795 5800
5796 5801 Update sets the working directory's parent revision to the specified
5797 5802 changeset (see :hg:`help parents`).
5798 5803
5799 5804 If the changeset is not a descendant or ancestor of the working
5800 5805 directory's parent, the update is aborted. With the -c/--check
5801 5806 option, the working directory is checked for uncommitted changes; if
5802 5807 none are found, the working directory is updated to the specified
5803 5808 changeset.
5804 5809
5805 5810 .. container:: verbose
5806 5811
5807 5812 The following rules apply when the working directory contains
5808 5813 uncommitted changes:
5809 5814
5810 5815 1. If neither -c/--check nor -C/--clean is specified, and if
5811 5816 the requested changeset is an ancestor or descendant of
5812 5817 the working directory's parent, the uncommitted changes
5813 5818 are merged into the requested changeset and the merged
5814 5819 result is left uncommitted. If the requested changeset is
5815 5820 not an ancestor or descendant (that is, it is on another
5816 5821 branch), the update is aborted and the uncommitted changes
5817 5822 are preserved.
5818 5823
5819 5824 2. With the -c/--check option, the update is aborted and the
5820 5825 uncommitted changes are preserved.
5821 5826
5822 5827 3. With the -C/--clean option, uncommitted changes are discarded and
5823 5828 the working directory is updated to the requested changeset.
5824 5829
5825 5830 To cancel an uncommitted merge (and lose your changes), use
5826 5831 :hg:`update --clean .`.
5827 5832
5828 5833 Use null as the changeset to remove the working directory (like
5829 5834 :hg:`clone -U`).
5830 5835
5831 5836 If you want to revert just one file to an older revision, use
5832 5837 :hg:`revert [-r REV] NAME`.
5833 5838
5834 5839 See :hg:`help dates` for a list of formats valid for -d/--date.
5835 5840
5836 5841 Returns 0 on success, 1 if there are unresolved files.
5837 5842 """
5838 5843 if rev and node:
5839 5844 raise util.Abort(_("please specify just one revision"))
5840 5845
5841 5846 if rev is None or rev == '':
5842 5847 rev = node
5843 5848
5844 5849 # with no argument, we also move the current bookmark, if any
5845 5850 movemarkfrom = None
5846 5851 if rev is None or node == '':
5847 5852 movemarkfrom = repo['.'].node()
5848 5853
5849 5854 # if we defined a bookmark, we have to remember the original bookmark name
5850 5855 brev = rev
5851 5856 rev = scmutil.revsingle(repo, rev, rev).rev()
5852 5857
5853 5858 if check and clean:
5854 5859 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
5855 5860
5856 5861 if date:
5857 5862 if rev is not None:
5858 5863 raise util.Abort(_("you can't specify a revision and a date"))
5859 5864 rev = cmdutil.finddate(ui, repo, date)
5860 5865
5861 5866 if check:
5862 5867 c = repo[None]
5863 5868 if c.dirty(merge=False, branch=False):
5864 5869 raise util.Abort(_("uncommitted local changes"))
5865 5870 if rev is None:
5866 5871 rev = repo[repo[None].branch()].rev()
5867 5872 mergemod._checkunknown(repo, repo[None], repo[rev])
5868 5873
5869 5874 if clean:
5870 5875 ret = hg.clean(repo, rev)
5871 5876 else:
5872 5877 ret = hg.update(repo, rev)
5873 5878
5874 5879 if not ret and movemarkfrom:
5875 5880 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
5876 5881 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
5877 5882 elif brev in repo._bookmarks:
5878 5883 bookmarks.setcurrent(repo, brev)
5879 5884 elif brev:
5880 5885 bookmarks.unsetcurrent(repo)
5881 5886
5882 5887 return ret
5883 5888
5884 5889 @command('verify', [])
5885 5890 def verify(ui, repo):
5886 5891 """verify the integrity of the repository
5887 5892
5888 5893 Verify the integrity of the current repository.
5889 5894
5890 5895 This will perform an extensive check of the repository's
5891 5896 integrity, validating the hashes and checksums of each entry in
5892 5897 the changelog, manifest, and tracked files, as well as the
5893 5898 integrity of their crosslinks and indices.
5894 5899
5895 5900 Please see http://mercurial.selenic.com/wiki/RepositoryCorruption
5896 5901 for more information about recovery from corruption of the
5897 5902 repository.
5898 5903
5899 5904 Returns 0 on success, 1 if errors are encountered.
5900 5905 """
5901 5906 return hg.verify(repo)
5902 5907
5903 5908 @command('version', [])
5904 5909 def version_(ui):
5905 5910 """output version and copyright information"""
5906 5911 ui.write(_("Mercurial Distributed SCM (version %s)\n")
5907 5912 % util.version())
5908 5913 ui.status(_(
5909 5914 "(see http://mercurial.selenic.com for more information)\n"
5910 5915 "\nCopyright (C) 2005-2012 Matt Mackall and others\n"
5911 5916 "This is free software; see the source for copying conditions. "
5912 5917 "There is NO\nwarranty; "
5913 5918 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5914 5919 ))
5915 5920
5916 5921 norepo = ("clone init version help debugcommands debugcomplete"
5917 5922 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
5918 5923 " debugknown debuggetbundle debugbundle")
5919 5924 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
5920 5925 " debugdata debugindex debugindexdot debugrevlog")
5921 5926 inferrepo = ("add addremove annotate cat commit diff grep forget log parents"
5922 5927 " remove resolve status debugwalk")
@@ -1,178 +1,178 b''
1 1 $ hg init t
2 2 $ cd t
3 3 $ echo import > port
4 4 $ hg add port
5 5 $ hg commit -m 0 -u spam -d '0 0'
6 6 $ echo export >> port
7 7 $ hg commit -m 1 -u eggs -d '1 0'
8 8 $ echo export > port
9 9 $ echo vaportight >> port
10 10 $ echo 'import/export' >> port
11 11 $ hg commit -m 2 -u spam -d '2 0'
12 12 $ echo 'import/export' >> port
13 13 $ hg commit -m 3 -u eggs -d '3 0'
14 14 $ head -n 3 port > port1
15 15 $ mv port1 port
16 16 $ hg commit -m 4 -u spam -d '4 0'
17 17
18 18 pattern error
19 19
20 20 $ hg grep '**test**'
21 21 grep: invalid match pattern: nothing to repeat
22 22 [1]
23 23
24 24 simple
25 25
26 26 $ hg grep port port
27 27 port:4:export
28 28 port:4:vaportight
29 29 port:4:import/export
30 30
31 31 simple with color
32 32
33 33 $ hg --config extensions.color= grep --config color.mode=ansi \
34 34 > --color=always port port
35 port:4:ex\x1b[0;31;1mport\x1b[0m (esc)
36 port:4:va\x1b[0;31;1mport\x1b[0might (esc)
37 port:4:im\x1b[0;31;1mport\x1b[0m/export (esc)
35 \x1b[0;35mport\x1b[0m\x1b[0;36m:\x1b[0m\x1b[0;32m4\x1b[0m\x1b[0;36m:\x1b[0mex\x1b[0;31;1mport\x1b[0m (esc)
36 \x1b[0;35mport\x1b[0m\x1b[0;36m:\x1b[0m\x1b[0;32m4\x1b[0m\x1b[0;36m:\x1b[0mva\x1b[0;31;1mport\x1b[0might (esc)
37 \x1b[0;35mport\x1b[0m\x1b[0;36m:\x1b[0m\x1b[0;32m4\x1b[0m\x1b[0;36m:\x1b[0mim\x1b[0;31;1mport\x1b[0m/export (esc)
38 38
39 39 all
40 40
41 41 $ hg grep --traceback --all -nu port port
42 42 port:4:4:-:spam:import/export
43 43 port:3:4:+:eggs:import/export
44 44 port:2:1:-:spam:import
45 45 port:2:2:-:spam:export
46 46 port:2:1:+:spam:export
47 47 port:2:2:+:spam:vaportight
48 48 port:2:3:+:spam:import/export
49 49 port:1:2:+:eggs:export
50 50 port:0:1:+:spam:import
51 51
52 52 other
53 53
54 54 $ hg grep -l port port
55 55 port:4
56 56 $ hg grep import port
57 57 port:4:import/export
58 58
59 59 $ hg cp port port2
60 60 $ hg commit -m 4 -u spam -d '5 0'
61 61
62 62 follow
63 63
64 64 $ hg grep --traceback -f 'import\n\Z' port2
65 65 port:0:import
66 66
67 67 $ echo deport >> port2
68 68 $ hg commit -m 5 -u eggs -d '6 0'
69 69 $ hg grep -f --all -nu port port2
70 70 port2:6:4:+:eggs:deport
71 71 port:4:4:-:spam:import/export
72 72 port:3:4:+:eggs:import/export
73 73 port:2:1:-:spam:import
74 74 port:2:2:-:spam:export
75 75 port:2:1:+:spam:export
76 76 port:2:2:+:spam:vaportight
77 77 port:2:3:+:spam:import/export
78 78 port:1:2:+:eggs:export
79 79 port:0:1:+:spam:import
80 80
81 81 $ cd ..
82 82 $ hg init t2
83 83 $ cd t2
84 84 $ hg grep foobar foo
85 85 [1]
86 86 $ hg grep foobar
87 87 [1]
88 88 $ echo blue >> color
89 89 $ echo black >> color
90 90 $ hg add color
91 91 $ hg ci -m 0
92 92 $ echo orange >> color
93 93 $ hg ci -m 1
94 94 $ echo black > color
95 95 $ hg ci -m 2
96 96 $ echo orange >> color
97 97 $ echo blue >> color
98 98 $ hg ci -m 3
99 99 $ hg grep orange
100 100 color:3:orange
101 101 $ hg grep --all orange
102 102 color:3:+:orange
103 103 color:2:-:orange
104 104 color:1:+:orange
105 105
106 106
107 107 match in last "line" without newline
108 108
109 109 $ python -c 'fp = open("noeol", "wb"); fp.write("no infinite loop"); fp.close();'
110 110 $ hg ci -Amnoeol
111 111 adding noeol
112 112 $ hg grep loop
113 113 noeol:4:no infinite loop
114 114
115 115 $ cd ..
116 116
117 117 Issue685: trackback in grep -r after rename
118 118
119 119 Got a traceback when using grep on a single
120 120 revision with renamed files.
121 121
122 122 $ hg init issue685
123 123 $ cd issue685
124 124 $ echo octarine > color
125 125 $ hg ci -Amcolor
126 126 adding color
127 127 $ hg rename color colour
128 128 $ hg ci -Am rename
129 129 $ hg grep octarine
130 130 colour:1:octarine
131 131 color:0:octarine
132 132
133 133 Used to crash here
134 134
135 135 $ hg grep -r 1 octarine
136 136 colour:1:octarine
137 137 $ cd ..
138 138
139 139
140 140 Issue337: test that grep follows parent-child relationships instead
141 141 of just using revision numbers.
142 142
143 143 $ hg init issue337
144 144 $ cd issue337
145 145
146 146 $ echo white > color
147 147 $ hg commit -A -m "0 white"
148 148 adding color
149 149
150 150 $ echo red > color
151 151 $ hg commit -A -m "1 red"
152 152
153 153 $ hg update 0
154 154 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
155 155 $ echo black > color
156 156 $ hg commit -A -m "2 black"
157 157 created new head
158 158
159 159 $ hg update --clean 1
160 160 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
161 161 $ echo blue > color
162 162 $ hg commit -A -m "3 blue"
163 163
164 164 $ hg grep --all red
165 165 color:3:-:red
166 166 color:1:+:red
167 167
168 168 $ cd ..
169 169
170 170 $ hg init a
171 171 $ cd a
172 172 $ cp "$TESTDIR/binfile.bin" .
173 173 $ hg add binfile.bin
174 174 $ hg ci -m 'add binfile.bin'
175 175 $ hg grep "MaCam" --all
176 176 binfile.bin:0:+: Binary file matches
177 177
178 178 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now