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