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