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