##// END OF EJS Templates
Add --date support to update and revert...
Matt Mackall -
r3814:120be84f default
parent child Browse files
Show More
@@ -1,749 +1,768 b''
1 1 # cmdutil.py - help for command processing in mercurial
2 2 #
3 3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from demandload import demandload
9 9 from node import *
10 10 from i18n import gettext as _
11 11 demandload(globals(), 'os sys')
12 12 demandload(globals(), 'mdiff util templater patch')
13 13
14 14 revrangesep = ':'
15 15
16 16 def revpair(repo, revs):
17 17 '''return pair of nodes, given list of revisions. second item can
18 18 be None, meaning use working dir.'''
19 19
20 20 def revfix(repo, val, defval):
21 21 if not val and val != 0:
22 22 val = defval
23 23 return repo.lookup(val)
24 24
25 25 if not revs:
26 26 return repo.dirstate.parents()[0], None
27 27 end = None
28 28 if len(revs) == 1:
29 29 if revrangesep in revs[0]:
30 30 start, end = revs[0].split(revrangesep, 1)
31 31 start = revfix(repo, start, 0)
32 32 end = revfix(repo, end, repo.changelog.count() - 1)
33 33 else:
34 34 start = revfix(repo, revs[0], None)
35 35 elif len(revs) == 2:
36 36 if revrangesep in revs[0] or revrangesep in revs[1]:
37 37 raise util.Abort(_('too many revisions specified'))
38 38 start = revfix(repo, revs[0], None)
39 39 end = revfix(repo, revs[1], None)
40 40 else:
41 41 raise util.Abort(_('too many revisions specified'))
42 42 return start, end
43 43
44 44 def revrange(repo, revs):
45 45 """Yield revision as strings from a list of revision specifications."""
46 46
47 47 def revfix(repo, val, defval):
48 48 if not val and val != 0 and defval is not None:
49 49 return defval
50 50 return repo.changelog.rev(repo.lookup(val))
51 51
52 52 seen, l = {}, []
53 53 for spec in revs:
54 54 if revrangesep in spec:
55 55 start, end = spec.split(revrangesep, 1)
56 56 start = revfix(repo, start, 0)
57 57 end = revfix(repo, end, repo.changelog.count() - 1)
58 58 step = start > end and -1 or 1
59 59 for rev in xrange(start, end+step, step):
60 60 if rev in seen:
61 61 continue
62 62 seen[rev] = 1
63 63 l.append(rev)
64 64 else:
65 65 rev = revfix(repo, spec, None)
66 66 if rev in seen:
67 67 continue
68 68 seen[rev] = 1
69 69 l.append(rev)
70 70
71 71 return l
72 72
73 73 def make_filename(repo, pat, node,
74 74 total=None, seqno=None, revwidth=None, pathname=None):
75 75 node_expander = {
76 76 'H': lambda: hex(node),
77 77 'R': lambda: str(repo.changelog.rev(node)),
78 78 'h': lambda: short(node),
79 79 }
80 80 expander = {
81 81 '%': lambda: '%',
82 82 'b': lambda: os.path.basename(repo.root),
83 83 }
84 84
85 85 try:
86 86 if node:
87 87 expander.update(node_expander)
88 88 if node and revwidth is not None:
89 89 expander['r'] = (lambda:
90 90 str(repo.changelog.rev(node)).zfill(revwidth))
91 91 if total is not None:
92 92 expander['N'] = lambda: str(total)
93 93 if seqno is not None:
94 94 expander['n'] = lambda: str(seqno)
95 95 if total is not None and seqno is not None:
96 96 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
97 97 if pathname is not None:
98 98 expander['s'] = lambda: os.path.basename(pathname)
99 99 expander['d'] = lambda: os.path.dirname(pathname) or '.'
100 100 expander['p'] = lambda: pathname
101 101
102 102 newname = []
103 103 patlen = len(pat)
104 104 i = 0
105 105 while i < patlen:
106 106 c = pat[i]
107 107 if c == '%':
108 108 i += 1
109 109 c = pat[i]
110 110 c = expander[c]()
111 111 newname.append(c)
112 112 i += 1
113 113 return ''.join(newname)
114 114 except KeyError, inst:
115 115 raise util.Abort(_("invalid format spec '%%%s' in output file name") %
116 116 inst.args[0])
117 117
118 118 def make_file(repo, pat, node=None,
119 119 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
120 120 if not pat or pat == '-':
121 121 return 'w' in mode and sys.stdout or sys.stdin
122 122 if hasattr(pat, 'write') and 'w' in mode:
123 123 return pat
124 124 if hasattr(pat, 'read') and 'r' in mode:
125 125 return pat
126 126 return open(make_filename(repo, pat, node, total, seqno, revwidth,
127 127 pathname),
128 128 mode)
129 129
130 130 def matchpats(repo, pats=[], opts={}, head=''):
131 131 cwd = repo.getcwd()
132 132 if not pats and cwd:
133 133 opts['include'] = [os.path.join(cwd, i)
134 134 for i in opts.get('include', [])]
135 135 opts['exclude'] = [os.path.join(cwd, x)
136 136 for x in opts.get('exclude', [])]
137 137 cwd = ''
138 138 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
139 139 opts.get('exclude'), head)
140 140
141 141 def walk(repo, pats=[], opts={}, node=None, head='', badmatch=None):
142 142 files, matchfn, anypats = matchpats(repo, pats, opts, head)
143 143 exact = dict.fromkeys(files)
144 144 for src, fn in repo.walk(node=node, files=files, match=matchfn,
145 145 badmatch=badmatch):
146 146 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact
147 147
148 148 def findrenames(repo, added=None, removed=None, threshold=0.5):
149 149 if added is None or removed is None:
150 150 added, removed = repo.status()[1:3]
151 151 changes = repo.changelog.read(repo.dirstate.parents()[0])
152 152 mf = repo.manifest.read(changes[0])
153 153 for a in added:
154 154 aa = repo.wread(a)
155 155 bestscore, bestname = None, None
156 156 for r in removed:
157 157 rr = repo.file(r).read(mf[r])
158 158 delta = mdiff.textdiff(aa, rr)
159 159 if len(delta) < len(aa):
160 160 myscore = 1.0 - (float(len(delta)) / len(aa))
161 161 if bestscore is None or myscore > bestscore:
162 162 bestscore, bestname = myscore, r
163 163 if bestname and bestscore >= threshold:
164 164 yield bestname, a, bestscore
165 165
166 166 def addremove(repo, pats=[], opts={}, wlock=None, dry_run=None,
167 167 similarity=None):
168 168 if dry_run is None:
169 169 dry_run = opts.get('dry_run')
170 170 if similarity is None:
171 171 similarity = float(opts.get('similarity') or 0)
172 172 add, remove = [], []
173 173 mapping = {}
174 174 for src, abs, rel, exact in walk(repo, pats, opts):
175 175 if src == 'f' and repo.dirstate.state(abs) == '?':
176 176 add.append(abs)
177 177 mapping[abs] = rel, exact
178 178 if repo.ui.verbose or not exact:
179 179 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
180 180 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
181 181 remove.append(abs)
182 182 mapping[abs] = rel, exact
183 183 if repo.ui.verbose or not exact:
184 184 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
185 185 if not dry_run:
186 186 repo.add(add, wlock=wlock)
187 187 repo.remove(remove, wlock=wlock)
188 188 if similarity > 0:
189 189 for old, new, score in findrenames(repo, add, remove, similarity):
190 190 oldrel, oldexact = mapping[old]
191 191 newrel, newexact = mapping[new]
192 192 if repo.ui.verbose or not oldexact or not newexact:
193 193 repo.ui.status(_('recording removal of %s as rename to %s '
194 194 '(%d%% similar)\n') %
195 195 (oldrel, newrel, score * 100))
196 196 if not dry_run:
197 197 repo.copy(old, new, wlock=wlock)
198 198
199 199 class changeset_printer(object):
200 200 '''show changeset information when templating not requested.'''
201 201
202 202 def __init__(self, ui, repo, patch, brinfo, buffered):
203 203 self.ui = ui
204 204 self.repo = repo
205 205 self.buffered = buffered
206 206 self.patch = patch
207 207 self.brinfo = brinfo
208 208 self.header = {}
209 209 self.hunk = {}
210 210 self.lastheader = None
211 211
212 212 def flush(self, rev):
213 213 if rev in self.header:
214 214 h = self.header[rev]
215 215 if h != self.lastheader:
216 216 self.lastheader = h
217 217 self.ui.write(h)
218 218 del self.header[rev]
219 219 if rev in self.hunk:
220 220 self.ui.write(self.hunk[rev])
221 221 del self.hunk[rev]
222 222 return 1
223 223 return 0
224 224
225 225 def show(self, rev=0, changenode=None, copies=None, **props):
226 226 if self.buffered:
227 227 self.ui.pushbuffer()
228 228 self._show(rev, changenode, copies, props)
229 229 self.hunk[rev] = self.ui.popbuffer()
230 230 else:
231 231 self._show(rev, changenode, copies, props)
232 232
233 233 def _show(self, rev, changenode, copies, props):
234 234 '''show a single changeset or file revision'''
235 235 log = self.repo.changelog
236 236 if changenode is None:
237 237 changenode = log.node(rev)
238 238 elif not rev:
239 239 rev = log.rev(changenode)
240 240
241 241 if self.ui.quiet:
242 242 self.ui.write("%d:%s\n" % (rev, short(changenode)))
243 243 return
244 244
245 245 changes = log.read(changenode)
246 246 date = util.datestr(changes[2])
247 247 extra = changes[5]
248 248 branch = extra.get("branch")
249 249
250 250 hexfunc = self.ui.debugflag and hex or short
251 251
252 252 parents = log.parentrevs(rev)
253 253 if not self.ui.debugflag:
254 254 if parents[1] == nullrev:
255 255 if parents[0] >= rev - 1:
256 256 parents = []
257 257 else:
258 258 parents = [parents[0]]
259 259 parents = [(p, hexfunc(log.node(p))) for p in parents]
260 260
261 261 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
262 262
263 263 if branch:
264 264 self.ui.write(_("branch: %s\n") % branch)
265 265 for tag in self.repo.nodetags(changenode):
266 266 self.ui.write(_("tag: %s\n") % tag)
267 267 for parent in parents:
268 268 self.ui.write(_("parent: %d:%s\n") % parent)
269 269
270 270 if self.brinfo:
271 271 br = self.repo.branchlookup([changenode])
272 272 if br:
273 273 self.ui.write(_("branch: %s\n") % " ".join(br[changenode]))
274 274
275 275 if self.ui.debugflag:
276 276 self.ui.write(_("manifest: %d:%s\n") %
277 277 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
278 278 self.ui.write(_("user: %s\n") % changes[1])
279 279 self.ui.write(_("date: %s\n") % date)
280 280
281 281 if self.ui.debugflag:
282 282 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
283 283 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
284 284 files):
285 285 if value:
286 286 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
287 287 elif changes[3] and self.ui.verbose:
288 288 self.ui.write(_("files: %s\n") % " ".join(changes[3]))
289 289 if copies and self.ui.verbose:
290 290 copies = ['%s (%s)' % c for c in copies]
291 291 self.ui.write(_("copies: %s\n") % ' '.join(copies))
292 292
293 293 if extra and self.ui.debugflag:
294 294 extraitems = extra.items()
295 295 extraitems.sort()
296 296 for key, value in extraitems:
297 297 self.ui.write(_("extra: %s=%s\n")
298 298 % (key, value.encode('string_escape')))
299 299
300 300 description = changes[4].strip()
301 301 if description:
302 302 if self.ui.verbose:
303 303 self.ui.write(_("description:\n"))
304 304 self.ui.write(description)
305 305 self.ui.write("\n\n")
306 306 else:
307 307 self.ui.write(_("summary: %s\n") %
308 308 description.splitlines()[0])
309 309 self.ui.write("\n")
310 310
311 311 self.showpatch(changenode)
312 312
313 313 def showpatch(self, node):
314 314 if self.patch:
315 315 prev = self.repo.changelog.parents(node)[0]
316 316 patch.diff(self.repo, prev, node, fp=self.ui)
317 317 self.ui.write("\n")
318 318
319 319 class changeset_templater(changeset_printer):
320 320 '''format changeset information.'''
321 321
322 322 def __init__(self, ui, repo, patch, brinfo, mapfile, buffered):
323 323 changeset_printer.__init__(self, ui, repo, patch, brinfo, buffered)
324 324 self.t = templater.templater(mapfile, templater.common_filters,
325 325 cache={'parent': '{rev}:{node|short} ',
326 326 'manifest': '{rev}:{node|short}',
327 327 'filecopy': '{name} ({source})'})
328 328
329 329 def use_template(self, t):
330 330 '''set template string to use'''
331 331 self.t.cache['changeset'] = t
332 332
333 333 def _show(self, rev, changenode, copies, props):
334 334 '''show a single changeset or file revision'''
335 335 log = self.repo.changelog
336 336 if changenode is None:
337 337 changenode = log.node(rev)
338 338 elif not rev:
339 339 rev = log.rev(changenode)
340 340
341 341 changes = log.read(changenode)
342 342
343 343 def showlist(name, values, plural=None, **args):
344 344 '''expand set of values.
345 345 name is name of key in template map.
346 346 values is list of strings or dicts.
347 347 plural is plural of name, if not simply name + 's'.
348 348
349 349 expansion works like this, given name 'foo'.
350 350
351 351 if values is empty, expand 'no_foos'.
352 352
353 353 if 'foo' not in template map, return values as a string,
354 354 joined by space.
355 355
356 356 expand 'start_foos'.
357 357
358 358 for each value, expand 'foo'. if 'last_foo' in template
359 359 map, expand it instead of 'foo' for last key.
360 360
361 361 expand 'end_foos'.
362 362 '''
363 363 if plural: names = plural
364 364 else: names = name + 's'
365 365 if not values:
366 366 noname = 'no_' + names
367 367 if noname in self.t:
368 368 yield self.t(noname, **args)
369 369 return
370 370 if name not in self.t:
371 371 if isinstance(values[0], str):
372 372 yield ' '.join(values)
373 373 else:
374 374 for v in values:
375 375 yield dict(v, **args)
376 376 return
377 377 startname = 'start_' + names
378 378 if startname in self.t:
379 379 yield self.t(startname, **args)
380 380 vargs = args.copy()
381 381 def one(v, tag=name):
382 382 try:
383 383 vargs.update(v)
384 384 except (AttributeError, ValueError):
385 385 try:
386 386 for a, b in v:
387 387 vargs[a] = b
388 388 except ValueError:
389 389 vargs[name] = v
390 390 return self.t(tag, **vargs)
391 391 lastname = 'last_' + name
392 392 if lastname in self.t:
393 393 last = values.pop()
394 394 else:
395 395 last = None
396 396 for v in values:
397 397 yield one(v)
398 398 if last is not None:
399 399 yield one(last, tag=lastname)
400 400 endname = 'end_' + names
401 401 if endname in self.t:
402 402 yield self.t(endname, **args)
403 403
404 404 def showbranches(**args):
405 405 branch = changes[5].get("branch")
406 406 if branch:
407 407 return showlist('branch', [branch], plural='branches', **args)
408 408 # add old style branches if requested
409 409 if self.brinfo:
410 410 br = self.repo.branchlookup([changenode])
411 411 if changenode in br:
412 412 return showlist('branch', br[changenode],
413 413 plural='branches', **args)
414 414
415 415 def showparents(**args):
416 416 parents = [[('rev', log.rev(p)), ('node', hex(p))]
417 417 for p in log.parents(changenode)
418 418 if self.ui.debugflag or p != nullid]
419 419 if (not self.ui.debugflag and len(parents) == 1 and
420 420 parents[0][0][1] == rev - 1):
421 421 return
422 422 return showlist('parent', parents, **args)
423 423
424 424 def showtags(**args):
425 425 return showlist('tag', self.repo.nodetags(changenode), **args)
426 426
427 427 def showextras(**args):
428 428 extras = changes[5].items()
429 429 extras.sort()
430 430 for key, value in extras:
431 431 args = args.copy()
432 432 args.update(dict(key=key, value=value))
433 433 yield self.t('extra', **args)
434 434
435 435 def showcopies(**args):
436 436 c = [{'name': x[0], 'source': x[1]} for x in copies]
437 437 return showlist('file_copy', c, plural='file_copies', **args)
438 438
439 439 if self.ui.debugflag:
440 440 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
441 441 def showfiles(**args):
442 442 return showlist('file', files[0], **args)
443 443 def showadds(**args):
444 444 return showlist('file_add', files[1], **args)
445 445 def showdels(**args):
446 446 return showlist('file_del', files[2], **args)
447 447 def showmanifest(**args):
448 448 args = args.copy()
449 449 args.update(dict(rev=self.repo.manifest.rev(changes[0]),
450 450 node=hex(changes[0])))
451 451 return self.t('manifest', **args)
452 452 else:
453 453 def showfiles(**args):
454 454 return showlist('file', changes[3], **args)
455 455 showadds = ''
456 456 showdels = ''
457 457 showmanifest = ''
458 458
459 459 defprops = {
460 460 'author': changes[1],
461 461 'branches': showbranches,
462 462 'date': changes[2],
463 463 'desc': changes[4],
464 464 'file_adds': showadds,
465 465 'file_dels': showdels,
466 466 'files': showfiles,
467 467 'file_copies': showcopies,
468 468 'manifest': showmanifest,
469 469 'node': hex(changenode),
470 470 'parents': showparents,
471 471 'rev': rev,
472 472 'tags': showtags,
473 473 'extras': showextras,
474 474 }
475 475 props = props.copy()
476 476 props.update(defprops)
477 477
478 478 try:
479 479 if self.ui.debugflag and 'header_debug' in self.t:
480 480 key = 'header_debug'
481 481 elif self.ui.quiet and 'header_quiet' in self.t:
482 482 key = 'header_quiet'
483 483 elif self.ui.verbose and 'header_verbose' in self.t:
484 484 key = 'header_verbose'
485 485 elif 'header' in self.t:
486 486 key = 'header'
487 487 else:
488 488 key = ''
489 489 if key:
490 490 h = templater.stringify(self.t(key, **props))
491 491 if self.buffered:
492 492 self.header[rev] = h
493 493 else:
494 494 self.ui.write(h)
495 495 if self.ui.debugflag and 'changeset_debug' in self.t:
496 496 key = 'changeset_debug'
497 497 elif self.ui.quiet and 'changeset_quiet' in self.t:
498 498 key = 'changeset_quiet'
499 499 elif self.ui.verbose and 'changeset_verbose' in self.t:
500 500 key = 'changeset_verbose'
501 501 else:
502 502 key = 'changeset'
503 503 self.ui.write(templater.stringify(self.t(key, **props)))
504 504 self.showpatch(changenode)
505 505 except KeyError, inst:
506 506 raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
507 507 inst.args[0]))
508 508 except SyntaxError, inst:
509 509 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
510 510
511 511 def show_changeset(ui, repo, opts, buffered=False):
512 512 """show one changeset using template or regular display.
513 513
514 514 Display format will be the first non-empty hit of:
515 515 1. option 'template'
516 516 2. option 'style'
517 517 3. [ui] setting 'logtemplate'
518 518 4. [ui] setting 'style'
519 519 If all of these values are either the unset or the empty string,
520 520 regular display via changeset_printer() is done.
521 521 """
522 522 # options
523 523 patch = opts.get('patch')
524 524 br = None
525 525 if opts.get('branches'):
526 526 ui.warn(_("the --branches option is deprecated, "
527 527 "please use 'hg branches' instead\n"))
528 528 br = True
529 529 tmpl = opts.get('template')
530 530 mapfile = None
531 531 if tmpl:
532 532 tmpl = templater.parsestring(tmpl, quoted=False)
533 533 else:
534 534 mapfile = opts.get('style')
535 535 # ui settings
536 536 if not mapfile:
537 537 tmpl = ui.config('ui', 'logtemplate')
538 538 if tmpl:
539 539 tmpl = templater.parsestring(tmpl)
540 540 else:
541 541 mapfile = ui.config('ui', 'style')
542 542
543 543 if tmpl or mapfile:
544 544 if mapfile:
545 545 if not os.path.split(mapfile)[0]:
546 546 mapname = (templater.templatepath('map-cmdline.' + mapfile)
547 547 or templater.templatepath(mapfile))
548 548 if mapname: mapfile = mapname
549 549 try:
550 550 t = changeset_templater(ui, repo, patch, br, mapfile, buffered)
551 551 except SyntaxError, inst:
552 552 raise util.Abort(inst.args[0])
553 553 if tmpl: t.use_template(tmpl)
554 554 return t
555 555 return changeset_printer(ui, repo, patch, br, buffered)
556 556
557 def finddate(ui, repo, date):
558 """Find the tipmost changeset that matches the given date spec"""
559 df = util.matchdate(date + " to " + date)
560 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
561 changeiter, matchfn = walkchangerevs(ui, repo, [], get, {'rev':None})
562 results = {}
563 for st, rev, fns in changeiter:
564 if st == 'add':
565 d = get(rev)[2]
566 if df(d[0]):
567 results[rev] = d
568 elif st == 'iter':
569 if rev in results:
570 ui.status("Found revision %s from %s\n" %
571 (rev, util.datestr(results[rev])))
572 return str(rev)
573
574 raise util.Abort(_("revision matching date not found"))
575
557 576 def walkchangerevs(ui, repo, pats, change, opts):
558 577 '''Iterate over files and the revs they changed in.
559 578
560 579 Callers most commonly need to iterate backwards over the history
561 580 it is interested in. Doing so has awful (quadratic-looking)
562 581 performance, so we use iterators in a "windowed" way.
563 582
564 583 We walk a window of revisions in the desired order. Within the
565 584 window, we first walk forwards to gather data, then in the desired
566 585 order (usually backwards) to display it.
567 586
568 587 This function returns an (iterator, matchfn) tuple. The iterator
569 588 yields 3-tuples. They will be of one of the following forms:
570 589
571 590 "window", incrementing, lastrev: stepping through a window,
572 591 positive if walking forwards through revs, last rev in the
573 592 sequence iterated over - use to reset state for the current window
574 593
575 594 "add", rev, fns: out-of-order traversal of the given file names
576 595 fns, which changed during revision rev - use to gather data for
577 596 possible display
578 597
579 598 "iter", rev, None: in-order traversal of the revs earlier iterated
580 599 over with "add" - use to display data'''
581 600
582 601 def increasing_windows(start, end, windowsize=8, sizelimit=512):
583 602 if start < end:
584 603 while start < end:
585 604 yield start, min(windowsize, end-start)
586 605 start += windowsize
587 606 if windowsize < sizelimit:
588 607 windowsize *= 2
589 608 else:
590 609 while start > end:
591 610 yield start, min(windowsize, start-end-1)
592 611 start -= windowsize
593 612 if windowsize < sizelimit:
594 613 windowsize *= 2
595 614
596 615 files, matchfn, anypats = matchpats(repo, pats, opts)
597 616 follow = opts.get('follow') or opts.get('follow_first')
598 617
599 618 if repo.changelog.count() == 0:
600 619 return [], matchfn
601 620
602 621 if follow:
603 622 defrange = '%s:0' % repo.changectx().rev()
604 623 else:
605 624 defrange = 'tip:0'
606 625 revs = revrange(repo, opts['rev'] or [defrange])
607 626 wanted = {}
608 627 slowpath = anypats or opts.get('removed')
609 628 fncache = {}
610 629
611 630 if not slowpath and not files:
612 631 # No files, no patterns. Display all revs.
613 632 wanted = dict.fromkeys(revs)
614 633 copies = []
615 634 if not slowpath:
616 635 # Only files, no patterns. Check the history of each file.
617 636 def filerevgen(filelog, node):
618 637 cl_count = repo.changelog.count()
619 638 if node is None:
620 639 last = filelog.count() - 1
621 640 else:
622 641 last = filelog.rev(node)
623 642 for i, window in increasing_windows(last, nullrev):
624 643 revs = []
625 644 for j in xrange(i - window, i + 1):
626 645 n = filelog.node(j)
627 646 revs.append((filelog.linkrev(n),
628 647 follow and filelog.renamed(n)))
629 648 revs.reverse()
630 649 for rev in revs:
631 650 # only yield rev for which we have the changelog, it can
632 651 # happen while doing "hg log" during a pull or commit
633 652 if rev[0] < cl_count:
634 653 yield rev
635 654 def iterfiles():
636 655 for filename in files:
637 656 yield filename, None
638 657 for filename_node in copies:
639 658 yield filename_node
640 659 minrev, maxrev = min(revs), max(revs)
641 660 for file_, node in iterfiles():
642 661 filelog = repo.file(file_)
643 662 # A zero count may be a directory or deleted file, so
644 663 # try to find matching entries on the slow path.
645 664 if filelog.count() == 0:
646 665 slowpath = True
647 666 break
648 667 for rev, copied in filerevgen(filelog, node):
649 668 if rev <= maxrev:
650 669 if rev < minrev:
651 670 break
652 671 fncache.setdefault(rev, [])
653 672 fncache[rev].append(file_)
654 673 wanted[rev] = 1
655 674 if follow and copied:
656 675 copies.append(copied)
657 676 if slowpath:
658 677 if follow:
659 678 raise util.Abort(_('can only follow copies/renames for explicit '
660 679 'file names'))
661 680
662 681 # The slow path checks files modified in every changeset.
663 682 def changerevgen():
664 683 for i, window in increasing_windows(repo.changelog.count()-1,
665 684 nullrev):
666 685 for j in xrange(i - window, i + 1):
667 686 yield j, change(j)[3]
668 687
669 688 for rev, changefiles in changerevgen():
670 689 matches = filter(matchfn, changefiles)
671 690 if matches:
672 691 fncache[rev] = matches
673 692 wanted[rev] = 1
674 693
675 694 class followfilter:
676 695 def __init__(self, onlyfirst=False):
677 696 self.startrev = nullrev
678 697 self.roots = []
679 698 self.onlyfirst = onlyfirst
680 699
681 700 def match(self, rev):
682 701 def realparents(rev):
683 702 if self.onlyfirst:
684 703 return repo.changelog.parentrevs(rev)[0:1]
685 704 else:
686 705 return filter(lambda x: x != nullrev,
687 706 repo.changelog.parentrevs(rev))
688 707
689 708 if self.startrev == nullrev:
690 709 self.startrev = rev
691 710 return True
692 711
693 712 if rev > self.startrev:
694 713 # forward: all descendants
695 714 if not self.roots:
696 715 self.roots.append(self.startrev)
697 716 for parent in realparents(rev):
698 717 if parent in self.roots:
699 718 self.roots.append(rev)
700 719 return True
701 720 else:
702 721 # backwards: all parents
703 722 if not self.roots:
704 723 self.roots.extend(realparents(self.startrev))
705 724 if rev in self.roots:
706 725 self.roots.remove(rev)
707 726 self.roots.extend(realparents(rev))
708 727 return True
709 728
710 729 return False
711 730
712 731 # it might be worthwhile to do this in the iterator if the rev range
713 732 # is descending and the prune args are all within that range
714 733 for rev in opts.get('prune', ()):
715 734 rev = repo.changelog.rev(repo.lookup(rev))
716 735 ff = followfilter()
717 736 stop = min(revs[0], revs[-1])
718 737 for x in xrange(rev, stop-1, -1):
719 738 if ff.match(x) and x in wanted:
720 739 del wanted[x]
721 740
722 741 def iterate():
723 742 if follow and not files:
724 743 ff = followfilter(onlyfirst=opts.get('follow_first'))
725 744 def want(rev):
726 745 if ff.match(rev) and rev in wanted:
727 746 return True
728 747 return False
729 748 else:
730 749 def want(rev):
731 750 return rev in wanted
732 751
733 752 for i, window in increasing_windows(0, len(revs)):
734 753 yield 'window', revs[0] < revs[-1], revs[-1]
735 754 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
736 755 srevs = list(nrevs)
737 756 srevs.sort()
738 757 for rev in srevs:
739 758 fns = fncache.get(rev)
740 759 if not fns:
741 760 def fns_generator():
742 761 for f in change(rev)[3]:
743 762 if matchfn(f):
744 763 yield f
745 764 fns = fns_generator()
746 765 yield 'add', rev, fns
747 766 for rev in nrevs:
748 767 yield 'iter', rev, None
749 768 return iterate(), matchfn
@@ -1,3133 +1,3146 b''
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from demandload import demandload
9 9 from node import *
10 10 from i18n import gettext as _
11 11 demandload(globals(), "bisect os re sys signal imp urllib pdb shlex stat")
12 12 demandload(globals(), "fancyopts ui hg util lock revlog bundlerepo")
13 13 demandload(globals(), "difflib patch time help")
14 14 demandload(globals(), "traceback errno version atexit")
15 15 demandload(globals(), "archival changegroup cmdutil hgweb.server sshserver")
16 16
17 17 class UnknownCommand(Exception):
18 18 """Exception raised if command is not in the command table."""
19 19 class AmbiguousCommand(Exception):
20 20 """Exception raised if command shortcut matches more than one command."""
21 21
22 22 def bail_if_changed(repo):
23 23 modified, added, removed, deleted = repo.status()[:4]
24 24 if modified or added or removed or deleted:
25 25 raise util.Abort(_("outstanding uncommitted changes"))
26 26
27 27 def logmessage(opts):
28 28 """ get the log message according to -m and -l option """
29 29 message = opts['message']
30 30 logfile = opts['logfile']
31 31
32 32 if message and logfile:
33 33 raise util.Abort(_('options --message and --logfile are mutually '
34 34 'exclusive'))
35 35 if not message and logfile:
36 36 try:
37 37 if logfile == '-':
38 38 message = sys.stdin.read()
39 39 else:
40 40 message = open(logfile).read()
41 41 except IOError, inst:
42 42 raise util.Abort(_("can't read commit message '%s': %s") %
43 43 (logfile, inst.strerror))
44 44 return message
45 45
46 46 def setremoteconfig(ui, opts):
47 47 "copy remote options to ui tree"
48 48 if opts.get('ssh'):
49 49 ui.setconfig("ui", "ssh", opts['ssh'])
50 50 if opts.get('remotecmd'):
51 51 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
52 52
53 53 # Commands start here, listed alphabetically
54 54
55 55 def add(ui, repo, *pats, **opts):
56 56 """add the specified files on the next commit
57 57
58 58 Schedule files to be version controlled and added to the repository.
59 59
60 60 The files will be added to the repository at the next commit.
61 61
62 62 If no names are given, add all files in the repository.
63 63 """
64 64
65 65 names = []
66 66 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
67 67 if exact:
68 68 if ui.verbose:
69 69 ui.status(_('adding %s\n') % rel)
70 70 names.append(abs)
71 71 elif repo.dirstate.state(abs) == '?':
72 72 ui.status(_('adding %s\n') % rel)
73 73 names.append(abs)
74 74 if not opts.get('dry_run'):
75 75 repo.add(names)
76 76
77 77 def addremove(ui, repo, *pats, **opts):
78 78 """add all new files, delete all missing files
79 79
80 80 Add all new files and remove all missing files from the repository.
81 81
82 82 New files are ignored if they match any of the patterns in .hgignore. As
83 83 with add, these changes take effect at the next commit.
84 84
85 85 Use the -s option to detect renamed files. With a parameter > 0,
86 86 this compares every removed file with every added file and records
87 87 those similar enough as renames. This option takes a percentage
88 88 between 0 (disabled) and 100 (files must be identical) as its
89 89 parameter. Detecting renamed files this way can be expensive.
90 90 """
91 91 sim = float(opts.get('similarity') or 0)
92 92 if sim < 0 or sim > 100:
93 93 raise util.Abort(_('similarity must be between 0 and 100'))
94 94 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
95 95
96 96 def annotate(ui, repo, *pats, **opts):
97 97 """show changeset information per file line
98 98
99 99 List changes in files, showing the revision id responsible for each line
100 100
101 101 This command is useful to discover who did a change or when a change took
102 102 place.
103 103
104 104 Without the -a option, annotate will avoid processing files it
105 105 detects as binary. With -a, annotate will generate an annotation
106 106 anyway, probably with undesirable results.
107 107 """
108 108 getdate = util.cachefunc(lambda x: util.datestr(x.date()))
109 109
110 110 if not pats:
111 111 raise util.Abort(_('at least one file name or pattern required'))
112 112
113 113 opmap = [['user', lambda x: ui.shortuser(x.user())],
114 114 ['number', lambda x: str(x.rev())],
115 115 ['changeset', lambda x: short(x.node())],
116 116 ['date', getdate], ['follow', lambda x: x.path()]]
117 117 if (not opts['user'] and not opts['changeset'] and not opts['date']
118 118 and not opts['follow']):
119 119 opts['number'] = 1
120 120
121 121 ctx = repo.changectx(opts['rev'])
122 122
123 123 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
124 124 node=ctx.node()):
125 125 fctx = ctx.filectx(abs)
126 126 if not opts['text'] and util.binary(fctx.data()):
127 127 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
128 128 continue
129 129
130 130 lines = fctx.annotate(follow=opts.get('follow'))
131 131 pieces = []
132 132
133 133 for o, f in opmap:
134 134 if opts[o]:
135 135 l = [f(n) for n, dummy in lines]
136 136 if l:
137 137 m = max(map(len, l))
138 138 pieces.append(["%*s" % (m, x) for x in l])
139 139
140 140 if pieces:
141 141 for p, l in zip(zip(*pieces), lines):
142 142 ui.write("%s: %s" % (" ".join(p), l[1]))
143 143
144 144 def archive(ui, repo, dest, **opts):
145 145 '''create unversioned archive of a repository revision
146 146
147 147 By default, the revision used is the parent of the working
148 148 directory; use "-r" to specify a different revision.
149 149
150 150 To specify the type of archive to create, use "-t". Valid
151 151 types are:
152 152
153 153 "files" (default): a directory full of files
154 154 "tar": tar archive, uncompressed
155 155 "tbz2": tar archive, compressed using bzip2
156 156 "tgz": tar archive, compressed using gzip
157 157 "uzip": zip archive, uncompressed
158 158 "zip": zip archive, compressed using deflate
159 159
160 160 The exact name of the destination archive or directory is given
161 161 using a format string; see "hg help export" for details.
162 162
163 163 Each member added to an archive file has a directory prefix
164 164 prepended. Use "-p" to specify a format string for the prefix.
165 165 The default is the basename of the archive, with suffixes removed.
166 166 '''
167 167
168 168 node = repo.changectx(opts['rev']).node()
169 169 dest = cmdutil.make_filename(repo, dest, node)
170 170 if os.path.realpath(dest) == repo.root:
171 171 raise util.Abort(_('repository root cannot be destination'))
172 172 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
173 173 kind = opts.get('type') or 'files'
174 174 prefix = opts['prefix']
175 175 if dest == '-':
176 176 if kind == 'files':
177 177 raise util.Abort(_('cannot archive plain files to stdout'))
178 178 dest = sys.stdout
179 179 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
180 180 prefix = cmdutil.make_filename(repo, prefix, node)
181 181 archival.archive(repo, dest, node, kind, not opts['no_decode'],
182 182 matchfn, prefix)
183 183
184 184 def backout(ui, repo, rev, **opts):
185 185 '''reverse effect of earlier changeset
186 186
187 187 Commit the backed out changes as a new changeset. The new
188 188 changeset is a child of the backed out changeset.
189 189
190 190 If you back out a changeset other than the tip, a new head is
191 191 created. This head is the parent of the working directory. If
192 192 you back out an old changeset, your working directory will appear
193 193 old after the backout. You should merge the backout changeset
194 194 with another head.
195 195
196 196 The --merge option remembers the parent of the working directory
197 197 before starting the backout, then merges the new head with that
198 198 changeset afterwards. This saves you from doing the merge by
199 199 hand. The result of this merge is not committed, as for a normal
200 200 merge.'''
201 201
202 202 bail_if_changed(repo)
203 203 op1, op2 = repo.dirstate.parents()
204 204 if op2 != nullid:
205 205 raise util.Abort(_('outstanding uncommitted merge'))
206 206 node = repo.lookup(rev)
207 207 p1, p2 = repo.changelog.parents(node)
208 208 if p1 == nullid:
209 209 raise util.Abort(_('cannot back out a change with no parents'))
210 210 if p2 != nullid:
211 211 if not opts['parent']:
212 212 raise util.Abort(_('cannot back out a merge changeset without '
213 213 '--parent'))
214 214 p = repo.lookup(opts['parent'])
215 215 if p not in (p1, p2):
216 216 raise util.Abort(_('%s is not a parent of %s') %
217 217 (short(p), short(node)))
218 218 parent = p
219 219 else:
220 220 if opts['parent']:
221 221 raise util.Abort(_('cannot use --parent on non-merge changeset'))
222 222 parent = p1
223 223 hg.clean(repo, node, show_stats=False)
224 224 revert_opts = opts.copy()
225 revert_opts['date'] = None
225 226 revert_opts['all'] = True
226 227 revert_opts['rev'] = hex(parent)
227 228 revert(ui, repo, **revert_opts)
228 229 commit_opts = opts.copy()
229 230 commit_opts['addremove'] = False
230 231 if not commit_opts['message'] and not commit_opts['logfile']:
231 232 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
232 233 commit_opts['force_editor'] = True
233 234 commit(ui, repo, **commit_opts)
234 235 def nice(node):
235 236 return '%d:%s' % (repo.changelog.rev(node), short(node))
236 237 ui.status(_('changeset %s backs out changeset %s\n') %
237 238 (nice(repo.changelog.tip()), nice(node)))
238 239 if op1 != node:
239 240 if opts['merge']:
240 241 ui.status(_('merging with changeset %s\n') % nice(op1))
241 242 n = _lookup(repo, hex(op1))
242 243 hg.merge(repo, n)
243 244 else:
244 245 ui.status(_('the backout changeset is a new head - '
245 246 'do not forget to merge\n'))
246 247 ui.status(_('(use "backout --merge" '
247 248 'if you want to auto-merge)\n'))
248 249
249 250 def branch(ui, repo, label=None):
250 251 """set or show the current branch name
251 252
252 253 With <name>, set the current branch name. Otherwise, show the
253 254 current branch name.
254 255 """
255 256
256 257 if label is not None:
257 258 repo.opener("branch", "w").write(label)
258 259 else:
259 260 b = repo.workingctx().branch()
260 261 if b:
261 262 ui.write("%s\n" % b)
262 263
263 264 def branches(ui, repo):
264 265 """list repository named branches
265 266
266 267 List the repository's named branches.
267 268 """
268 269 b = repo.branchtags()
269 270 l = [(-repo.changelog.rev(n), n, t) for t, n in b.items()]
270 271 l.sort()
271 272 for r, n, t in l:
272 273 hexfunc = ui.debugflag and hex or short
273 274 if ui.quiet:
274 275 ui.write("%s\n" % t)
275 276 else:
276 277 t = util.localsub(t, 30)
277 278 t += " " * (30 - util.locallen(t))
278 279 ui.write("%s %s:%s\n" % (t, -r, hexfunc(n)))
279 280
280 281 def bundle(ui, repo, fname, dest=None, **opts):
281 282 """create a changegroup file
282 283
283 284 Generate a compressed changegroup file collecting changesets not
284 285 found in the other repository.
285 286
286 287 If no destination repository is specified the destination is assumed
287 288 to have all the nodes specified by one or more --base parameters.
288 289
289 290 The bundle file can then be transferred using conventional means and
290 291 applied to another repository with the unbundle or pull command.
291 292 This is useful when direct push and pull are not available or when
292 293 exporting an entire repository is undesirable.
293 294
294 295 Applying bundles preserves all changeset contents including
295 296 permissions, copy/rename information, and revision history.
296 297 """
297 298 revs = opts.get('rev') or None
298 299 if revs:
299 300 revs = [repo.lookup(rev) for rev in revs]
300 301 base = opts.get('base')
301 302 if base:
302 303 if dest:
303 304 raise util.Abort(_("--base is incompatible with specifiying "
304 305 "a destination"))
305 306 base = [repo.lookup(rev) for rev in base]
306 307 # create the right base
307 308 # XXX: nodesbetween / changegroup* should be "fixed" instead
308 309 o = []
309 310 has = {nullid: None}
310 311 for n in base:
311 312 has.update(repo.changelog.reachable(n))
312 313 if revs:
313 314 visit = list(revs)
314 315 else:
315 316 visit = repo.changelog.heads()
316 317 seen = {}
317 318 while visit:
318 319 n = visit.pop(0)
319 320 parents = [p for p in repo.changelog.parents(n) if p not in has]
320 321 if len(parents) == 0:
321 322 o.insert(0, n)
322 323 else:
323 324 for p in parents:
324 325 if p not in seen:
325 326 seen[p] = 1
326 327 visit.append(p)
327 328 else:
328 329 setremoteconfig(ui, opts)
329 330 dest = ui.expandpath(dest or 'default-push', dest or 'default')
330 331 other = hg.repository(ui, dest)
331 332 o = repo.findoutgoing(other, force=opts['force'])
332 333
333 334 if revs:
334 335 cg = repo.changegroupsubset(o, revs, 'bundle')
335 336 else:
336 337 cg = repo.changegroup(o, 'bundle')
337 338 changegroup.writebundle(cg, fname, "HG10BZ")
338 339
339 340 def cat(ui, repo, file1, *pats, **opts):
340 341 """output the latest or given revisions of files
341 342
342 343 Print the specified files as they were at the given revision.
343 344 If no revision is given then working dir parent is used, or tip
344 345 if no revision is checked out.
345 346
346 347 Output may be to a file, in which case the name of the file is
347 348 given using a format string. The formatting rules are the same as
348 349 for the export command, with the following additions:
349 350
350 351 %s basename of file being printed
351 352 %d dirname of file being printed, or '.' if in repo root
352 353 %p root-relative path name of file being printed
353 354 """
354 355 ctx = repo.changectx(opts['rev'])
355 356 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
356 357 ctx.node()):
357 358 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
358 359 fp.write(ctx.filectx(abs).data())
359 360
360 361 def clone(ui, source, dest=None, **opts):
361 362 """make a copy of an existing repository
362 363
363 364 Create a copy of an existing repository in a new directory.
364 365
365 366 If no destination directory name is specified, it defaults to the
366 367 basename of the source.
367 368
368 369 The location of the source is added to the new repository's
369 370 .hg/hgrc file, as the default to be used for future pulls.
370 371
371 372 For efficiency, hardlinks are used for cloning whenever the source
372 373 and destination are on the same filesystem (note this applies only
373 374 to the repository data, not to the checked out files). Some
374 375 filesystems, such as AFS, implement hardlinking incorrectly, but
375 376 do not report errors. In these cases, use the --pull option to
376 377 avoid hardlinking.
377 378
378 379 You can safely clone repositories and checked out files using full
379 380 hardlinks with
380 381
381 382 $ cp -al REPO REPOCLONE
382 383
383 384 which is the fastest way to clone. However, the operation is not
384 385 atomic (making sure REPO is not modified during the operation is
385 386 up to you) and you have to make sure your editor breaks hardlinks
386 387 (Emacs and most Linux Kernel tools do so).
387 388
388 389 If you use the -r option to clone up to a specific revision, no
389 390 subsequent revisions will be present in the cloned repository.
390 391 This option implies --pull, even on local repositories.
391 392
392 393 See pull for valid source format details.
393 394
394 395 It is possible to specify an ssh:// URL as the destination, but no
395 396 .hg/hgrc and working directory will be created on the remote side.
396 397 Look at the help text for the pull command for important details
397 398 about ssh:// URLs.
398 399 """
399 400 setremoteconfig(ui, opts)
400 401 hg.clone(ui, ui.expandpath(source), dest,
401 402 pull=opts['pull'],
402 403 stream=opts['uncompressed'],
403 404 rev=opts['rev'],
404 405 update=not opts['noupdate'])
405 406
406 407 def commit(ui, repo, *pats, **opts):
407 408 """commit the specified files or all outstanding changes
408 409
409 410 Commit changes to the given files into the repository.
410 411
411 412 If a list of files is omitted, all changes reported by "hg status"
412 413 will be committed.
413 414
414 415 If no commit message is specified, the editor configured in your hgrc
415 416 or in the EDITOR environment variable is started to enter a message.
416 417 """
417 418 message = logmessage(opts)
418 419
419 420 if opts['addremove']:
420 421 cmdutil.addremove(repo, pats, opts)
421 422 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
422 423 if pats:
423 424 status = repo.status(files=fns, match=match)
424 425 modified, added, removed, deleted, unknown = status[:5]
425 426 files = modified + added + removed
426 427 slist = None
427 428 for f in fns:
428 429 if f not in files:
429 430 rf = repo.wjoin(f)
430 431 if f in unknown:
431 432 raise util.Abort(_("file %s not tracked!") % rf)
432 433 try:
433 434 mode = os.lstat(rf)[stat.ST_MODE]
434 435 except OSError:
435 436 raise util.Abort(_("file %s not found!") % rf)
436 437 if stat.S_ISDIR(mode):
437 438 name = f + '/'
438 439 if slist is None:
439 440 slist = list(files)
440 441 slist.sort()
441 442 i = bisect.bisect(slist, name)
442 443 if i >= len(slist) or not slist[i].startswith(name):
443 444 raise util.Abort(_("no match under directory %s!")
444 445 % rf)
445 446 elif not stat.S_ISREG(mode):
446 447 raise util.Abort(_("can't commit %s: "
447 448 "unsupported file type!") % rf)
448 449 else:
449 450 files = []
450 451 try:
451 452 repo.commit(files, message, opts['user'], opts['date'], match,
452 453 force_editor=opts.get('force_editor'))
453 454 except ValueError, inst:
454 455 raise util.Abort(str(inst))
455 456
456 457 def docopy(ui, repo, pats, opts, wlock):
457 458 # called with the repo lock held
458 459 #
459 460 # hgsep => pathname that uses "/" to separate directories
460 461 # ossep => pathname that uses os.sep to separate directories
461 462 cwd = repo.getcwd()
462 463 errors = 0
463 464 copied = []
464 465 targets = {}
465 466
466 467 # abs: hgsep
467 468 # rel: ossep
468 469 # return: hgsep
469 470 def okaytocopy(abs, rel, exact):
470 471 reasons = {'?': _('is not managed'),
471 472 'a': _('has been marked for add'),
472 473 'r': _('has been marked for remove')}
473 474 state = repo.dirstate.state(abs)
474 475 reason = reasons.get(state)
475 476 if reason:
476 477 if state == 'a':
477 478 origsrc = repo.dirstate.copied(abs)
478 479 if origsrc is not None:
479 480 return origsrc
480 481 if exact:
481 482 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
482 483 else:
483 484 return abs
484 485
485 486 # origsrc: hgsep
486 487 # abssrc: hgsep
487 488 # relsrc: ossep
488 489 # target: ossep
489 490 def copy(origsrc, abssrc, relsrc, target, exact):
490 491 abstarget = util.canonpath(repo.root, cwd, target)
491 492 reltarget = util.pathto(cwd, abstarget)
492 493 prevsrc = targets.get(abstarget)
493 494 if prevsrc is not None:
494 495 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
495 496 (reltarget, util.localpath(abssrc),
496 497 util.localpath(prevsrc)))
497 498 return
498 499 if (not opts['after'] and os.path.exists(reltarget) or
499 500 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
500 501 if not opts['force']:
501 502 ui.warn(_('%s: not overwriting - file exists\n') %
502 503 reltarget)
503 504 return
504 505 if not opts['after'] and not opts.get('dry_run'):
505 506 os.unlink(reltarget)
506 507 if opts['after']:
507 508 if not os.path.exists(reltarget):
508 509 return
509 510 else:
510 511 targetdir = os.path.dirname(reltarget) or '.'
511 512 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
512 513 os.makedirs(targetdir)
513 514 try:
514 515 restore = repo.dirstate.state(abstarget) == 'r'
515 516 if restore and not opts.get('dry_run'):
516 517 repo.undelete([abstarget], wlock)
517 518 try:
518 519 if not opts.get('dry_run'):
519 520 util.copyfile(relsrc, reltarget)
520 521 restore = False
521 522 finally:
522 523 if restore:
523 524 repo.remove([abstarget], wlock)
524 525 except IOError, inst:
525 526 if inst.errno == errno.ENOENT:
526 527 ui.warn(_('%s: deleted in working copy\n') % relsrc)
527 528 else:
528 529 ui.warn(_('%s: cannot copy - %s\n') %
529 530 (relsrc, inst.strerror))
530 531 errors += 1
531 532 return
532 533 if ui.verbose or not exact:
533 534 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
534 535 targets[abstarget] = abssrc
535 536 if abstarget != origsrc and not opts.get('dry_run'):
536 537 repo.copy(origsrc, abstarget, wlock)
537 538 copied.append((abssrc, relsrc, exact))
538 539
539 540 # pat: ossep
540 541 # dest ossep
541 542 # srcs: list of (hgsep, hgsep, ossep, bool)
542 543 # return: function that takes hgsep and returns ossep
543 544 def targetpathfn(pat, dest, srcs):
544 545 if os.path.isdir(pat):
545 546 abspfx = util.canonpath(repo.root, cwd, pat)
546 547 abspfx = util.localpath(abspfx)
547 548 if destdirexists:
548 549 striplen = len(os.path.split(abspfx)[0])
549 550 else:
550 551 striplen = len(abspfx)
551 552 if striplen:
552 553 striplen += len(os.sep)
553 554 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
554 555 elif destdirexists:
555 556 res = lambda p: os.path.join(dest,
556 557 os.path.basename(util.localpath(p)))
557 558 else:
558 559 res = lambda p: dest
559 560 return res
560 561
561 562 # pat: ossep
562 563 # dest ossep
563 564 # srcs: list of (hgsep, hgsep, ossep, bool)
564 565 # return: function that takes hgsep and returns ossep
565 566 def targetpathafterfn(pat, dest, srcs):
566 567 if util.patkind(pat, None)[0]:
567 568 # a mercurial pattern
568 569 res = lambda p: os.path.join(dest,
569 570 os.path.basename(util.localpath(p)))
570 571 else:
571 572 abspfx = util.canonpath(repo.root, cwd, pat)
572 573 if len(abspfx) < len(srcs[0][0]):
573 574 # A directory. Either the target path contains the last
574 575 # component of the source path or it does not.
575 576 def evalpath(striplen):
576 577 score = 0
577 578 for s in srcs:
578 579 t = os.path.join(dest, util.localpath(s[0])[striplen:])
579 580 if os.path.exists(t):
580 581 score += 1
581 582 return score
582 583
583 584 abspfx = util.localpath(abspfx)
584 585 striplen = len(abspfx)
585 586 if striplen:
586 587 striplen += len(os.sep)
587 588 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
588 589 score = evalpath(striplen)
589 590 striplen1 = len(os.path.split(abspfx)[0])
590 591 if striplen1:
591 592 striplen1 += len(os.sep)
592 593 if evalpath(striplen1) > score:
593 594 striplen = striplen1
594 595 res = lambda p: os.path.join(dest,
595 596 util.localpath(p)[striplen:])
596 597 else:
597 598 # a file
598 599 if destdirexists:
599 600 res = lambda p: os.path.join(dest,
600 601 os.path.basename(util.localpath(p)))
601 602 else:
602 603 res = lambda p: dest
603 604 return res
604 605
605 606
606 607 pats = list(pats)
607 608 if not pats:
608 609 raise util.Abort(_('no source or destination specified'))
609 610 if len(pats) == 1:
610 611 raise util.Abort(_('no destination specified'))
611 612 dest = pats.pop()
612 613 destdirexists = os.path.isdir(dest)
613 614 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
614 615 raise util.Abort(_('with multiple sources, destination must be an '
615 616 'existing directory'))
616 617 if opts['after']:
617 618 tfn = targetpathafterfn
618 619 else:
619 620 tfn = targetpathfn
620 621 copylist = []
621 622 for pat in pats:
622 623 srcs = []
623 624 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts):
624 625 origsrc = okaytocopy(abssrc, relsrc, exact)
625 626 if origsrc:
626 627 srcs.append((origsrc, abssrc, relsrc, exact))
627 628 if not srcs:
628 629 continue
629 630 copylist.append((tfn(pat, dest, srcs), srcs))
630 631 if not copylist:
631 632 raise util.Abort(_('no files to copy'))
632 633
633 634 for targetpath, srcs in copylist:
634 635 for origsrc, abssrc, relsrc, exact in srcs:
635 636 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
636 637
637 638 if errors:
638 639 ui.warn(_('(consider using --after)\n'))
639 640 return errors, copied
640 641
641 642 def copy(ui, repo, *pats, **opts):
642 643 """mark files as copied for the next commit
643 644
644 645 Mark dest as having copies of source files. If dest is a
645 646 directory, copies are put in that directory. If dest is a file,
646 647 there can only be one source.
647 648
648 649 By default, this command copies the contents of files as they
649 650 stand in the working directory. If invoked with --after, the
650 651 operation is recorded, but no copying is performed.
651 652
652 653 This command takes effect in the next commit.
653 654 """
654 655 wlock = repo.wlock(0)
655 656 errs, copied = docopy(ui, repo, pats, opts, wlock)
656 657 return errs
657 658
658 659 def debugancestor(ui, index, rev1, rev2):
659 660 """find the ancestor revision of two revisions in a given index"""
660 661 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0)
661 662 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
662 663 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
663 664
664 665 def debugcomplete(ui, cmd='', **opts):
665 666 """returns the completion list associated with the given command"""
666 667
667 668 if opts['options']:
668 669 options = []
669 670 otables = [globalopts]
670 671 if cmd:
671 672 aliases, entry = findcmd(ui, cmd)
672 673 otables.append(entry[1])
673 674 for t in otables:
674 675 for o in t:
675 676 if o[0]:
676 677 options.append('-%s' % o[0])
677 678 options.append('--%s' % o[1])
678 679 ui.write("%s\n" % "\n".join(options))
679 680 return
680 681
681 682 clist = findpossible(ui, cmd).keys()
682 683 clist.sort()
683 684 ui.write("%s\n" % "\n".join(clist))
684 685
685 686 def debugrebuildstate(ui, repo, rev=None):
686 687 """rebuild the dirstate as it would look like for the given revision"""
687 688 if not rev:
688 689 rev = repo.changelog.tip()
689 690 else:
690 691 rev = repo.lookup(rev)
691 692 change = repo.changelog.read(rev)
692 693 n = change[0]
693 694 files = repo.manifest.read(n)
694 695 wlock = repo.wlock()
695 696 repo.dirstate.rebuild(rev, files)
696 697
697 698 def debugcheckstate(ui, repo):
698 699 """validate the correctness of the current dirstate"""
699 700 parent1, parent2 = repo.dirstate.parents()
700 701 repo.dirstate.read()
701 702 dc = repo.dirstate.map
702 703 keys = dc.keys()
703 704 keys.sort()
704 705 m1n = repo.changelog.read(parent1)[0]
705 706 m2n = repo.changelog.read(parent2)[0]
706 707 m1 = repo.manifest.read(m1n)
707 708 m2 = repo.manifest.read(m2n)
708 709 errors = 0
709 710 for f in dc:
710 711 state = repo.dirstate.state(f)
711 712 if state in "nr" and f not in m1:
712 713 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
713 714 errors += 1
714 715 if state in "a" and f in m1:
715 716 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
716 717 errors += 1
717 718 if state in "m" and f not in m1 and f not in m2:
718 719 ui.warn(_("%s in state %s, but not in either manifest\n") %
719 720 (f, state))
720 721 errors += 1
721 722 for f in m1:
722 723 state = repo.dirstate.state(f)
723 724 if state not in "nrm":
724 725 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
725 726 errors += 1
726 727 if errors:
727 728 error = _(".hg/dirstate inconsistent with current parent's manifest")
728 729 raise util.Abort(error)
729 730
730 731 def showconfig(ui, repo, *values, **opts):
731 732 """show combined config settings from all hgrc files
732 733
733 734 With no args, print names and values of all config items.
734 735
735 736 With one arg of the form section.name, print just the value of
736 737 that config item.
737 738
738 739 With multiple args, print names and values of all config items
739 740 with matching section names."""
740 741
741 742 untrusted = bool(opts.get('untrusted'))
742 743 if values:
743 744 if len([v for v in values if '.' in v]) > 1:
744 745 raise util.Abort(_('only one config item permitted'))
745 746 for section, name, value in ui.walkconfig(untrusted=untrusted):
746 747 sectname = section + '.' + name
747 748 if values:
748 749 for v in values:
749 750 if v == section:
750 751 ui.write('%s=%s\n' % (sectname, value))
751 752 elif v == sectname:
752 753 ui.write(value, '\n')
753 754 else:
754 755 ui.write('%s=%s\n' % (sectname, value))
755 756
756 757 def debugsetparents(ui, repo, rev1, rev2=None):
757 758 """manually set the parents of the current working directory
758 759
759 760 This is useful for writing repository conversion tools, but should
760 761 be used with care.
761 762 """
762 763
763 764 if not rev2:
764 765 rev2 = hex(nullid)
765 766
766 767 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
767 768
768 769 def debugstate(ui, repo):
769 770 """show the contents of the current dirstate"""
770 771 repo.dirstate.read()
771 772 dc = repo.dirstate.map
772 773 keys = dc.keys()
773 774 keys.sort()
774 775 for file_ in keys:
775 776 ui.write("%c %3o %10d %s %s\n"
776 777 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
777 778 time.strftime("%x %X",
778 779 time.localtime(dc[file_][3])), file_))
779 780 for f in repo.dirstate.copies():
780 781 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
781 782
782 783 def debugdata(ui, file_, rev):
783 784 """dump the contents of an data file revision"""
784 785 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
785 786 file_[:-2] + ".i", file_, 0)
786 787 try:
787 788 ui.write(r.revision(r.lookup(rev)))
788 789 except KeyError:
789 790 raise util.Abort(_('invalid revision identifier %s') % rev)
790 791
791 792 def debugdate(ui, date, range=None, **opts):
792 793 """parse and display a date"""
793 794 if opts["extended"]:
794 795 d = util.parsedate(date, util.extendeddateformats)
795 796 else:
796 797 d = util.parsedate(date)
797 798 ui.write("internal: %s %s\n" % d)
798 799 ui.write("standard: %s\n" % util.datestr(d))
799 800 if range:
800 801 m = util.matchdate(range)
801 802 ui.write("match: %s\n" % m(d[0]))
802 803
803 804 def debugindex(ui, file_):
804 805 """dump the contents of an index file"""
805 806 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
806 807 ui.write(" rev offset length base linkrev" +
807 808 " nodeid p1 p2\n")
808 809 for i in xrange(r.count()):
809 810 node = r.node(i)
810 811 pp = r.parents(node)
811 812 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
812 813 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
813 814 short(node), short(pp[0]), short(pp[1])))
814 815
815 816 def debugindexdot(ui, file_):
816 817 """dump an index DAG as a .dot file"""
817 818 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
818 819 ui.write("digraph G {\n")
819 820 for i in xrange(r.count()):
820 821 node = r.node(i)
821 822 pp = r.parents(node)
822 823 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
823 824 if pp[1] != nullid:
824 825 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
825 826 ui.write("}\n")
826 827
827 828 def debugrename(ui, repo, file1, *pats, **opts):
828 829 """dump rename information"""
829 830
830 831 ctx = repo.changectx(opts.get('rev', 'tip'))
831 832 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
832 833 ctx.node()):
833 834 m = ctx.filectx(abs).renamed()
834 835 if m:
835 836 ui.write(_("%s renamed from %s:%s\n") % (rel, m[0], hex(m[1])))
836 837 else:
837 838 ui.write(_("%s not renamed\n") % rel)
838 839
839 840 def debugwalk(ui, repo, *pats, **opts):
840 841 """show how files match on given patterns"""
841 842 items = list(cmdutil.walk(repo, pats, opts))
842 843 if not items:
843 844 return
844 845 fmt = '%%s %%-%ds %%-%ds %%s' % (
845 846 max([len(abs) for (src, abs, rel, exact) in items]),
846 847 max([len(rel) for (src, abs, rel, exact) in items]))
847 848 for src, abs, rel, exact in items:
848 849 line = fmt % (src, abs, rel, exact and 'exact' or '')
849 850 ui.write("%s\n" % line.rstrip())
850 851
851 852 def diff(ui, repo, *pats, **opts):
852 853 """diff repository (or selected files)
853 854
854 855 Show differences between revisions for the specified files.
855 856
856 857 Differences between files are shown using the unified diff format.
857 858
858 859 When two revision arguments are given, then changes are shown
859 860 between those revisions. If only one revision is specified then
860 861 that revision is compared to the working directory, and, when no
861 862 revisions are specified, the working directory files are compared
862 863 to its parent.
863 864
864 865 Without the -a option, diff will avoid generating diffs of files
865 866 it detects as binary. With -a, diff will generate a diff anyway,
866 867 probably with undesirable results.
867 868 """
868 869 node1, node2 = cmdutil.revpair(repo, opts['rev'])
869 870
870 871 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
871 872
872 873 patch.diff(repo, node1, node2, fns, match=matchfn,
873 874 opts=patch.diffopts(ui, opts))
874 875
875 876 def export(ui, repo, *changesets, **opts):
876 877 """dump the header and diffs for one or more changesets
877 878
878 879 Print the changeset header and diffs for one or more revisions.
879 880
880 881 The information shown in the changeset header is: author,
881 882 changeset hash, parent and commit comment.
882 883
883 884 Output may be to a file, in which case the name of the file is
884 885 given using a format string. The formatting rules are as follows:
885 886
886 887 %% literal "%" character
887 888 %H changeset hash (40 bytes of hexadecimal)
888 889 %N number of patches being generated
889 890 %R changeset revision number
890 891 %b basename of the exporting repository
891 892 %h short-form changeset hash (12 bytes of hexadecimal)
892 893 %n zero-padded sequence number, starting at 1
893 894 %r zero-padded changeset revision number
894 895
895 896 Without the -a option, export will avoid generating diffs of files
896 897 it detects as binary. With -a, export will generate a diff anyway,
897 898 probably with undesirable results.
898 899
899 900 With the --switch-parent option, the diff will be against the second
900 901 parent. It can be useful to review a merge.
901 902 """
902 903 if not changesets:
903 904 raise util.Abort(_("export requires at least one changeset"))
904 905 revs = cmdutil.revrange(repo, changesets)
905 906 if len(revs) > 1:
906 907 ui.note(_('exporting patches:\n'))
907 908 else:
908 909 ui.note(_('exporting patch:\n'))
909 910 patch.export(repo, map(repo.lookup, revs), template=opts['output'],
910 911 switch_parent=opts['switch_parent'],
911 912 opts=patch.diffopts(ui, opts))
912 913
913 914 def grep(ui, repo, pattern, *pats, **opts):
914 915 """search for a pattern in specified files and revisions
915 916
916 917 Search revisions of files for a regular expression.
917 918
918 919 This command behaves differently than Unix grep. It only accepts
919 920 Python/Perl regexps. It searches repository history, not the
920 921 working directory. It always prints the revision number in which
921 922 a match appears.
922 923
923 924 By default, grep only prints output for the first revision of a
924 925 file in which it finds a match. To get it to print every revision
925 926 that contains a change in match status ("-" for a match that
926 927 becomes a non-match, or "+" for a non-match that becomes a match),
927 928 use the --all flag.
928 929 """
929 930 reflags = 0
930 931 if opts['ignore_case']:
931 932 reflags |= re.I
932 933 regexp = re.compile(pattern, reflags)
933 934 sep, eol = ':', '\n'
934 935 if opts['print0']:
935 936 sep = eol = '\0'
936 937
937 938 fcache = {}
938 939 def getfile(fn):
939 940 if fn not in fcache:
940 941 fcache[fn] = repo.file(fn)
941 942 return fcache[fn]
942 943
943 944 def matchlines(body):
944 945 begin = 0
945 946 linenum = 0
946 947 while True:
947 948 match = regexp.search(body, begin)
948 949 if not match:
949 950 break
950 951 mstart, mend = match.span()
951 952 linenum += body.count('\n', begin, mstart) + 1
952 953 lstart = body.rfind('\n', begin, mstart) + 1 or begin
953 954 lend = body.find('\n', mend)
954 955 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
955 956 begin = lend + 1
956 957
957 958 class linestate(object):
958 959 def __init__(self, line, linenum, colstart, colend):
959 960 self.line = line
960 961 self.linenum = linenum
961 962 self.colstart = colstart
962 963 self.colend = colend
963 964
964 965 def __eq__(self, other):
965 966 return self.line == other.line
966 967
967 968 matches = {}
968 969 copies = {}
969 970 def grepbody(fn, rev, body):
970 971 matches[rev].setdefault(fn, [])
971 972 m = matches[rev][fn]
972 973 for lnum, cstart, cend, line in matchlines(body):
973 974 s = linestate(line, lnum, cstart, cend)
974 975 m.append(s)
975 976
976 977 def difflinestates(a, b):
977 978 sm = difflib.SequenceMatcher(None, a, b)
978 979 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
979 980 if tag == 'insert':
980 981 for i in xrange(blo, bhi):
981 982 yield ('+', b[i])
982 983 elif tag == 'delete':
983 984 for i in xrange(alo, ahi):
984 985 yield ('-', a[i])
985 986 elif tag == 'replace':
986 987 for i in xrange(alo, ahi):
987 988 yield ('-', a[i])
988 989 for i in xrange(blo, bhi):
989 990 yield ('+', b[i])
990 991
991 992 prev = {}
992 993 def display(fn, rev, states, prevstates):
993 994 counts = {'-': 0, '+': 0}
994 995 filerevmatches = {}
995 996 if incrementing or not opts['all']:
996 997 a, b, r = prevstates, states, rev
997 998 else:
998 999 a, b, r = states, prevstates, prev.get(fn, -1)
999 1000 for change, l in difflinestates(a, b):
1000 1001 cols = [fn, str(r)]
1001 1002 if opts['line_number']:
1002 1003 cols.append(str(l.linenum))
1003 1004 if opts['all']:
1004 1005 cols.append(change)
1005 1006 if opts['user']:
1006 1007 cols.append(ui.shortuser(get(r)[1]))
1007 1008 if opts['files_with_matches']:
1008 1009 c = (fn, r)
1009 1010 if c in filerevmatches:
1010 1011 continue
1011 1012 filerevmatches[c] = 1
1012 1013 else:
1013 1014 cols.append(l.line)
1014 1015 ui.write(sep.join(cols), eol)
1015 1016 counts[change] += 1
1016 1017 return counts['+'], counts['-']
1017 1018
1018 1019 fstate = {}
1019 1020 skip = {}
1020 1021 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1021 1022 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1022 1023 count = 0
1023 1024 incrementing = False
1024 1025 follow = opts.get('follow')
1025 1026 for st, rev, fns in changeiter:
1026 1027 if st == 'window':
1027 1028 incrementing = rev
1028 1029 matches.clear()
1029 1030 elif st == 'add':
1030 1031 mf = repo.changectx(rev).manifest()
1031 1032 matches[rev] = {}
1032 1033 for fn in fns:
1033 1034 if fn in skip:
1034 1035 continue
1035 1036 fstate.setdefault(fn, {})
1036 1037 try:
1037 1038 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1038 1039 if follow:
1039 1040 copied = getfile(fn).renamed(mf[fn])
1040 1041 if copied:
1041 1042 copies.setdefault(rev, {})[fn] = copied[0]
1042 1043 except KeyError:
1043 1044 pass
1044 1045 elif st == 'iter':
1045 1046 states = matches[rev].items()
1046 1047 states.sort()
1047 1048 for fn, m in states:
1048 1049 copy = copies.get(rev, {}).get(fn)
1049 1050 if fn in skip:
1050 1051 if copy:
1051 1052 skip[copy] = True
1052 1053 continue
1053 1054 if incrementing or not opts['all'] or fstate[fn]:
1054 1055 pos, neg = display(fn, rev, m, fstate[fn])
1055 1056 count += pos + neg
1056 1057 if pos and not opts['all']:
1057 1058 skip[fn] = True
1058 1059 if copy:
1059 1060 skip[copy] = True
1060 1061 fstate[fn] = m
1061 1062 if copy:
1062 1063 fstate[copy] = m
1063 1064 prev[fn] = rev
1064 1065
1065 1066 if not incrementing:
1066 1067 fstate = fstate.items()
1067 1068 fstate.sort()
1068 1069 for fn, state in fstate:
1069 1070 if fn in skip:
1070 1071 continue
1071 1072 if fn not in copies.get(prev[fn], {}):
1072 1073 display(fn, rev, {}, state)
1073 1074 return (count == 0 and 1) or 0
1074 1075
1075 1076 def heads(ui, repo, **opts):
1076 1077 """show current repository heads
1077 1078
1078 1079 Show all repository head changesets.
1079 1080
1080 1081 Repository "heads" are changesets that don't have children
1081 1082 changesets. They are where development generally takes place and
1082 1083 are the usual targets for update and merge operations.
1083 1084 """
1084 1085 if opts['rev']:
1085 1086 heads = repo.heads(repo.lookup(opts['rev']))
1086 1087 else:
1087 1088 heads = repo.heads()
1088 1089 displayer = cmdutil.show_changeset(ui, repo, opts)
1089 1090 for n in heads:
1090 1091 displayer.show(changenode=n)
1091 1092
1092 1093 def help_(ui, name=None, with_version=False):
1093 1094 """show help for a command, extension, or list of commands
1094 1095
1095 1096 With no arguments, print a list of commands and short help.
1096 1097
1097 1098 Given a command name, print help for that command.
1098 1099
1099 1100 Given an extension name, print help for that extension, and the
1100 1101 commands it provides."""
1101 1102 option_lists = []
1102 1103
1103 1104 def helpcmd(name):
1104 1105 if with_version:
1105 1106 version_(ui)
1106 1107 ui.write('\n')
1107 1108 aliases, i = findcmd(ui, name)
1108 1109 # synopsis
1109 1110 ui.write("%s\n\n" % i[2])
1110 1111
1111 1112 # description
1112 1113 doc = i[0].__doc__
1113 1114 if not doc:
1114 1115 doc = _("(No help text available)")
1115 1116 if ui.quiet:
1116 1117 doc = doc.splitlines(0)[0]
1117 1118 ui.write("%s\n" % doc.rstrip())
1118 1119
1119 1120 if not ui.quiet:
1120 1121 # aliases
1121 1122 if len(aliases) > 1:
1122 1123 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1123 1124
1124 1125 # options
1125 1126 if i[1]:
1126 1127 option_lists.append(("options", i[1]))
1127 1128
1128 1129 def helplist(select=None):
1129 1130 h = {}
1130 1131 cmds = {}
1131 1132 for c, e in table.items():
1132 1133 f = c.split("|", 1)[0]
1133 1134 if select and not select(f):
1134 1135 continue
1135 1136 if name == "shortlist" and not f.startswith("^"):
1136 1137 continue
1137 1138 f = f.lstrip("^")
1138 1139 if not ui.debugflag and f.startswith("debug"):
1139 1140 continue
1140 1141 doc = e[0].__doc__
1141 1142 if not doc:
1142 1143 doc = _("(No help text available)")
1143 1144 h[f] = doc.splitlines(0)[0].rstrip()
1144 1145 cmds[f] = c.lstrip("^")
1145 1146
1146 1147 fns = h.keys()
1147 1148 fns.sort()
1148 1149 m = max(map(len, fns))
1149 1150 for f in fns:
1150 1151 if ui.verbose:
1151 1152 commands = cmds[f].replace("|",", ")
1152 1153 ui.write(" %s:\n %s\n"%(commands, h[f]))
1153 1154 else:
1154 1155 ui.write(' %-*s %s\n' % (m, f, h[f]))
1155 1156
1156 1157 def helptopic(name):
1157 1158 v = None
1158 1159 for i in help.helptable:
1159 1160 l = i.split('|')
1160 1161 if name in l:
1161 1162 v = i
1162 1163 header = l[-1]
1163 1164 if not v:
1164 1165 raise UnknownCommand(name)
1165 1166
1166 1167 # description
1167 1168 doc = help.helptable[v]
1168 1169 if not doc:
1169 1170 doc = _("(No help text available)")
1170 1171 if callable(doc):
1171 1172 doc = doc()
1172 1173
1173 1174 ui.write("%s\n" % header)
1174 1175 ui.write("%s\n" % doc.rstrip())
1175 1176
1176 1177 def helpext(name):
1177 1178 try:
1178 1179 mod = findext(name)
1179 1180 except KeyError:
1180 1181 raise UnknownCommand(name)
1181 1182
1182 1183 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1183 1184 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1184 1185 for d in doc[1:]:
1185 1186 ui.write(d, '\n')
1186 1187
1187 1188 ui.status('\n')
1188 1189 if ui.verbose:
1189 1190 ui.status(_('list of commands:\n\n'))
1190 1191 else:
1191 1192 ui.status(_('list of commands (use "hg help -v %s" '
1192 1193 'to show aliases and global options):\n\n') % name)
1193 1194
1194 1195 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in mod.cmdtable])
1195 1196 helplist(modcmds.has_key)
1196 1197
1197 1198 if name and name != 'shortlist':
1198 1199 i = None
1199 1200 for f in (helpcmd, helptopic, helpext):
1200 1201 try:
1201 1202 f(name)
1202 1203 i = None
1203 1204 break
1204 1205 except UnknownCommand, inst:
1205 1206 i = inst
1206 1207 if i:
1207 1208 raise i
1208 1209
1209 1210 else:
1210 1211 # program name
1211 1212 if ui.verbose or with_version:
1212 1213 version_(ui)
1213 1214 else:
1214 1215 ui.status(_("Mercurial Distributed SCM\n"))
1215 1216 ui.status('\n')
1216 1217
1217 1218 # list of commands
1218 1219 if name == "shortlist":
1219 1220 ui.status(_('basic commands (use "hg help" '
1220 1221 'for the full list or option "-v" for details):\n\n'))
1221 1222 elif ui.verbose:
1222 1223 ui.status(_('list of commands:\n\n'))
1223 1224 else:
1224 1225 ui.status(_('list of commands (use "hg help -v" '
1225 1226 'to show aliases and global options):\n\n'))
1226 1227
1227 1228 helplist()
1228 1229
1229 1230 # global options
1230 1231 if ui.verbose:
1231 1232 option_lists.append(("global options", globalopts))
1232 1233
1233 1234 # list all option lists
1234 1235 opt_output = []
1235 1236 for title, options in option_lists:
1236 1237 opt_output.append(("\n%s:\n" % title, None))
1237 1238 for shortopt, longopt, default, desc in options:
1238 1239 if "DEPRECATED" in desc and not ui.verbose: continue
1239 1240 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1240 1241 longopt and " --%s" % longopt),
1241 1242 "%s%s" % (desc,
1242 1243 default
1243 1244 and _(" (default: %s)") % default
1244 1245 or "")))
1245 1246
1246 1247 if opt_output:
1247 1248 opts_len = max([len(line[0]) for line in opt_output if line[1]])
1248 1249 for first, second in opt_output:
1249 1250 if second:
1250 1251 ui.write(" %-*s %s\n" % (opts_len, first, second))
1251 1252 else:
1252 1253 ui.write("%s\n" % first)
1253 1254
1254 1255 def identify(ui, repo):
1255 1256 """print information about the working copy
1256 1257
1257 1258 Print a short summary of the current state of the repo.
1258 1259
1259 1260 This summary identifies the repository state using one or two parent
1260 1261 hash identifiers, followed by a "+" if there are uncommitted changes
1261 1262 in the working directory, followed by a list of tags for this revision.
1262 1263 """
1263 1264 parents = [p for p in repo.dirstate.parents() if p != nullid]
1264 1265 if not parents:
1265 1266 ui.write(_("unknown\n"))
1266 1267 return
1267 1268
1268 1269 hexfunc = ui.debugflag and hex or short
1269 1270 modified, added, removed, deleted = repo.status()[:4]
1270 1271 output = ["%s%s" %
1271 1272 ('+'.join([hexfunc(parent) for parent in parents]),
1272 1273 (modified or added or removed or deleted) and "+" or "")]
1273 1274
1274 1275 if not ui.quiet:
1275 1276
1276 1277 branch = repo.workingctx().branch()
1277 1278 if branch:
1278 1279 output.append("(%s)" % branch)
1279 1280
1280 1281 # multiple tags for a single parent separated by '/'
1281 1282 parenttags = ['/'.join(tags)
1282 1283 for tags in map(repo.nodetags, parents) if tags]
1283 1284 # tags for multiple parents separated by ' + '
1284 1285 if parenttags:
1285 1286 output.append(' + '.join(parenttags))
1286 1287
1287 1288 ui.write("%s\n" % ' '.join(output))
1288 1289
1289 1290 def import_(ui, repo, patch1, *patches, **opts):
1290 1291 """import an ordered set of patches
1291 1292
1292 1293 Import a list of patches and commit them individually.
1293 1294
1294 1295 If there are outstanding changes in the working directory, import
1295 1296 will abort unless given the -f flag.
1296 1297
1297 1298 You can import a patch straight from a mail message. Even patches
1298 1299 as attachments work (body part must be type text/plain or
1299 1300 text/x-patch to be used). From and Subject headers of email
1300 1301 message are used as default committer and commit message. All
1301 1302 text/plain body parts before first diff are added to commit
1302 1303 message.
1303 1304
1304 1305 If imported patch was generated by hg export, user and description
1305 1306 from patch override values from message headers and body. Values
1306 1307 given on command line with -m and -u override these.
1307 1308
1308 1309 To read a patch from standard input, use patch name "-".
1309 1310 """
1310 1311 patches = (patch1,) + patches
1311 1312
1312 1313 if not opts['force']:
1313 1314 bail_if_changed(repo)
1314 1315
1315 1316 d = opts["base"]
1316 1317 strip = opts["strip"]
1317 1318
1318 1319 wlock = repo.wlock()
1319 1320 lock = repo.lock()
1320 1321
1321 1322 for p in patches:
1322 1323 pf = os.path.join(d, p)
1323 1324
1324 1325 if pf == '-':
1325 1326 ui.status(_("applying patch from stdin\n"))
1326 1327 tmpname, message, user, date = patch.extract(ui, sys.stdin)
1327 1328 else:
1328 1329 ui.status(_("applying %s\n") % p)
1329 1330 tmpname, message, user, date = patch.extract(ui, file(pf))
1330 1331
1331 1332 if tmpname is None:
1332 1333 raise util.Abort(_('no diffs found'))
1333 1334
1334 1335 try:
1335 1336 if opts['message']:
1336 1337 # pickup the cmdline msg
1337 1338 message = opts['message']
1338 1339 elif message:
1339 1340 # pickup the patch msg
1340 1341 message = message.strip()
1341 1342 else:
1342 1343 # launch the editor
1343 1344 message = None
1344 1345 ui.debug(_('message:\n%s\n') % message)
1345 1346
1346 1347 files = {}
1347 1348 try:
1348 1349 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1349 1350 files=files)
1350 1351 finally:
1351 1352 files = patch.updatedir(ui, repo, files, wlock=wlock)
1352 1353 repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1353 1354 finally:
1354 1355 os.unlink(tmpname)
1355 1356
1356 1357 def incoming(ui, repo, source="default", **opts):
1357 1358 """show new changesets found in source
1358 1359
1359 1360 Show new changesets found in the specified path/URL or the default
1360 1361 pull location. These are the changesets that would be pulled if a pull
1361 1362 was requested.
1362 1363
1363 1364 For remote repository, using --bundle avoids downloading the changesets
1364 1365 twice if the incoming is followed by a pull.
1365 1366
1366 1367 See pull for valid source format details.
1367 1368 """
1368 1369 source = ui.expandpath(source)
1369 1370 setremoteconfig(ui, opts)
1370 1371
1371 1372 other = hg.repository(ui, source)
1372 1373 incoming = repo.findincoming(other, force=opts["force"])
1373 1374 if not incoming:
1374 1375 ui.status(_("no changes found\n"))
1375 1376 return
1376 1377
1377 1378 cleanup = None
1378 1379 try:
1379 1380 fname = opts["bundle"]
1380 1381 if fname or not other.local():
1381 1382 # create a bundle (uncompressed if other repo is not local)
1382 1383 cg = other.changegroup(incoming, "incoming")
1383 1384 bundletype = other.local() and "HG10BZ" or "HG10UN"
1384 1385 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1385 1386 # keep written bundle?
1386 1387 if opts["bundle"]:
1387 1388 cleanup = None
1388 1389 if not other.local():
1389 1390 # use the created uncompressed bundlerepo
1390 1391 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1391 1392
1392 1393 revs = None
1393 1394 if opts['rev']:
1394 1395 revs = [other.lookup(rev) for rev in opts['rev']]
1395 1396 o = other.changelog.nodesbetween(incoming, revs)[0]
1396 1397 if opts['newest_first']:
1397 1398 o.reverse()
1398 1399 displayer = cmdutil.show_changeset(ui, other, opts)
1399 1400 for n in o:
1400 1401 parents = [p for p in other.changelog.parents(n) if p != nullid]
1401 1402 if opts['no_merges'] and len(parents) == 2:
1402 1403 continue
1403 1404 displayer.show(changenode=n)
1404 1405 finally:
1405 1406 if hasattr(other, 'close'):
1406 1407 other.close()
1407 1408 if cleanup:
1408 1409 os.unlink(cleanup)
1409 1410
1410 1411 def init(ui, dest=".", **opts):
1411 1412 """create a new repository in the given directory
1412 1413
1413 1414 Initialize a new repository in the given directory. If the given
1414 1415 directory does not exist, it is created.
1415 1416
1416 1417 If no directory is given, the current directory is used.
1417 1418
1418 1419 It is possible to specify an ssh:// URL as the destination.
1419 1420 Look at the help text for the pull command for important details
1420 1421 about ssh:// URLs.
1421 1422 """
1422 1423 setremoteconfig(ui, opts)
1423 1424 hg.repository(ui, dest, create=1)
1424 1425
1425 1426 def locate(ui, repo, *pats, **opts):
1426 1427 """locate files matching specific patterns
1427 1428
1428 1429 Print all files under Mercurial control whose names match the
1429 1430 given patterns.
1430 1431
1431 1432 This command searches the current directory and its
1432 1433 subdirectories. To search an entire repository, move to the root
1433 1434 of the repository.
1434 1435
1435 1436 If no patterns are given to match, this command prints all file
1436 1437 names.
1437 1438
1438 1439 If you want to feed the output of this command into the "xargs"
1439 1440 command, use the "-0" option to both this command and "xargs".
1440 1441 This will avoid the problem of "xargs" treating single filenames
1441 1442 that contain white space as multiple filenames.
1442 1443 """
1443 1444 end = opts['print0'] and '\0' or '\n'
1444 1445 rev = opts['rev']
1445 1446 if rev:
1446 1447 node = repo.lookup(rev)
1447 1448 else:
1448 1449 node = None
1449 1450
1450 1451 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1451 1452 head='(?:.*/|)'):
1452 1453 if not node and repo.dirstate.state(abs) == '?':
1453 1454 continue
1454 1455 if opts['fullpath']:
1455 1456 ui.write(os.path.join(repo.root, abs), end)
1456 1457 else:
1457 1458 ui.write(((pats and rel) or abs), end)
1458 1459
1459 1460 def log(ui, repo, *pats, **opts):
1460 1461 """show revision history of entire repository or files
1461 1462
1462 1463 Print the revision history of the specified files or the entire
1463 1464 project.
1464 1465
1465 1466 File history is shown without following rename or copy history of
1466 1467 files. Use -f/--follow with a file name to follow history across
1467 1468 renames and copies. --follow without a file name will only show
1468 1469 ancestors or descendants of the starting revision. --follow-first
1469 1470 only follows the first parent of merge revisions.
1470 1471
1471 1472 If no revision range is specified, the default is tip:0 unless
1472 1473 --follow is set, in which case the working directory parent is
1473 1474 used as the starting revision.
1474 1475
1475 1476 By default this command outputs: changeset id and hash, tags,
1476 1477 non-trivial parents, user, date and time, and a summary for each
1477 1478 commit. When the -v/--verbose switch is used, the list of changed
1478 1479 files and full commit message is shown.
1479 1480 """
1480 1481
1481 1482 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1482 1483 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1483 1484
1484 1485 if opts['limit']:
1485 1486 try:
1486 1487 limit = int(opts['limit'])
1487 1488 except ValueError:
1488 1489 raise util.Abort(_('limit must be a positive integer'))
1489 1490 if limit <= 0: raise util.Abort(_('limit must be positive'))
1490 1491 else:
1491 1492 limit = sys.maxint
1492 1493 count = 0
1493 1494
1494 1495 if opts['copies'] and opts['rev']:
1495 1496 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1496 1497 else:
1497 1498 endrev = repo.changelog.count()
1498 1499 rcache = {}
1499 1500 ncache = {}
1500 1501 dcache = []
1501 1502 def getrenamed(fn, rev, man):
1502 1503 '''looks up all renames for a file (up to endrev) the first
1503 1504 time the file is given. It indexes on the changerev and only
1504 1505 parses the manifest if linkrev != changerev.
1505 1506 Returns rename info for fn at changerev rev.'''
1506 1507 if fn not in rcache:
1507 1508 rcache[fn] = {}
1508 1509 ncache[fn] = {}
1509 1510 fl = repo.file(fn)
1510 1511 for i in xrange(fl.count()):
1511 1512 node = fl.node(i)
1512 1513 lr = fl.linkrev(node)
1513 1514 renamed = fl.renamed(node)
1514 1515 rcache[fn][lr] = renamed
1515 1516 if renamed:
1516 1517 ncache[fn][node] = renamed
1517 1518 if lr >= endrev:
1518 1519 break
1519 1520 if rev in rcache[fn]:
1520 1521 return rcache[fn][rev]
1521 1522 mr = repo.manifest.rev(man)
1522 1523 if repo.manifest.parentrevs(mr) != (mr - 1, nullrev):
1523 1524 return ncache[fn].get(repo.manifest.find(man, fn)[0])
1524 1525 if not dcache or dcache[0] != man:
1525 1526 dcache[:] = [man, repo.manifest.readdelta(man)]
1526 1527 if fn in dcache[1]:
1527 1528 return ncache[fn].get(dcache[1][fn])
1528 1529 return None
1529 1530
1530 1531 df = False
1531 1532 if opts["date"]:
1532 1533 df = util.matchdate(opts["date"])
1533 1534
1534 1535
1535 1536 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
1536 1537 for st, rev, fns in changeiter:
1537 1538 if st == 'add':
1538 1539 changenode = repo.changelog.node(rev)
1539 1540 parents = [p for p in repo.changelog.parentrevs(rev)
1540 1541 if p != nullrev]
1541 1542 if opts['no_merges'] and len(parents) == 2:
1542 1543 continue
1543 1544 if opts['only_merges'] and len(parents) != 2:
1544 1545 continue
1545 1546
1546 1547 if df:
1547 1548 changes = get(rev)
1548 1549 if not df(changes[2][0]):
1549 1550 continue
1550 1551
1551 1552 if opts['keyword']:
1552 1553 changes = get(rev)
1553 1554 miss = 0
1554 1555 for k in [kw.lower() for kw in opts['keyword']]:
1555 1556 if not (k in changes[1].lower() or
1556 1557 k in changes[4].lower() or
1557 1558 k in " ".join(changes[3][:20]).lower()):
1558 1559 miss = 1
1559 1560 break
1560 1561 if miss:
1561 1562 continue
1562 1563
1563 1564 copies = []
1564 1565 if opts.get('copies') and rev:
1565 1566 mf = get(rev)[0]
1566 1567 for fn in get(rev)[3]:
1567 1568 rename = getrenamed(fn, rev, mf)
1568 1569 if rename:
1569 1570 copies.append((fn, rename[0]))
1570 1571 displayer.show(rev, changenode, copies=copies)
1571 1572 elif st == 'iter':
1572 1573 if count == limit: break
1573 1574 if displayer.flush(rev):
1574 1575 count += 1
1575 1576
1576 1577 def manifest(ui, repo, rev=None):
1577 1578 """output the latest or given revision of the project manifest
1578 1579
1579 1580 Print a list of version controlled files for the given revision.
1580 1581
1581 1582 The manifest is the list of files being version controlled. If no revision
1582 1583 is given then the first parent of the working directory is used.
1583 1584
1584 1585 With -v flag, print file permissions. With --debug flag, print
1585 1586 file revision hashes.
1586 1587 """
1587 1588
1588 1589 m = repo.changectx(rev).manifest()
1589 1590 files = m.keys()
1590 1591 files.sort()
1591 1592
1592 1593 for f in files:
1593 1594 if ui.debugflag:
1594 1595 ui.write("%40s " % hex(m[f]))
1595 1596 if ui.verbose:
1596 1597 ui.write("%3s " % (m.execf(f) and "755" or "644"))
1597 1598 ui.write("%s\n" % f)
1598 1599
1599 def merge(ui, repo, node=None, force=None, branch=None):
1600 def merge(ui, repo, node=None, force=None, branch=None, date=None):
1600 1601 """Merge working directory with another revision
1601 1602
1602 1603 Merge the contents of the current working directory and the
1603 1604 requested revision. Files that changed between either parent are
1604 1605 marked as changed for the next commit and a commit must be
1605 1606 performed before any further updates are allowed.
1606 1607
1607 1608 If no revision is specified, the working directory's parent is a
1608 1609 head revision, and the repository contains exactly one other head,
1609 1610 the other head is merged with by default. Otherwise, an explicit
1610 1611 revision to merge with must be provided.
1611 1612 """
1612 1613
1613 1614 if node or branch:
1614 1615 node = _lookup(repo, node, branch)
1615 1616 else:
1616 1617 heads = repo.heads()
1617 1618 if len(heads) > 2:
1618 1619 raise util.Abort(_('repo has %d heads - '
1619 1620 'please merge with an explicit rev') %
1620 1621 len(heads))
1621 1622 if len(heads) == 1:
1622 1623 raise util.Abort(_('there is nothing to merge - '
1623 1624 'use "hg update" instead'))
1624 1625 parent = repo.dirstate.parents()[0]
1625 1626 if parent not in heads:
1626 1627 raise util.Abort(_('working dir not at a head rev - '
1627 1628 'use "hg update" or merge with an explicit rev'))
1628 1629 node = parent == heads[0] and heads[-1] or heads[0]
1629 1630 return hg.merge(repo, node, force=force)
1630 1631
1631 1632 def outgoing(ui, repo, dest=None, **opts):
1632 1633 """show changesets not found in destination
1633 1634
1634 1635 Show changesets not found in the specified destination repository or
1635 1636 the default push location. These are the changesets that would be pushed
1636 1637 if a push was requested.
1637 1638
1638 1639 See pull for valid destination format details.
1639 1640 """
1640 1641 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1641 1642 setremoteconfig(ui, opts)
1642 1643 revs = None
1643 1644 if opts['rev']:
1644 1645 revs = [repo.lookup(rev) for rev in opts['rev']]
1645 1646
1646 1647 other = hg.repository(ui, dest)
1647 1648 o = repo.findoutgoing(other, force=opts['force'])
1648 1649 if not o:
1649 1650 ui.status(_("no changes found\n"))
1650 1651 return
1651 1652 o = repo.changelog.nodesbetween(o, revs)[0]
1652 1653 if opts['newest_first']:
1653 1654 o.reverse()
1654 1655 displayer = cmdutil.show_changeset(ui, repo, opts)
1655 1656 for n in o:
1656 1657 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1657 1658 if opts['no_merges'] and len(parents) == 2:
1658 1659 continue
1659 1660 displayer.show(changenode=n)
1660 1661
1661 1662 def parents(ui, repo, file_=None, **opts):
1662 1663 """show the parents of the working dir or revision
1663 1664
1664 1665 Print the working directory's parent revisions.
1665 1666 """
1666 1667 rev = opts.get('rev')
1667 1668 if rev:
1668 1669 if file_:
1669 1670 ctx = repo.filectx(file_, changeid=rev)
1670 1671 else:
1671 1672 ctx = repo.changectx(rev)
1672 1673 p = [cp.node() for cp in ctx.parents()]
1673 1674 else:
1674 1675 p = repo.dirstate.parents()
1675 1676
1676 1677 displayer = cmdutil.show_changeset(ui, repo, opts)
1677 1678 for n in p:
1678 1679 if n != nullid:
1679 1680 displayer.show(changenode=n)
1680 1681
1681 1682 def paths(ui, repo, search=None):
1682 1683 """show definition of symbolic path names
1683 1684
1684 1685 Show definition of symbolic path name NAME. If no name is given, show
1685 1686 definition of available names.
1686 1687
1687 1688 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1688 1689 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1689 1690 """
1690 1691 if search:
1691 1692 for name, path in ui.configitems("paths"):
1692 1693 if name == search:
1693 1694 ui.write("%s\n" % path)
1694 1695 return
1695 1696 ui.warn(_("not found!\n"))
1696 1697 return 1
1697 1698 else:
1698 1699 for name, path in ui.configitems("paths"):
1699 1700 ui.write("%s = %s\n" % (name, path))
1700 1701
1701 1702 def postincoming(ui, repo, modheads, optupdate):
1702 1703 if modheads == 0:
1703 1704 return
1704 1705 if optupdate:
1705 1706 if modheads == 1:
1706 1707 return hg.update(repo, repo.changelog.tip()) # update
1707 1708 else:
1708 1709 ui.status(_("not updating, since new heads added\n"))
1709 1710 if modheads > 1:
1710 1711 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
1711 1712 else:
1712 1713 ui.status(_("(run 'hg update' to get a working copy)\n"))
1713 1714
1714 1715 def pull(ui, repo, source="default", **opts):
1715 1716 """pull changes from the specified source
1716 1717
1717 1718 Pull changes from a remote repository to a local one.
1718 1719
1719 1720 This finds all changes from the repository at the specified path
1720 1721 or URL and adds them to the local repository. By default, this
1721 1722 does not update the copy of the project in the working directory.
1722 1723
1723 1724 Valid URLs are of the form:
1724 1725
1725 1726 local/filesystem/path (or file://local/filesystem/path)
1726 1727 http://[user@]host[:port]/[path]
1727 1728 https://[user@]host[:port]/[path]
1728 1729 ssh://[user@]host[:port]/[path]
1729 1730 static-http://host[:port]/[path]
1730 1731
1731 1732 Paths in the local filesystem can either point to Mercurial
1732 1733 repositories or to bundle files (as created by 'hg bundle' or
1733 1734 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
1734 1735 allows access to a Mercurial repository where you simply use a web
1735 1736 server to publish the .hg directory as static content.
1736 1737
1737 1738 Some notes about using SSH with Mercurial:
1738 1739 - SSH requires an accessible shell account on the destination machine
1739 1740 and a copy of hg in the remote path or specified with as remotecmd.
1740 1741 - path is relative to the remote user's home directory by default.
1741 1742 Use an extra slash at the start of a path to specify an absolute path:
1742 1743 ssh://example.com//tmp/repository
1743 1744 - Mercurial doesn't use its own compression via SSH; the right thing
1744 1745 to do is to configure it in your ~/.ssh/config, e.g.:
1745 1746 Host *.mylocalnetwork.example.com
1746 1747 Compression no
1747 1748 Host *
1748 1749 Compression yes
1749 1750 Alternatively specify "ssh -C" as your ssh command in your hgrc or
1750 1751 with the --ssh command line option.
1751 1752 """
1752 1753 source = ui.expandpath(source)
1753 1754 setremoteconfig(ui, opts)
1754 1755
1755 1756 other = hg.repository(ui, source)
1756 1757 ui.status(_('pulling from %s\n') % (source))
1757 1758 revs = None
1758 1759 if opts['rev']:
1759 1760 if 'lookup' in other.capabilities:
1760 1761 revs = [other.lookup(rev) for rev in opts['rev']]
1761 1762 else:
1762 1763 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
1763 1764 raise util.Abort(error)
1764 1765 modheads = repo.pull(other, heads=revs, force=opts['force'])
1765 1766 return postincoming(ui, repo, modheads, opts['update'])
1766 1767
1767 1768 def push(ui, repo, dest=None, **opts):
1768 1769 """push changes to the specified destination
1769 1770
1770 1771 Push changes from the local repository to the given destination.
1771 1772
1772 1773 This is the symmetrical operation for pull. It helps to move
1773 1774 changes from the current repository to a different one. If the
1774 1775 destination is local this is identical to a pull in that directory
1775 1776 from the current one.
1776 1777
1777 1778 By default, push will refuse to run if it detects the result would
1778 1779 increase the number of remote heads. This generally indicates the
1779 1780 the client has forgotten to sync and merge before pushing.
1780 1781
1781 1782 Valid URLs are of the form:
1782 1783
1783 1784 local/filesystem/path (or file://local/filesystem/path)
1784 1785 ssh://[user@]host[:port]/[path]
1785 1786 http://[user@]host[:port]/[path]
1786 1787 https://[user@]host[:port]/[path]
1787 1788
1788 1789 Look at the help text for the pull command for important details
1789 1790 about ssh:// URLs.
1790 1791
1791 1792 Pushing to http:// and https:// URLs is only possible, if this
1792 1793 feature is explicitly enabled on the remote Mercurial server.
1793 1794 """
1794 1795 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1795 1796 setremoteconfig(ui, opts)
1796 1797
1797 1798 other = hg.repository(ui, dest)
1798 1799 ui.status('pushing to %s\n' % (dest))
1799 1800 revs = None
1800 1801 if opts['rev']:
1801 1802 revs = [repo.lookup(rev) for rev in opts['rev']]
1802 1803 r = repo.push(other, opts['force'], revs=revs)
1803 1804 return r == 0
1804 1805
1805 1806 def rawcommit(ui, repo, *pats, **opts):
1806 1807 """raw commit interface (DEPRECATED)
1807 1808
1808 1809 (DEPRECATED)
1809 1810 Lowlevel commit, for use in helper scripts.
1810 1811
1811 1812 This command is not intended to be used by normal users, as it is
1812 1813 primarily useful for importing from other SCMs.
1813 1814
1814 1815 This command is now deprecated and will be removed in a future
1815 1816 release, please use debugsetparents and commit instead.
1816 1817 """
1817 1818
1818 1819 ui.warn(_("(the rawcommit command is deprecated)\n"))
1819 1820
1820 1821 message = logmessage(opts)
1821 1822
1822 1823 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
1823 1824 if opts['files']:
1824 1825 files += open(opts['files']).read().splitlines()
1825 1826
1826 1827 parents = [repo.lookup(p) for p in opts['parent']]
1827 1828
1828 1829 try:
1829 1830 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
1830 1831 except ValueError, inst:
1831 1832 raise util.Abort(str(inst))
1832 1833
1833 1834 def recover(ui, repo):
1834 1835 """roll back an interrupted transaction
1835 1836
1836 1837 Recover from an interrupted commit or pull.
1837 1838
1838 1839 This command tries to fix the repository status after an interrupted
1839 1840 operation. It should only be necessary when Mercurial suggests it.
1840 1841 """
1841 1842 if repo.recover():
1842 1843 return hg.verify(repo)
1843 1844 return 1
1844 1845
1845 1846 def remove(ui, repo, *pats, **opts):
1846 1847 """remove the specified files on the next commit
1847 1848
1848 1849 Schedule the indicated files for removal from the repository.
1849 1850
1850 1851 This command schedules the files to be removed at the next commit.
1851 1852 This only removes files from the current branch, not from the
1852 1853 entire project history. If the files still exist in the working
1853 1854 directory, they will be deleted from it. If invoked with --after,
1854 1855 files that have been manually deleted are marked as removed.
1855 1856
1856 1857 Modified files and added files are not removed by default. To
1857 1858 remove them, use the -f/--force option.
1858 1859 """
1859 1860 names = []
1860 1861 if not opts['after'] and not pats:
1861 1862 raise util.Abort(_('no files specified'))
1862 1863 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1863 1864 exact = dict.fromkeys(files)
1864 1865 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
1865 1866 modified, added, removed, deleted, unknown = mardu
1866 1867 remove, forget = [], []
1867 1868 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
1868 1869 reason = None
1869 1870 if abs not in deleted and opts['after']:
1870 1871 reason = _('is still present')
1871 1872 elif abs in modified and not opts['force']:
1872 1873 reason = _('is modified (use -f to force removal)')
1873 1874 elif abs in added:
1874 1875 if opts['force']:
1875 1876 forget.append(abs)
1876 1877 continue
1877 1878 reason = _('has been marked for add (use -f to force removal)')
1878 1879 elif abs in unknown:
1879 1880 reason = _('is not managed')
1880 1881 elif abs in removed:
1881 1882 continue
1882 1883 if reason:
1883 1884 if exact:
1884 1885 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
1885 1886 else:
1886 1887 if ui.verbose or not exact:
1887 1888 ui.status(_('removing %s\n') % rel)
1888 1889 remove.append(abs)
1889 1890 repo.forget(forget)
1890 1891 repo.remove(remove, unlink=not opts['after'])
1891 1892
1892 1893 def rename(ui, repo, *pats, **opts):
1893 1894 """rename files; equivalent of copy + remove
1894 1895
1895 1896 Mark dest as copies of sources; mark sources for deletion. If
1896 1897 dest is a directory, copies are put in that directory. If dest is
1897 1898 a file, there can only be one source.
1898 1899
1899 1900 By default, this command copies the contents of files as they
1900 1901 stand in the working directory. If invoked with --after, the
1901 1902 operation is recorded, but no copying is performed.
1902 1903
1903 1904 This command takes effect in the next commit.
1904 1905 """
1905 1906 wlock = repo.wlock(0)
1906 1907 errs, copied = docopy(ui, repo, pats, opts, wlock)
1907 1908 names = []
1908 1909 for abs, rel, exact in copied:
1909 1910 if ui.verbose or not exact:
1910 1911 ui.status(_('removing %s\n') % rel)
1911 1912 names.append(abs)
1912 1913 if not opts.get('dry_run'):
1913 1914 repo.remove(names, True, wlock)
1914 1915 return errs
1915 1916
1916 1917 def revert(ui, repo, *pats, **opts):
1917 1918 """revert files or dirs to their states as of some revision
1918 1919
1919 1920 With no revision specified, revert the named files or directories
1920 1921 to the contents they had in the parent of the working directory.
1921 1922 This restores the contents of the affected files to an unmodified
1922 1923 state. If the working directory has two parents, you must
1923 1924 explicitly specify the revision to revert to.
1924 1925
1925 1926 Modified files are saved with a .orig suffix before reverting.
1926 1927 To disable these backups, use --no-backup.
1927 1928
1928 1929 Using the -r option, revert the given files or directories to their
1929 1930 contents as of a specific revision. This can be helpful to "roll
1930 1931 back" some or all of a change that should not have been committed.
1931 1932
1932 1933 Revert modifies the working directory. It does not commit any
1933 1934 changes, or change the parent of the working directory. If you
1934 1935 revert to a revision other than the parent of the working
1935 1936 directory, the reverted files will thus appear modified
1936 1937 afterwards.
1937 1938
1938 1939 If a file has been deleted, it is recreated. If the executable
1939 1940 mode of a file was changed, it is reset.
1940 1941
1941 1942 If names are given, all files matching the names are reverted.
1942 1943
1943 1944 If no arguments are given, no files are reverted.
1944 1945 """
1945 1946
1947 if opts["date"]:
1948 if opts["rev"]:
1949 raise util.Abort(_("you can't specify a revision and a date"))
1950 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
1951
1946 1952 if not pats and not opts['all']:
1947 1953 raise util.Abort(_('no files or directories specified; '
1948 1954 'use --all to revert the whole repo'))
1949 1955
1950 1956 parent, p2 = repo.dirstate.parents()
1951 1957 if not opts['rev'] and p2 != nullid:
1952 1958 raise util.Abort(_('uncommitted merge - please provide a '
1953 1959 'specific revision'))
1954 1960 node = repo.changectx(opts['rev']).node()
1955 1961 mf = repo.manifest.read(repo.changelog.read(node)[0])
1956 1962 if node == parent:
1957 1963 pmf = mf
1958 1964 else:
1959 1965 pmf = None
1960 1966
1961 1967 wlock = repo.wlock()
1962 1968
1963 1969 # need all matching names in dirstate and manifest of target rev,
1964 1970 # so have to walk both. do not print errors if files exist in one
1965 1971 # but not other.
1966 1972
1967 1973 names = {}
1968 1974 target_only = {}
1969 1975
1970 1976 # walk dirstate.
1971 1977
1972 1978 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
1973 1979 badmatch=mf.has_key):
1974 1980 names[abs] = (rel, exact)
1975 1981 if src == 'b':
1976 1982 target_only[abs] = True
1977 1983
1978 1984 # walk target manifest.
1979 1985
1980 1986 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1981 1987 badmatch=names.has_key):
1982 1988 if abs in names: continue
1983 1989 names[abs] = (rel, exact)
1984 1990 target_only[abs] = True
1985 1991
1986 1992 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
1987 1993 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
1988 1994
1989 1995 revert = ([], _('reverting %s\n'))
1990 1996 add = ([], _('adding %s\n'))
1991 1997 remove = ([], _('removing %s\n'))
1992 1998 forget = ([], _('forgetting %s\n'))
1993 1999 undelete = ([], _('undeleting %s\n'))
1994 2000 update = {}
1995 2001
1996 2002 disptable = (
1997 2003 # dispatch table:
1998 2004 # file state
1999 2005 # action if in target manifest
2000 2006 # action if not in target manifest
2001 2007 # make backup if in target manifest
2002 2008 # make backup if not in target manifest
2003 2009 (modified, revert, remove, True, True),
2004 2010 (added, revert, forget, True, False),
2005 2011 (removed, undelete, None, False, False),
2006 2012 (deleted, revert, remove, False, False),
2007 2013 (unknown, add, None, True, False),
2008 2014 (target_only, add, None, False, False),
2009 2015 )
2010 2016
2011 2017 entries = names.items()
2012 2018 entries.sort()
2013 2019
2014 2020 for abs, (rel, exact) in entries:
2015 2021 mfentry = mf.get(abs)
2016 2022 def handle(xlist, dobackup):
2017 2023 xlist[0].append(abs)
2018 2024 update[abs] = 1
2019 2025 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2020 2026 bakname = "%s.orig" % rel
2021 2027 ui.note(_('saving current version of %s as %s\n') %
2022 2028 (rel, bakname))
2023 2029 if not opts.get('dry_run'):
2024 2030 util.copyfile(rel, bakname)
2025 2031 if ui.verbose or not exact:
2026 2032 ui.status(xlist[1] % rel)
2027 2033 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2028 2034 if abs not in table: continue
2029 2035 # file has changed in dirstate
2030 2036 if mfentry:
2031 2037 handle(hitlist, backuphit)
2032 2038 elif misslist is not None:
2033 2039 handle(misslist, backupmiss)
2034 2040 else:
2035 2041 if exact: ui.warn(_('file not managed: %s\n') % rel)
2036 2042 break
2037 2043 else:
2038 2044 # file has not changed in dirstate
2039 2045 if node == parent:
2040 2046 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2041 2047 continue
2042 2048 if pmf is None:
2043 2049 # only need parent manifest in this unlikely case,
2044 2050 # so do not read by default
2045 2051 pmf = repo.manifest.read(repo.changelog.read(parent)[0])
2046 2052 if abs in pmf:
2047 2053 if mfentry:
2048 2054 # if version of file is same in parent and target
2049 2055 # manifests, do nothing
2050 2056 if pmf[abs] != mfentry:
2051 2057 handle(revert, False)
2052 2058 else:
2053 2059 handle(remove, False)
2054 2060
2055 2061 if not opts.get('dry_run'):
2056 2062 repo.dirstate.forget(forget[0])
2057 2063 r = hg.revert(repo, node, update.has_key, wlock)
2058 2064 repo.dirstate.update(add[0], 'a')
2059 2065 repo.dirstate.update(undelete[0], 'n')
2060 2066 repo.dirstate.update(remove[0], 'r')
2061 2067 return r
2062 2068
2063 2069 def rollback(ui, repo):
2064 2070 """roll back the last transaction in this repository
2065 2071
2066 2072 Roll back the last transaction in this repository, restoring the
2067 2073 project to its state prior to the transaction.
2068 2074
2069 2075 Transactions are used to encapsulate the effects of all commands
2070 2076 that create new changesets or propagate existing changesets into a
2071 2077 repository. For example, the following commands are transactional,
2072 2078 and their effects can be rolled back:
2073 2079
2074 2080 commit
2075 2081 import
2076 2082 pull
2077 2083 push (with this repository as destination)
2078 2084 unbundle
2079 2085
2080 2086 This command should be used with care. There is only one level of
2081 2087 rollback, and there is no way to undo a rollback.
2082 2088
2083 2089 This command is not intended for use on public repositories. Once
2084 2090 changes are visible for pull by other users, rolling a transaction
2085 2091 back locally is ineffective (someone else may already have pulled
2086 2092 the changes). Furthermore, a race is possible with readers of the
2087 2093 repository; for example an in-progress pull from the repository
2088 2094 may fail if a rollback is performed.
2089 2095 """
2090 2096 repo.rollback()
2091 2097
2092 2098 def root(ui, repo):
2093 2099 """print the root (top) of the current working dir
2094 2100
2095 2101 Print the root directory of the current repository.
2096 2102 """
2097 2103 ui.write(repo.root + "\n")
2098 2104
2099 2105 def serve(ui, repo, **opts):
2100 2106 """export the repository via HTTP
2101 2107
2102 2108 Start a local HTTP repository browser and pull server.
2103 2109
2104 2110 By default, the server logs accesses to stdout and errors to
2105 2111 stderr. Use the "-A" and "-E" options to log to files.
2106 2112 """
2107 2113
2108 2114 if opts["stdio"]:
2109 2115 if repo is None:
2110 2116 raise hg.RepoError(_("There is no Mercurial repository here"
2111 2117 " (.hg not found)"))
2112 2118 s = sshserver.sshserver(ui, repo)
2113 2119 s.serve_forever()
2114 2120
2115 2121 optlist = ("name templates style address port ipv6"
2116 2122 " accesslog errorlog webdir_conf")
2117 2123 for o in optlist.split():
2118 2124 if opts[o]:
2119 2125 ui.setconfig("web", o, str(opts[o]))
2120 2126
2121 2127 if repo is None and not ui.config("web", "webdir_conf"):
2122 2128 raise hg.RepoError(_("There is no Mercurial repository here"
2123 2129 " (.hg not found)"))
2124 2130
2125 2131 if opts['daemon'] and not opts['daemon_pipefds']:
2126 2132 rfd, wfd = os.pipe()
2127 2133 args = sys.argv[:]
2128 2134 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2129 2135 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2130 2136 args[0], args)
2131 2137 os.close(wfd)
2132 2138 os.read(rfd, 1)
2133 2139 os._exit(0)
2134 2140
2135 2141 httpd = hgweb.server.create_server(ui, repo)
2136 2142
2137 2143 if ui.verbose:
2138 2144 if httpd.port != 80:
2139 2145 ui.status(_('listening at http://%s:%d/\n') %
2140 2146 (httpd.addr, httpd.port))
2141 2147 else:
2142 2148 ui.status(_('listening at http://%s/\n') % httpd.addr)
2143 2149
2144 2150 if opts['pid_file']:
2145 2151 fp = open(opts['pid_file'], 'w')
2146 2152 fp.write(str(os.getpid()) + '\n')
2147 2153 fp.close()
2148 2154
2149 2155 if opts['daemon_pipefds']:
2150 2156 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2151 2157 os.close(rfd)
2152 2158 os.write(wfd, 'y')
2153 2159 os.close(wfd)
2154 2160 sys.stdout.flush()
2155 2161 sys.stderr.flush()
2156 2162 fd = os.open(util.nulldev, os.O_RDWR)
2157 2163 if fd != 0: os.dup2(fd, 0)
2158 2164 if fd != 1: os.dup2(fd, 1)
2159 2165 if fd != 2: os.dup2(fd, 2)
2160 2166 if fd not in (0, 1, 2): os.close(fd)
2161 2167
2162 2168 httpd.serve_forever()
2163 2169
2164 2170 def status(ui, repo, *pats, **opts):
2165 2171 """show changed files in the working directory
2166 2172
2167 2173 Show status of files in the repository. If names are given, only
2168 2174 files that match are shown. Files that are clean or ignored, are
2169 2175 not listed unless -c (clean), -i (ignored) or -A is given.
2170 2176
2171 2177 If one revision is given, it is used as the base revision.
2172 2178 If two revisions are given, the difference between them is shown.
2173 2179
2174 2180 The codes used to show the status of files are:
2175 2181 M = modified
2176 2182 A = added
2177 2183 R = removed
2178 2184 C = clean
2179 2185 ! = deleted, but still tracked
2180 2186 ? = not tracked
2181 2187 I = ignored (not shown by default)
2182 2188 = the previous added file was copied from here
2183 2189 """
2184 2190
2185 2191 all = opts['all']
2186 2192 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2187 2193
2188 2194 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2189 2195 cwd = (pats and repo.getcwd()) or ''
2190 2196 modified, added, removed, deleted, unknown, ignored, clean = [
2191 2197 [util.pathto(cwd, x) for x in n]
2192 2198 for n in repo.status(node1=node1, node2=node2, files=files,
2193 2199 match=matchfn,
2194 2200 list_ignored=all or opts['ignored'],
2195 2201 list_clean=all or opts['clean'])]
2196 2202
2197 2203 changetypes = (('modified', 'M', modified),
2198 2204 ('added', 'A', added),
2199 2205 ('removed', 'R', removed),
2200 2206 ('deleted', '!', deleted),
2201 2207 ('unknown', '?', unknown),
2202 2208 ('ignored', 'I', ignored))
2203 2209
2204 2210 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2205 2211
2206 2212 end = opts['print0'] and '\0' or '\n'
2207 2213
2208 2214 for opt, char, changes in ([ct for ct in explicit_changetypes
2209 2215 if all or opts[ct[0]]]
2210 2216 or changetypes):
2211 2217 if opts['no_status']:
2212 2218 format = "%%s%s" % end
2213 2219 else:
2214 2220 format = "%s %%s%s" % (char, end)
2215 2221
2216 2222 for f in changes:
2217 2223 ui.write(format % f)
2218 2224 if ((all or opts.get('copies')) and not opts.get('no_status')):
2219 2225 copied = repo.dirstate.copied(f)
2220 2226 if copied:
2221 2227 ui.write(' %s%s' % (copied, end))
2222 2228
2223 2229 def tag(ui, repo, name, rev_=None, **opts):
2224 2230 """add a tag for the current tip or a given revision
2225 2231
2226 2232 Name a particular revision using <name>.
2227 2233
2228 2234 Tags are used to name particular revisions of the repository and are
2229 2235 very useful to compare different revision, to go back to significant
2230 2236 earlier versions or to mark branch points as releases, etc.
2231 2237
2232 2238 If no revision is given, the parent of the working directory is used.
2233 2239
2234 2240 To facilitate version control, distribution, and merging of tags,
2235 2241 they are stored as a file named ".hgtags" which is managed
2236 2242 similarly to other project files and can be hand-edited if
2237 2243 necessary. The file '.hg/localtags' is used for local tags (not
2238 2244 shared among repositories).
2239 2245 """
2240 2246 if name in ['tip', '.', 'null']:
2241 2247 raise util.Abort(_("the name '%s' is reserved") % name)
2242 2248 if rev_ is not None:
2243 2249 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2244 2250 "please use 'hg tag [-r REV] NAME' instead\n"))
2245 2251 if opts['rev']:
2246 2252 raise util.Abort(_("use only one form to specify the revision"))
2247 2253 if opts['rev']:
2248 2254 rev_ = opts['rev']
2249 2255 if not rev_ and repo.dirstate.parents()[1] != nullid:
2250 2256 raise util.Abort(_('uncommitted merge - please provide a '
2251 2257 'specific revision'))
2252 2258 r = repo.changectx(rev_).node()
2253 2259
2254 2260 message = opts['message']
2255 2261 if not message:
2256 2262 message = _('Added tag %s for changeset %s') % (name, short(r))
2257 2263
2258 2264 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2259 2265
2260 2266 def tags(ui, repo):
2261 2267 """list repository tags
2262 2268
2263 2269 List the repository tags.
2264 2270
2265 2271 This lists both regular and local tags.
2266 2272 """
2267 2273
2268 2274 l = repo.tagslist()
2269 2275 l.reverse()
2270 2276 hexfunc = ui.debugflag and hex or short
2271 2277 for t, n in l:
2272 2278 try:
2273 2279 r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
2274 2280 except KeyError:
2275 2281 r = " ?:?"
2276 2282 if ui.quiet:
2277 2283 ui.write("%s\n" % t)
2278 2284 else:
2279 2285 t = util.localsub(t, 30)
2280 2286 t += " " * (30 - util.locallen(t))
2281 2287 ui.write("%s %s\n" % (t, r))
2282 2288
2283 2289 def tip(ui, repo, **opts):
2284 2290 """show the tip revision
2285 2291
2286 2292 Show the tip revision.
2287 2293 """
2288 2294 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2289 2295
2290 2296 def unbundle(ui, repo, fname, **opts):
2291 2297 """apply a changegroup file
2292 2298
2293 2299 Apply a compressed changegroup file generated by the bundle
2294 2300 command.
2295 2301 """
2296 2302 gen = changegroup.readbundle(urllib.urlopen(fname))
2297 2303 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2298 2304 return postincoming(ui, repo, modheads, opts['update'])
2299 2305
2300 def update(ui, repo, node=None, clean=False, branch=None):
2306 def update(ui, repo, node=None, clean=False, branch=None, date=None):
2301 2307 """update or merge working directory
2302 2308
2303 2309 Update the working directory to the specified revision.
2304 2310
2305 2311 If there are no outstanding changes in the working directory and
2306 2312 there is a linear relationship between the current version and the
2307 2313 requested version, the result is the requested version.
2308 2314
2309 2315 To merge the working directory with another revision, use the
2310 2316 merge command.
2311 2317
2312 2318 By default, update will refuse to run if doing so would require
2313 2319 merging or discarding local changes.
2314 2320 """
2321 if date:
2322 if node:
2323 raise util.Abort(_("you can't specify a revision and a date"))
2324 node = cmdutil.finddate(ui, repo, date)
2325
2315 2326 node = _lookup(repo, node, branch)
2316 2327 if clean:
2317 2328 return hg.clean(repo, node)
2318 2329 else:
2319 2330 return hg.update(repo, node)
2320 2331
2321 2332 def _lookup(repo, node, branch=None):
2322 2333 if branch:
2323 2334 repo.ui.warn(_("the --branch option is deprecated, "
2324 2335 "please use 'hg branch' instead\n"))
2325 2336 br = repo.branchlookup(branch=branch)
2326 2337 found = []
2327 2338 for x in br:
2328 2339 if branch in br[x]:
2329 2340 found.append(x)
2330 2341 if len(found) > 1:
2331 2342 repo.ui.warn(_("Found multiple heads for %s\n") % branch)
2332 2343 for x in found:
2333 2344 cmdutil.show_changeset(ui, repo, {}).show(changenode=x)
2334 2345 raise util.Abort("")
2335 2346 if len(found) == 1:
2336 2347 node = found[0]
2337 2348 repo.ui.warn(_("Using head %s for branch %s\n")
2338 2349 % (short(node), branch))
2339 2350 else:
2340 2351 raise util.Abort(_("branch %s not found") % branch)
2341 2352 else:
2342 2353 node = node and repo.lookup(node) or repo.changelog.tip()
2343 2354 return node
2344 2355
2345 2356 def verify(ui, repo):
2346 2357 """verify the integrity of the repository
2347 2358
2348 2359 Verify the integrity of the current repository.
2349 2360
2350 2361 This will perform an extensive check of the repository's
2351 2362 integrity, validating the hashes and checksums of each entry in
2352 2363 the changelog, manifest, and tracked files, as well as the
2353 2364 integrity of their crosslinks and indices.
2354 2365 """
2355 2366 return hg.verify(repo)
2356 2367
2357 2368 def version_(ui):
2358 2369 """output version and copyright information"""
2359 2370 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2360 2371 % version.get_version())
2361 2372 ui.status(_(
2362 2373 "\nCopyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>\n"
2363 2374 "This is free software; see the source for copying conditions. "
2364 2375 "There is NO\nwarranty; "
2365 2376 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2366 2377 ))
2367 2378
2368 2379 # Command options and aliases are listed here, alphabetically
2369 2380
2370 2381 globalopts = [
2371 2382 ('R', 'repository', '',
2372 2383 _('repository root directory or symbolic path name')),
2373 2384 ('', 'cwd', '', _('change working directory')),
2374 2385 ('y', 'noninteractive', None,
2375 2386 _('do not prompt, assume \'yes\' for any required answers')),
2376 2387 ('q', 'quiet', None, _('suppress output')),
2377 2388 ('v', 'verbose', None, _('enable additional output')),
2378 2389 ('', 'config', [], _('set/override config option')),
2379 2390 ('', 'debug', None, _('enable debugging output')),
2380 2391 ('', 'debugger', None, _('start debugger')),
2381 2392 ('', 'encoding', util._encoding, _('set the charset encoding')),
2382 2393 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2383 2394 ('', 'lsprof', None, _('print improved command execution profile')),
2384 2395 ('', 'traceback', None, _('print traceback on exception')),
2385 2396 ('', 'time', None, _('time how long the command takes')),
2386 2397 ('', 'profile', None, _('print command execution profile')),
2387 2398 ('', 'version', None, _('output version information and exit')),
2388 2399 ('h', 'help', None, _('display help and exit')),
2389 2400 ]
2390 2401
2391 2402 dryrunopts = [('n', 'dry-run', None,
2392 2403 _('do not perform actions, just print output'))]
2393 2404
2394 2405 remoteopts = [
2395 2406 ('e', 'ssh', '', _('specify ssh command to use')),
2396 2407 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2397 2408 ]
2398 2409
2399 2410 walkopts = [
2400 2411 ('I', 'include', [], _('include names matching the given patterns')),
2401 2412 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2402 2413 ]
2403 2414
2404 2415 table = {
2405 2416 "^add":
2406 2417 (add,
2407 2418 walkopts + dryrunopts,
2408 2419 _('hg add [OPTION]... [FILE]...')),
2409 2420 "addremove":
2410 2421 (addremove,
2411 2422 [('s', 'similarity', '',
2412 2423 _('guess renamed files by similarity (0<=s<=100)')),
2413 2424 ] + walkopts + dryrunopts,
2414 2425 _('hg addremove [OPTION]... [FILE]...')),
2415 2426 "^annotate":
2416 2427 (annotate,
2417 2428 [('r', 'rev', '', _('annotate the specified revision')),
2418 2429 ('f', 'follow', None, _('follow file copies and renames')),
2419 2430 ('a', 'text', None, _('treat all files as text')),
2420 2431 ('u', 'user', None, _('list the author')),
2421 2432 ('d', 'date', None, _('list the date')),
2422 2433 ('n', 'number', None, _('list the revision number (default)')),
2423 2434 ('c', 'changeset', None, _('list the changeset')),
2424 2435 ] + walkopts,
2425 2436 _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
2426 2437 "archive":
2427 2438 (archive,
2428 2439 [('', 'no-decode', None, _('do not pass files through decoders')),
2429 2440 ('p', 'prefix', '', _('directory prefix for files in archive')),
2430 2441 ('r', 'rev', '', _('revision to distribute')),
2431 2442 ('t', 'type', '', _('type of distribution to create')),
2432 2443 ] + walkopts,
2433 2444 _('hg archive [OPTION]... DEST')),
2434 2445 "backout":
2435 2446 (backout,
2436 2447 [('', 'merge', None,
2437 2448 _('merge with old dirstate parent after backout')),
2438 2449 ('m', 'message', '', _('use <text> as commit message')),
2439 2450 ('l', 'logfile', '', _('read commit message from <file>')),
2440 2451 ('d', 'date', '', _('record datecode as commit date')),
2441 2452 ('', 'parent', '', _('parent to choose when backing out merge')),
2442 2453 ('u', 'user', '', _('record user as committer')),
2443 2454 ] + walkopts,
2444 2455 _('hg backout [OPTION]... REV')),
2445 2456 "branch": (branch, [], _('hg branch [NAME]')),
2446 2457 "branches": (branches, [], _('hg branches')),
2447 2458 "bundle":
2448 2459 (bundle,
2449 2460 [('f', 'force', None,
2450 2461 _('run even when remote repository is unrelated')),
2451 2462 ('r', 'rev', [],
2452 2463 _('a changeset you would like to bundle')),
2453 2464 ('', 'base', [],
2454 2465 _('a base changeset to specify instead of a destination')),
2455 2466 ] + remoteopts,
2456 2467 _('hg bundle [--base REV]... [--rev REV]... FILE [DEST]')),
2457 2468 "cat":
2458 2469 (cat,
2459 2470 [('o', 'output', '', _('print output to file with formatted name')),
2460 2471 ('r', 'rev', '', _('print the given revision')),
2461 2472 ] + walkopts,
2462 2473 _('hg cat [OPTION]... FILE...')),
2463 2474 "^clone":
2464 2475 (clone,
2465 2476 [('U', 'noupdate', None, _('do not update the new working directory')),
2466 2477 ('r', 'rev', [],
2467 2478 _('a changeset you would like to have after cloning')),
2468 2479 ('', 'pull', None, _('use pull protocol to copy metadata')),
2469 2480 ('', 'uncompressed', None,
2470 2481 _('use uncompressed transfer (fast over LAN)')),
2471 2482 ] + remoteopts,
2472 2483 _('hg clone [OPTION]... SOURCE [DEST]')),
2473 2484 "^commit|ci":
2474 2485 (commit,
2475 2486 [('A', 'addremove', None,
2476 2487 _('mark new/missing files as added/removed before committing')),
2477 2488 ('m', 'message', '', _('use <text> as commit message')),
2478 2489 ('l', 'logfile', '', _('read the commit message from <file>')),
2479 2490 ('d', 'date', '', _('record datecode as commit date')),
2480 2491 ('u', 'user', '', _('record user as commiter')),
2481 2492 ] + walkopts,
2482 2493 _('hg commit [OPTION]... [FILE]...')),
2483 2494 "copy|cp":
2484 2495 (copy,
2485 2496 [('A', 'after', None, _('record a copy that has already occurred')),
2486 2497 ('f', 'force', None,
2487 2498 _('forcibly copy over an existing managed file')),
2488 2499 ] + walkopts + dryrunopts,
2489 2500 _('hg copy [OPTION]... [SOURCE]... DEST')),
2490 2501 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2491 2502 "debugcomplete":
2492 2503 (debugcomplete,
2493 2504 [('o', 'options', None, _('show the command options'))],
2494 2505 _('debugcomplete [-o] CMD')),
2495 2506 "debugrebuildstate":
2496 2507 (debugrebuildstate,
2497 2508 [('r', 'rev', '', _('revision to rebuild to'))],
2498 2509 _('debugrebuildstate [-r REV] [REV]')),
2499 2510 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2500 2511 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2501 2512 "debugstate": (debugstate, [], _('debugstate')),
2502 2513 "debugdate": (debugdate,
2503 2514 [('e','extended', None, _('try extended date formats'))],
2504 2515 _('debugdata [-e] DATE [RANGE]')),
2505 2516 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2506 2517 "debugindex": (debugindex, [], _('debugindex FILE')),
2507 2518 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2508 2519 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2509 2520 "debugwalk":
2510 2521 (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')),
2511 2522 "^diff":
2512 2523 (diff,
2513 2524 [('r', 'rev', [], _('revision')),
2514 2525 ('a', 'text', None, _('treat all files as text')),
2515 2526 ('p', 'show-function', None,
2516 2527 _('show which function each change is in')),
2517 2528 ('g', 'git', None, _('use git extended diff format')),
2518 2529 ('', 'nodates', None, _("don't include dates in diff headers")),
2519 2530 ('w', 'ignore-all-space', None,
2520 2531 _('ignore white space when comparing lines')),
2521 2532 ('b', 'ignore-space-change', None,
2522 2533 _('ignore changes in the amount of white space')),
2523 2534 ('B', 'ignore-blank-lines', None,
2524 2535 _('ignore changes whose lines are all blank')),
2525 2536 ] + walkopts,
2526 2537 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2527 2538 "^export":
2528 2539 (export,
2529 2540 [('o', 'output', '', _('print output to file with formatted name')),
2530 2541 ('a', 'text', None, _('treat all files as text')),
2531 2542 ('g', 'git', None, _('use git extended diff format')),
2532 2543 ('', 'nodates', None, _("don't include dates in diff headers")),
2533 2544 ('', 'switch-parent', None, _('diff against the second parent'))],
2534 2545 _('hg export [-a] [-o OUTFILESPEC] REV...')),
2535 2546 "grep":
2536 2547 (grep,
2537 2548 [('0', 'print0', None, _('end fields with NUL')),
2538 2549 ('', 'all', None, _('print all revisions that match')),
2539 2550 ('f', 'follow', None,
2540 2551 _('follow changeset history, or file history across copies and renames')),
2541 2552 ('i', 'ignore-case', None, _('ignore case when matching')),
2542 2553 ('l', 'files-with-matches', None,
2543 2554 _('print only filenames and revs that match')),
2544 2555 ('n', 'line-number', None, _('print matching line numbers')),
2545 2556 ('r', 'rev', [], _('search in given revision range')),
2546 2557 ('u', 'user', None, _('print user who committed change')),
2547 2558 ] + walkopts,
2548 2559 _('hg grep [OPTION]... PATTERN [FILE]...')),
2549 2560 "heads":
2550 2561 (heads,
2551 2562 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2552 2563 ('', 'style', '', _('display using template map file')),
2553 2564 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2554 2565 ('', 'template', '', _('display with template'))],
2555 2566 _('hg heads [-r REV]')),
2556 2567 "help": (help_, [], _('hg help [COMMAND]')),
2557 2568 "identify|id": (identify, [], _('hg identify')),
2558 2569 "import|patch":
2559 2570 (import_,
2560 2571 [('p', 'strip', 1,
2561 2572 _('directory strip option for patch. This has the same\n'
2562 2573 'meaning as the corresponding patch option')),
2563 2574 ('m', 'message', '', _('use <text> as commit message')),
2564 2575 ('b', 'base', '', _('base path (DEPRECATED)')),
2565 2576 ('f', 'force', None,
2566 2577 _('skip check for outstanding uncommitted changes'))],
2567 2578 _('hg import [-p NUM] [-m MESSAGE] [-f] PATCH...')),
2568 2579 "incoming|in": (incoming,
2569 2580 [('M', 'no-merges', None, _('do not show merges')),
2570 2581 ('f', 'force', None,
2571 2582 _('run even when remote repository is unrelated')),
2572 2583 ('', 'style', '', _('display using template map file')),
2573 2584 ('n', 'newest-first', None, _('show newest record first')),
2574 2585 ('', 'bundle', '', _('file to store the bundles into')),
2575 2586 ('p', 'patch', None, _('show patch')),
2576 2587 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2577 2588 ('', 'template', '', _('display with template')),
2578 2589 ] + remoteopts,
2579 2590 _('hg incoming [-p] [-n] [-M] [-r REV]...'
2580 2591 ' [--bundle FILENAME] [SOURCE]')),
2581 2592 "^init":
2582 2593 (init, remoteopts, _('hg init [-e FILE] [--remotecmd FILE] [DEST]')),
2583 2594 "locate":
2584 2595 (locate,
2585 2596 [('r', 'rev', '', _('search the repository as it stood at rev')),
2586 2597 ('0', 'print0', None,
2587 2598 _('end filenames with NUL, for use with xargs')),
2588 2599 ('f', 'fullpath', None,
2589 2600 _('print complete paths from the filesystem root')),
2590 2601 ] + walkopts,
2591 2602 _('hg locate [OPTION]... [PATTERN]...')),
2592 2603 "^log|history":
2593 2604 (log,
2594 2605 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2595 2606 ('f', 'follow', None,
2596 2607 _('follow changeset history, or file history across copies and renames')),
2597 2608 ('', 'follow-first', None,
2598 2609 _('only follow the first parent of merge changesets')),
2599 2610 ('d', 'date', '', _('show revs matching date spec')),
2600 2611 ('C', 'copies', None, _('show copied files')),
2601 2612 ('k', 'keyword', [], _('search for a keyword')),
2602 2613 ('l', 'limit', '', _('limit number of changes displayed')),
2603 2614 ('r', 'rev', [], _('show the specified revision or range')),
2604 2615 ('', 'removed', None, _('include revs where files were removed')),
2605 2616 ('M', 'no-merges', None, _('do not show merges')),
2606 2617 ('', 'style', '', _('display using template map file')),
2607 2618 ('m', 'only-merges', None, _('show only merges')),
2608 2619 ('p', 'patch', None, _('show patch')),
2609 2620 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
2610 2621 ('', 'template', '', _('display with template')),
2611 2622 ] + walkopts,
2612 2623 _('hg log [OPTION]... [FILE]')),
2613 2624 "manifest": (manifest, [], _('hg manifest [REV]')),
2614 2625 "merge":
2615 2626 (merge,
2616 2627 [('b', 'branch', '', _('merge with head of a specific branch (DEPRECATED)')),
2617 2628 ('f', 'force', None, _('force a merge with outstanding changes'))],
2618 2629 _('hg merge [-f] [REV]')),
2619 2630 "outgoing|out": (outgoing,
2620 2631 [('M', 'no-merges', None, _('do not show merges')),
2621 2632 ('f', 'force', None,
2622 2633 _('run even when remote repository is unrelated')),
2623 2634 ('p', 'patch', None, _('show patch')),
2624 2635 ('', 'style', '', _('display using template map file')),
2625 2636 ('r', 'rev', [], _('a specific revision you would like to push')),
2626 2637 ('n', 'newest-first', None, _('show newest record first')),
2627 2638 ('', 'template', '', _('display with template')),
2628 2639 ] + remoteopts,
2629 2640 _('hg outgoing [-M] [-p] [-n] [-r REV]... [DEST]')),
2630 2641 "^parents":
2631 2642 (parents,
2632 2643 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2633 2644 ('r', 'rev', '', _('show parents from the specified rev')),
2634 2645 ('', 'style', '', _('display using template map file')),
2635 2646 ('', 'template', '', _('display with template'))],
2636 2647 _('hg parents [-r REV] [FILE]')),
2637 2648 "paths": (paths, [], _('hg paths [NAME]')),
2638 2649 "^pull":
2639 2650 (pull,
2640 2651 [('u', 'update', None,
2641 2652 _('update to new tip if changesets were pulled')),
2642 2653 ('f', 'force', None,
2643 2654 _('run even when remote repository is unrelated')),
2644 2655 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2645 2656 ] + remoteopts,
2646 2657 _('hg pull [-u] [-r REV]... [-e FILE] [--remotecmd FILE] [SOURCE]')),
2647 2658 "^push":
2648 2659 (push,
2649 2660 [('f', 'force', None, _('force push')),
2650 2661 ('r', 'rev', [], _('a specific revision you would like to push')),
2651 2662 ] + remoteopts,
2652 2663 _('hg push [-f] [-r REV]... [-e FILE] [--remotecmd FILE] [DEST]')),
2653 2664 "debugrawcommit|rawcommit":
2654 2665 (rawcommit,
2655 2666 [('p', 'parent', [], _('parent')),
2656 2667 ('d', 'date', '', _('date code')),
2657 2668 ('u', 'user', '', _('user')),
2658 2669 ('F', 'files', '', _('file list')),
2659 2670 ('m', 'message', '', _('commit message')),
2660 2671 ('l', 'logfile', '', _('commit message file'))],
2661 2672 _('hg debugrawcommit [OPTION]... [FILE]...')),
2662 2673 "recover": (recover, [], _('hg recover')),
2663 2674 "^remove|rm":
2664 2675 (remove,
2665 2676 [('A', 'after', None, _('record remove that has already occurred')),
2666 2677 ('f', 'force', None, _('remove file even if modified')),
2667 2678 ] + walkopts,
2668 2679 _('hg remove [OPTION]... FILE...')),
2669 2680 "rename|mv":
2670 2681 (rename,
2671 2682 [('A', 'after', None, _('record a rename that has already occurred')),
2672 2683 ('f', 'force', None,
2673 2684 _('forcibly copy over an existing managed file')),
2674 2685 ] + walkopts + dryrunopts,
2675 2686 _('hg rename [OPTION]... SOURCE... DEST')),
2676 2687 "^revert":
2677 2688 (revert,
2678 2689 [('a', 'all', None, _('revert all changes when no arguments given')),
2690 ('d', 'date', '', _('tipmost revision matching date')),
2679 2691 ('r', 'rev', '', _('revision to revert to')),
2680 2692 ('', 'no-backup', None, _('do not save backup copies of files')),
2681 2693 ] + walkopts + dryrunopts,
2682 2694 _('hg revert [-r REV] [NAME]...')),
2683 2695 "rollback": (rollback, [], _('hg rollback')),
2684 2696 "root": (root, [], _('hg root')),
2685 2697 "showconfig|debugconfig":
2686 2698 (showconfig,
2687 2699 [('u', 'untrusted', None, _('show untrusted configuration options'))],
2688 2700 _('showconfig [-u] [NAME]...')),
2689 2701 "^serve":
2690 2702 (serve,
2691 2703 [('A', 'accesslog', '', _('name of access log file to write to')),
2692 2704 ('d', 'daemon', None, _('run server in background')),
2693 2705 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
2694 2706 ('E', 'errorlog', '', _('name of error log file to write to')),
2695 2707 ('p', 'port', 0, _('port to use (default: 8000)')),
2696 2708 ('a', 'address', '', _('address to use')),
2697 2709 ('n', 'name', '',
2698 2710 _('name to show in web pages (default: working dir)')),
2699 2711 ('', 'webdir-conf', '', _('name of the webdir config file'
2700 2712 ' (serve more than one repo)')),
2701 2713 ('', 'pid-file', '', _('name of file to write process ID to')),
2702 2714 ('', 'stdio', None, _('for remote clients')),
2703 2715 ('t', 'templates', '', _('web templates to use')),
2704 2716 ('', 'style', '', _('template style to use')),
2705 2717 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
2706 2718 _('hg serve [OPTION]...')),
2707 2719 "^status|st":
2708 2720 (status,
2709 2721 [('A', 'all', None, _('show status of all files')),
2710 2722 ('m', 'modified', None, _('show only modified files')),
2711 2723 ('a', 'added', None, _('show only added files')),
2712 2724 ('r', 'removed', None, _('show only removed files')),
2713 2725 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
2714 2726 ('c', 'clean', None, _('show only files without changes')),
2715 2727 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
2716 2728 ('i', 'ignored', None, _('show ignored files')),
2717 2729 ('n', 'no-status', None, _('hide status prefix')),
2718 2730 ('C', 'copies', None, _('show source of copied files')),
2719 2731 ('0', 'print0', None,
2720 2732 _('end filenames with NUL, for use with xargs')),
2721 2733 ('', 'rev', [], _('show difference from revision')),
2722 2734 ] + walkopts,
2723 2735 _('hg status [OPTION]... [FILE]...')),
2724 2736 "tag":
2725 2737 (tag,
2726 2738 [('l', 'local', None, _('make the tag local')),
2727 2739 ('m', 'message', '', _('message for tag commit log entry')),
2728 2740 ('d', 'date', '', _('record datecode as commit date')),
2729 2741 ('u', 'user', '', _('record user as commiter')),
2730 2742 ('r', 'rev', '', _('revision to tag'))],
2731 2743 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
2732 2744 "tags": (tags, [], _('hg tags')),
2733 2745 "tip":
2734 2746 (tip,
2735 2747 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2736 2748 ('', 'style', '', _('display using template map file')),
2737 2749 ('p', 'patch', None, _('show patch')),
2738 2750 ('', 'template', '', _('display with template'))],
2739 2751 _('hg tip [-p]')),
2740 2752 "unbundle":
2741 2753 (unbundle,
2742 2754 [('u', 'update', None,
2743 2755 _('update to new tip if changesets were unbundled'))],
2744 2756 _('hg unbundle [-u] FILE')),
2745 2757 "^update|up|checkout|co":
2746 2758 (update,
2747 2759 [('b', 'branch', '',
2748 2760 _('checkout the head of a specific branch (DEPRECATED)')),
2749 ('C', 'clean', None, _('overwrite locally modified files'))],
2761 ('C', 'clean', None, _('overwrite locally modified files')),
2762 ('d', 'date', '', _('tipmost revision matching date'))],
2750 2763 _('hg update [-C] [REV]')),
2751 2764 "verify": (verify, [], _('hg verify')),
2752 2765 "version": (version_, [], _('hg version')),
2753 2766 }
2754 2767
2755 2768 norepo = ("clone init version help debugancestor debugcomplete debugdata"
2756 2769 " debugindex debugindexdot debugdate")
2757 2770 optionalrepo = ("paths serve showconfig")
2758 2771
2759 2772 def findpossible(ui, cmd):
2760 2773 """
2761 2774 Return cmd -> (aliases, command table entry)
2762 2775 for each matching command.
2763 2776 Return debug commands (or their aliases) only if no normal command matches.
2764 2777 """
2765 2778 choice = {}
2766 2779 debugchoice = {}
2767 2780 for e in table.keys():
2768 2781 aliases = e.lstrip("^").split("|")
2769 2782 found = None
2770 2783 if cmd in aliases:
2771 2784 found = cmd
2772 2785 elif not ui.config("ui", "strict"):
2773 2786 for a in aliases:
2774 2787 if a.startswith(cmd):
2775 2788 found = a
2776 2789 break
2777 2790 if found is not None:
2778 2791 if aliases[0].startswith("debug") or found.startswith("debug"):
2779 2792 debugchoice[found] = (aliases, table[e])
2780 2793 else:
2781 2794 choice[found] = (aliases, table[e])
2782 2795
2783 2796 if not choice and debugchoice:
2784 2797 choice = debugchoice
2785 2798
2786 2799 return choice
2787 2800
2788 2801 def findcmd(ui, cmd):
2789 2802 """Return (aliases, command table entry) for command string."""
2790 2803 choice = findpossible(ui, cmd)
2791 2804
2792 2805 if choice.has_key(cmd):
2793 2806 return choice[cmd]
2794 2807
2795 2808 if len(choice) > 1:
2796 2809 clist = choice.keys()
2797 2810 clist.sort()
2798 2811 raise AmbiguousCommand(cmd, clist)
2799 2812
2800 2813 if choice:
2801 2814 return choice.values()[0]
2802 2815
2803 2816 raise UnknownCommand(cmd)
2804 2817
2805 2818 def catchterm(*args):
2806 2819 raise util.SignalInterrupt
2807 2820
2808 2821 def run():
2809 2822 sys.exit(dispatch(sys.argv[1:]))
2810 2823
2811 2824 class ParseError(Exception):
2812 2825 """Exception raised on errors in parsing the command line."""
2813 2826
2814 2827 def parse(ui, args):
2815 2828 options = {}
2816 2829 cmdoptions = {}
2817 2830
2818 2831 try:
2819 2832 args = fancyopts.fancyopts(args, globalopts, options)
2820 2833 except fancyopts.getopt.GetoptError, inst:
2821 2834 raise ParseError(None, inst)
2822 2835
2823 2836 if args:
2824 2837 cmd, args = args[0], args[1:]
2825 2838 aliases, i = findcmd(ui, cmd)
2826 2839 cmd = aliases[0]
2827 2840 defaults = ui.config("defaults", cmd)
2828 2841 if defaults:
2829 2842 args = shlex.split(defaults) + args
2830 2843 c = list(i[1])
2831 2844 else:
2832 2845 cmd = None
2833 2846 c = []
2834 2847
2835 2848 # combine global options into local
2836 2849 for o in globalopts:
2837 2850 c.append((o[0], o[1], options[o[1]], o[3]))
2838 2851
2839 2852 try:
2840 2853 args = fancyopts.fancyopts(args, c, cmdoptions)
2841 2854 except fancyopts.getopt.GetoptError, inst:
2842 2855 raise ParseError(cmd, inst)
2843 2856
2844 2857 # separate global options back out
2845 2858 for o in globalopts:
2846 2859 n = o[1]
2847 2860 options[n] = cmdoptions[n]
2848 2861 del cmdoptions[n]
2849 2862
2850 2863 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
2851 2864
2852 2865 external = {}
2853 2866
2854 2867 def findext(name):
2855 2868 '''return module with given extension name'''
2856 2869 try:
2857 2870 return sys.modules[external[name]]
2858 2871 except KeyError:
2859 2872 for k, v in external.iteritems():
2860 2873 if k.endswith('.' + name) or k.endswith('/' + name) or v == name:
2861 2874 return sys.modules[v]
2862 2875 raise KeyError(name)
2863 2876
2864 2877 def load_extensions(ui):
2865 2878 added = []
2866 2879 for ext_name, load_from_name in ui.extensions():
2867 2880 if ext_name in external:
2868 2881 continue
2869 2882 try:
2870 2883 if load_from_name:
2871 2884 # the module will be loaded in sys.modules
2872 2885 # choose an unique name so that it doesn't
2873 2886 # conflicts with other modules
2874 2887 module_name = "hgext_%s" % ext_name.replace('.', '_')
2875 2888 mod = imp.load_source(module_name, load_from_name)
2876 2889 else:
2877 2890 def importh(name):
2878 2891 mod = __import__(name)
2879 2892 components = name.split('.')
2880 2893 for comp in components[1:]:
2881 2894 mod = getattr(mod, comp)
2882 2895 return mod
2883 2896 try:
2884 2897 mod = importh("hgext.%s" % ext_name)
2885 2898 except ImportError:
2886 2899 mod = importh(ext_name)
2887 2900 external[ext_name] = mod.__name__
2888 2901 added.append((mod, ext_name))
2889 2902 except (util.SignalInterrupt, KeyboardInterrupt):
2890 2903 raise
2891 2904 except Exception, inst:
2892 2905 ui.warn(_("*** failed to import extension %s: %s\n") %
2893 2906 (ext_name, inst))
2894 2907 if ui.print_exc():
2895 2908 return 1
2896 2909
2897 2910 for mod, name in added:
2898 2911 uisetup = getattr(mod, 'uisetup', None)
2899 2912 if uisetup:
2900 2913 uisetup(ui)
2901 2914 cmdtable = getattr(mod, 'cmdtable', {})
2902 2915 for t in cmdtable:
2903 2916 if t in table:
2904 2917 ui.warn(_("module %s overrides %s\n") % (name, t))
2905 2918 table.update(cmdtable)
2906 2919
2907 2920 def parseconfig(config):
2908 2921 """parse the --config options from the command line"""
2909 2922 parsed = []
2910 2923 for cfg in config:
2911 2924 try:
2912 2925 name, value = cfg.split('=', 1)
2913 2926 section, name = name.split('.', 1)
2914 2927 if not section or not name:
2915 2928 raise IndexError
2916 2929 parsed.append((section, name, value))
2917 2930 except (IndexError, ValueError):
2918 2931 raise util.Abort(_('malformed --config option: %s') % cfg)
2919 2932 return parsed
2920 2933
2921 2934 def dispatch(args):
2922 2935 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
2923 2936 num = getattr(signal, name, None)
2924 2937 if num: signal.signal(num, catchterm)
2925 2938
2926 2939 try:
2927 2940 u = ui.ui(traceback='--traceback' in sys.argv[1:])
2928 2941 except util.Abort, inst:
2929 2942 sys.stderr.write(_("abort: %s\n") % inst)
2930 2943 return -1
2931 2944
2932 2945 load_extensions(u)
2933 2946 u.addreadhook(load_extensions)
2934 2947
2935 2948 try:
2936 2949 cmd, func, args, options, cmdoptions = parse(u, args)
2937 2950 if options["encoding"]:
2938 2951 util._encoding = options["encoding"]
2939 2952 if options["encodingmode"]:
2940 2953 util._encodingmode = options["encodingmode"]
2941 2954 if options["time"]:
2942 2955 def get_times():
2943 2956 t = os.times()
2944 2957 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
2945 2958 t = (t[0], t[1], t[2], t[3], time.clock())
2946 2959 return t
2947 2960 s = get_times()
2948 2961 def print_time():
2949 2962 t = get_times()
2950 2963 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
2951 2964 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
2952 2965 atexit.register(print_time)
2953 2966
2954 2967 # enter the debugger before command execution
2955 2968 if options['debugger']:
2956 2969 pdb.set_trace()
2957 2970
2958 2971 try:
2959 2972 if options['cwd']:
2960 2973 try:
2961 2974 os.chdir(options['cwd'])
2962 2975 except OSError, inst:
2963 2976 raise util.Abort('%s: %s' %
2964 2977 (options['cwd'], inst.strerror))
2965 2978
2966 2979 u.updateopts(options["verbose"], options["debug"], options["quiet"],
2967 2980 not options["noninteractive"], options["traceback"],
2968 2981 parseconfig(options["config"]))
2969 2982
2970 2983 path = u.expandpath(options["repository"]) or ""
2971 2984 repo = path and hg.repository(u, path=path) or None
2972 2985 if repo and not repo.local():
2973 2986 raise util.Abort(_("repository '%s' is not local") % path)
2974 2987
2975 2988 if options['help']:
2976 2989 return help_(u, cmd, options['version'])
2977 2990 elif options['version']:
2978 2991 return version_(u)
2979 2992 elif not cmd:
2980 2993 return help_(u, 'shortlist')
2981 2994
2982 2995 if cmd not in norepo.split():
2983 2996 try:
2984 2997 if not repo:
2985 2998 repo = hg.repository(u, path=path)
2986 2999 u = repo.ui
2987 3000 for name in external.itervalues():
2988 3001 mod = sys.modules[name]
2989 3002 if hasattr(mod, 'reposetup'):
2990 3003 mod.reposetup(u, repo)
2991 3004 hg.repo_setup_hooks.append(mod.reposetup)
2992 3005 except hg.RepoError:
2993 3006 if cmd not in optionalrepo.split():
2994 3007 raise
2995 3008 d = lambda: func(u, repo, *args, **cmdoptions)
2996 3009 else:
2997 3010 d = lambda: func(u, *args, **cmdoptions)
2998 3011
2999 3012 try:
3000 3013 if options['profile']:
3001 3014 import hotshot, hotshot.stats
3002 3015 prof = hotshot.Profile("hg.prof")
3003 3016 try:
3004 3017 try:
3005 3018 return prof.runcall(d)
3006 3019 except:
3007 3020 try:
3008 3021 u.warn(_('exception raised - generating '
3009 3022 'profile anyway\n'))
3010 3023 except:
3011 3024 pass
3012 3025 raise
3013 3026 finally:
3014 3027 prof.close()
3015 3028 stats = hotshot.stats.load("hg.prof")
3016 3029 stats.strip_dirs()
3017 3030 stats.sort_stats('time', 'calls')
3018 3031 stats.print_stats(40)
3019 3032 elif options['lsprof']:
3020 3033 try:
3021 3034 from mercurial import lsprof
3022 3035 except ImportError:
3023 3036 raise util.Abort(_(
3024 3037 'lsprof not available - install from '
3025 3038 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3026 3039 p = lsprof.Profiler()
3027 3040 p.enable(subcalls=True)
3028 3041 try:
3029 3042 return d()
3030 3043 finally:
3031 3044 p.disable()
3032 3045 stats = lsprof.Stats(p.getstats())
3033 3046 stats.sort()
3034 3047 stats.pprint(top=10, file=sys.stderr, climit=5)
3035 3048 else:
3036 3049 return d()
3037 3050 finally:
3038 3051 u.flush()
3039 3052 except:
3040 3053 # enter the debugger when we hit an exception
3041 3054 if options['debugger']:
3042 3055 pdb.post_mortem(sys.exc_info()[2])
3043 3056 u.print_exc()
3044 3057 raise
3045 3058 except ParseError, inst:
3046 3059 if inst.args[0]:
3047 3060 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3048 3061 help_(u, inst.args[0])
3049 3062 else:
3050 3063 u.warn(_("hg: %s\n") % inst.args[1])
3051 3064 help_(u, 'shortlist')
3052 3065 except AmbiguousCommand, inst:
3053 3066 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3054 3067 (inst.args[0], " ".join(inst.args[1])))
3055 3068 except UnknownCommand, inst:
3056 3069 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3057 3070 help_(u, 'shortlist')
3058 3071 except hg.RepoError, inst:
3059 3072 u.warn(_("abort: %s!\n") % inst)
3060 3073 except lock.LockHeld, inst:
3061 3074 if inst.errno == errno.ETIMEDOUT:
3062 3075 reason = _('timed out waiting for lock held by %s') % inst.locker
3063 3076 else:
3064 3077 reason = _('lock held by %s') % inst.locker
3065 3078 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3066 3079 except lock.LockUnavailable, inst:
3067 3080 u.warn(_("abort: could not lock %s: %s\n") %
3068 3081 (inst.desc or inst.filename, inst.strerror))
3069 3082 except revlog.RevlogError, inst:
3070 3083 u.warn(_("abort: %s!\n") % inst)
3071 3084 except util.SignalInterrupt:
3072 3085 u.warn(_("killed!\n"))
3073 3086 except KeyboardInterrupt:
3074 3087 try:
3075 3088 u.warn(_("interrupted!\n"))
3076 3089 except IOError, inst:
3077 3090 if inst.errno == errno.EPIPE:
3078 3091 if u.debugflag:
3079 3092 u.warn(_("\nbroken pipe\n"))
3080 3093 else:
3081 3094 raise
3082 3095 except IOError, inst:
3083 3096 if hasattr(inst, "code"):
3084 3097 u.warn(_("abort: %s\n") % inst)
3085 3098 elif hasattr(inst, "reason"):
3086 3099 u.warn(_("abort: error: %s\n") % inst.reason[1])
3087 3100 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3088 3101 if u.debugflag:
3089 3102 u.warn(_("broken pipe\n"))
3090 3103 elif getattr(inst, "strerror", None):
3091 3104 if getattr(inst, "filename", None):
3092 3105 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3093 3106 else:
3094 3107 u.warn(_("abort: %s\n") % inst.strerror)
3095 3108 else:
3096 3109 raise
3097 3110 except OSError, inst:
3098 3111 if getattr(inst, "filename", None):
3099 3112 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3100 3113 else:
3101 3114 u.warn(_("abort: %s\n") % inst.strerror)
3102 3115 except util.UnexpectedOutput, inst:
3103 3116 u.warn(_("abort: %s") % inst[0])
3104 3117 if not isinstance(inst[1], basestring):
3105 3118 u.warn(" %r\n" % (inst[1],))
3106 3119 elif not inst[1]:
3107 3120 u.warn(_(" empty string\n"))
3108 3121 else:
3109 3122 u.warn("\n%r\n" % util.ellipsis(inst[1]))
3110 3123 except util.Abort, inst:
3111 3124 u.warn(_("abort: %s\n") % inst)
3112 3125 except TypeError, inst:
3113 3126 # was this an argument error?
3114 3127 tb = traceback.extract_tb(sys.exc_info()[2])
3115 3128 if len(tb) > 2: # no
3116 3129 raise
3117 3130 u.debug(inst, "\n")
3118 3131 u.warn(_("%s: invalid arguments\n") % cmd)
3119 3132 help_(u, cmd)
3120 3133 except SystemExit, inst:
3121 3134 # Commands shouldn't sys.exit directly, but give a return code.
3122 3135 # Just in case catch this and and pass exit code to caller.
3123 3136 return inst.code
3124 3137 except:
3125 3138 u.warn(_("** unknown exception encountered, details follow\n"))
3126 3139 u.warn(_("** report bug details to "
3127 3140 "http://www.selenic.com/mercurial/bts\n"))
3128 3141 u.warn(_("** or mercurial@selenic.com\n"))
3129 3142 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3130 3143 % version.get_version())
3131 3144 raise
3132 3145
3133 3146 return -1
General Comments 0
You need to be logged in to leave comments. Login now