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