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