##// END OF EJS Templates
files: split reusable implementation into cmdutil for subrepo support
Matt Harbison -
r24275:e1cb460a default
parent child Browse files
Show More
@@ -1,3141 +1,3159 b''
1 1 # cmdutil.py - help for command processing in mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from node import hex, nullid, nullrev, short
9 9 from i18n import _
10 10 import os, sys, errno, re, tempfile, cStringIO, shutil
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 encoding
17 17 import lock as lockmod
18 18
19 19 def parsealiases(cmd):
20 20 return cmd.lstrip("^").split("|")
21 21
22 22 def dorecord(ui, repo, commitfunc, cmdsuggest, backupall, *pats, **opts):
23 23 import merge as mergemod
24 24 if not ui.interactive():
25 25 raise util.Abort(_('running non-interactively, use %s instead') %
26 26 cmdsuggest)
27 27
28 28 # make sure username is set before going interactive
29 29 if not opts.get('user'):
30 30 ui.username() # raise exception, username not provided
31 31
32 32 def recordfunc(ui, repo, message, match, opts):
33 33 """This is generic record driver.
34 34
35 35 Its job is to interactively filter local changes, and
36 36 accordingly prepare working directory into a state in which the
37 37 job can be delegated to a non-interactive commit command such as
38 38 'commit' or 'qrefresh'.
39 39
40 40 After the actual job is done by non-interactive command, the
41 41 working directory is restored to its original state.
42 42
43 43 In the end we'll record interesting changes, and everything else
44 44 will be left in place, so the user can continue working.
45 45 """
46 46
47 47 checkunfinished(repo, commit=True)
48 48 merge = len(repo[None].parents()) > 1
49 49 if merge:
50 50 raise util.Abort(_('cannot partially commit a merge '
51 51 '(use "hg commit" instead)'))
52 52
53 53 status = repo.status(match=match)
54 54 diffopts = patch.difffeatureopts(ui, opts=opts, whitespace=True)
55 55 diffopts.nodates = True
56 56 diffopts.git = True
57 57 originalchunks = patch.diff(repo, changes=status, opts=diffopts)
58 58 fp = cStringIO.StringIO()
59 59 fp.write(''.join(originalchunks))
60 60 fp.seek(0)
61 61
62 62 # 1. filter patch, so we have intending-to apply subset of it
63 63 try:
64 64 chunks = patch.filterpatch(ui, patch.parsepatch(fp))
65 65 except patch.PatchError, err:
66 66 raise util.Abort(_('error parsing patch: %s') % err)
67 67
68 68 del fp
69 69
70 70 contenders = set()
71 71 for h in chunks:
72 72 try:
73 73 contenders.update(set(h.files()))
74 74 except AttributeError:
75 75 pass
76 76
77 77 changed = status.modified + status.added + status.removed
78 78 newfiles = [f for f in changed if f in contenders]
79 79 if not newfiles:
80 80 ui.status(_('no changes to record\n'))
81 81 return 0
82 82
83 83 newandmodifiedfiles = set()
84 84 for h in chunks:
85 85 ishunk = isinstance(h, patch.recordhunk)
86 86 isnew = h.filename() in status.added
87 87 if ishunk and isnew and not h in originalchunks:
88 88 newandmodifiedfiles.add(h.filename())
89 89
90 90 modified = set(status.modified)
91 91
92 92 # 2. backup changed files, so we can restore them in the end
93 93
94 94 if backupall:
95 95 tobackup = changed
96 96 else:
97 97 tobackup = [f for f in newfiles
98 98 if f in modified or f in newandmodifiedfiles]
99 99
100 100 backups = {}
101 101 if tobackup:
102 102 backupdir = repo.join('record-backups')
103 103 try:
104 104 os.mkdir(backupdir)
105 105 except OSError, err:
106 106 if err.errno != errno.EEXIST:
107 107 raise
108 108 try:
109 109 # backup continues
110 110 for f in tobackup:
111 111 fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
112 112 dir=backupdir)
113 113 os.close(fd)
114 114 ui.debug('backup %r as %r\n' % (f, tmpname))
115 115 util.copyfile(repo.wjoin(f), tmpname)
116 116 shutil.copystat(repo.wjoin(f), tmpname)
117 117 backups[f] = tmpname
118 118
119 119 fp = cStringIO.StringIO()
120 120 for c in chunks:
121 121 fname = c.filename()
122 122 if fname in backups or fname in newandmodifiedfiles:
123 123 c.write(fp)
124 124 dopatch = fp.tell()
125 125 fp.seek(0)
126 126
127 127 [os.unlink(c) for c in newandmodifiedfiles]
128 128
129 129 # 3a. apply filtered patch to clean repo (clean)
130 130 if backups:
131 131 # Equivalent to hg.revert
132 132 choices = lambda key: key in backups
133 133 mergemod.update(repo, repo.dirstate.p1(),
134 134 False, True, choices)
135 135
136 136
137 137 # 3b. (apply)
138 138 if dopatch:
139 139 try:
140 140 ui.debug('applying patch\n')
141 141 ui.debug(fp.getvalue())
142 142 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
143 143 except patch.PatchError, err:
144 144 raise util.Abort(str(err))
145 145 del fp
146 146
147 147 # 4. We prepared working directory according to filtered
148 148 # patch. Now is the time to delegate the job to
149 149 # commit/qrefresh or the like!
150 150
151 151 # Make all of the pathnames absolute.
152 152 newfiles = [repo.wjoin(nf) for nf in newfiles]
153 153 commitfunc(ui, repo, *newfiles, **opts)
154 154
155 155 return 0
156 156 finally:
157 157 # 5. finally restore backed-up files
158 158 try:
159 159 for realname, tmpname in backups.iteritems():
160 160 ui.debug('restoring %r to %r\n' % (tmpname, realname))
161 161 util.copyfile(tmpname, repo.wjoin(realname))
162 162 # Our calls to copystat() here and above are a
163 163 # hack to trick any editors that have f open that
164 164 # we haven't modified them.
165 165 #
166 166 # Also note that this racy as an editor could
167 167 # notice the file's mtime before we've finished
168 168 # writing it.
169 169 shutil.copystat(tmpname, repo.wjoin(realname))
170 170 os.unlink(tmpname)
171 171 if tobackup:
172 172 os.rmdir(backupdir)
173 173 except OSError:
174 174 pass
175 175
176 176 # wrap ui.write so diff output can be labeled/colorized
177 177 def wrapwrite(orig, *args, **kw):
178 178 label = kw.pop('label', '')
179 179 for chunk, l in patch.difflabel(lambda: args):
180 180 orig(chunk, label=label + l)
181 181
182 182 oldwrite = ui.write
183 183 def wrap(*args, **kwargs):
184 184 return wrapwrite(oldwrite, *args, **kwargs)
185 185 setattr(ui, 'write', wrap)
186 186
187 187 try:
188 188 return commit(ui, repo, recordfunc, pats, opts)
189 189 finally:
190 190 ui.write = oldwrite
191 191
192 192
193 193 def findpossible(cmd, table, strict=False):
194 194 """
195 195 Return cmd -> (aliases, command table entry)
196 196 for each matching command.
197 197 Return debug commands (or their aliases) only if no normal command matches.
198 198 """
199 199 choice = {}
200 200 debugchoice = {}
201 201
202 202 if cmd in table:
203 203 # short-circuit exact matches, "log" alias beats "^log|history"
204 204 keys = [cmd]
205 205 else:
206 206 keys = table.keys()
207 207
208 208 allcmds = []
209 209 for e in keys:
210 210 aliases = parsealiases(e)
211 211 allcmds.extend(aliases)
212 212 found = None
213 213 if cmd in aliases:
214 214 found = cmd
215 215 elif not strict:
216 216 for a in aliases:
217 217 if a.startswith(cmd):
218 218 found = a
219 219 break
220 220 if found is not None:
221 221 if aliases[0].startswith("debug") or found.startswith("debug"):
222 222 debugchoice[found] = (aliases, table[e])
223 223 else:
224 224 choice[found] = (aliases, table[e])
225 225
226 226 if not choice and debugchoice:
227 227 choice = debugchoice
228 228
229 229 return choice, allcmds
230 230
231 231 def findcmd(cmd, table, strict=True):
232 232 """Return (aliases, command table entry) for command string."""
233 233 choice, allcmds = findpossible(cmd, table, strict)
234 234
235 235 if cmd in choice:
236 236 return choice[cmd]
237 237
238 238 if len(choice) > 1:
239 239 clist = choice.keys()
240 240 clist.sort()
241 241 raise error.AmbiguousCommand(cmd, clist)
242 242
243 243 if choice:
244 244 return choice.values()[0]
245 245
246 246 raise error.UnknownCommand(cmd, allcmds)
247 247
248 248 def findrepo(p):
249 249 while not os.path.isdir(os.path.join(p, ".hg")):
250 250 oldp, p = p, os.path.dirname(p)
251 251 if p == oldp:
252 252 return None
253 253
254 254 return p
255 255
256 256 def bailifchanged(repo):
257 257 if repo.dirstate.p2() != nullid:
258 258 raise util.Abort(_('outstanding uncommitted merge'))
259 259 modified, added, removed, deleted = repo.status()[:4]
260 260 if modified or added or removed or deleted:
261 261 raise util.Abort(_('uncommitted changes'))
262 262 ctx = repo[None]
263 263 for s in sorted(ctx.substate):
264 264 if ctx.sub(s).dirty():
265 265 raise util.Abort(_("uncommitted changes in subrepo %s") % s)
266 266
267 267 def logmessage(ui, opts):
268 268 """ get the log message according to -m and -l option """
269 269 message = opts.get('message')
270 270 logfile = opts.get('logfile')
271 271
272 272 if message and logfile:
273 273 raise util.Abort(_('options --message and --logfile are mutually '
274 274 'exclusive'))
275 275 if not message and logfile:
276 276 try:
277 277 if logfile == '-':
278 278 message = ui.fin.read()
279 279 else:
280 280 message = '\n'.join(util.readfile(logfile).splitlines())
281 281 except IOError, inst:
282 282 raise util.Abort(_("can't read commit message '%s': %s") %
283 283 (logfile, inst.strerror))
284 284 return message
285 285
286 286 def mergeeditform(ctxorbool, baseformname):
287 287 """return appropriate editform name (referencing a committemplate)
288 288
289 289 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
290 290 merging is committed.
291 291
292 292 This returns baseformname with '.merge' appended if it is a merge,
293 293 otherwise '.normal' is appended.
294 294 """
295 295 if isinstance(ctxorbool, bool):
296 296 if ctxorbool:
297 297 return baseformname + ".merge"
298 298 elif 1 < len(ctxorbool.parents()):
299 299 return baseformname + ".merge"
300 300
301 301 return baseformname + ".normal"
302 302
303 303 def getcommiteditor(edit=False, finishdesc=None, extramsg=None,
304 304 editform='', **opts):
305 305 """get appropriate commit message editor according to '--edit' option
306 306
307 307 'finishdesc' is a function to be called with edited commit message
308 308 (= 'description' of the new changeset) just after editing, but
309 309 before checking empty-ness. It should return actual text to be
310 310 stored into history. This allows to change description before
311 311 storing.
312 312
313 313 'extramsg' is a extra message to be shown in the editor instead of
314 314 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
315 315 is automatically added.
316 316
317 317 'editform' is a dot-separated list of names, to distinguish
318 318 the purpose of commit text editing.
319 319
320 320 'getcommiteditor' returns 'commitforceeditor' regardless of
321 321 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
322 322 they are specific for usage in MQ.
323 323 """
324 324 if edit or finishdesc or extramsg:
325 325 return lambda r, c, s: commitforceeditor(r, c, s,
326 326 finishdesc=finishdesc,
327 327 extramsg=extramsg,
328 328 editform=editform)
329 329 elif editform:
330 330 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
331 331 else:
332 332 return commiteditor
333 333
334 334 def loglimit(opts):
335 335 """get the log limit according to option -l/--limit"""
336 336 limit = opts.get('limit')
337 337 if limit:
338 338 try:
339 339 limit = int(limit)
340 340 except ValueError:
341 341 raise util.Abort(_('limit must be a positive integer'))
342 342 if limit <= 0:
343 343 raise util.Abort(_('limit must be positive'))
344 344 else:
345 345 limit = None
346 346 return limit
347 347
348 348 def makefilename(repo, pat, node, desc=None,
349 349 total=None, seqno=None, revwidth=None, pathname=None):
350 350 node_expander = {
351 351 'H': lambda: hex(node),
352 352 'R': lambda: str(repo.changelog.rev(node)),
353 353 'h': lambda: short(node),
354 354 'm': lambda: re.sub('[^\w]', '_', str(desc))
355 355 }
356 356 expander = {
357 357 '%': lambda: '%',
358 358 'b': lambda: os.path.basename(repo.root),
359 359 }
360 360
361 361 try:
362 362 if node:
363 363 expander.update(node_expander)
364 364 if node:
365 365 expander['r'] = (lambda:
366 366 str(repo.changelog.rev(node)).zfill(revwidth or 0))
367 367 if total is not None:
368 368 expander['N'] = lambda: str(total)
369 369 if seqno is not None:
370 370 expander['n'] = lambda: str(seqno)
371 371 if total is not None and seqno is not None:
372 372 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
373 373 if pathname is not None:
374 374 expander['s'] = lambda: os.path.basename(pathname)
375 375 expander['d'] = lambda: os.path.dirname(pathname) or '.'
376 376 expander['p'] = lambda: pathname
377 377
378 378 newname = []
379 379 patlen = len(pat)
380 380 i = 0
381 381 while i < patlen:
382 382 c = pat[i]
383 383 if c == '%':
384 384 i += 1
385 385 c = pat[i]
386 386 c = expander[c]()
387 387 newname.append(c)
388 388 i += 1
389 389 return ''.join(newname)
390 390 except KeyError, inst:
391 391 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
392 392 inst.args[0])
393 393
394 394 def makefileobj(repo, pat, node=None, desc=None, total=None,
395 395 seqno=None, revwidth=None, mode='wb', modemap=None,
396 396 pathname=None):
397 397
398 398 writable = mode not in ('r', 'rb')
399 399
400 400 if not pat or pat == '-':
401 401 fp = writable and repo.ui.fout or repo.ui.fin
402 402 if util.safehasattr(fp, 'fileno'):
403 403 return os.fdopen(os.dup(fp.fileno()), mode)
404 404 else:
405 405 # if this fp can't be duped properly, return
406 406 # a dummy object that can be closed
407 407 class wrappedfileobj(object):
408 408 noop = lambda x: None
409 409 def __init__(self, f):
410 410 self.f = f
411 411 def __getattr__(self, attr):
412 412 if attr == 'close':
413 413 return self.noop
414 414 else:
415 415 return getattr(self.f, attr)
416 416
417 417 return wrappedfileobj(fp)
418 418 if util.safehasattr(pat, 'write') and writable:
419 419 return pat
420 420 if util.safehasattr(pat, 'read') and 'r' in mode:
421 421 return pat
422 422 fn = makefilename(repo, pat, node, desc, total, seqno, revwidth, pathname)
423 423 if modemap is not None:
424 424 mode = modemap.get(fn, mode)
425 425 if mode == 'wb':
426 426 modemap[fn] = 'ab'
427 427 return open(fn, mode)
428 428
429 429 def openrevlog(repo, cmd, file_, opts):
430 430 """opens the changelog, manifest, a filelog or a given revlog"""
431 431 cl = opts['changelog']
432 432 mf = opts['manifest']
433 433 msg = None
434 434 if cl and mf:
435 435 msg = _('cannot specify --changelog and --manifest at the same time')
436 436 elif cl or mf:
437 437 if file_:
438 438 msg = _('cannot specify filename with --changelog or --manifest')
439 439 elif not repo:
440 440 msg = _('cannot specify --changelog or --manifest '
441 441 'without a repository')
442 442 if msg:
443 443 raise util.Abort(msg)
444 444
445 445 r = None
446 446 if repo:
447 447 if cl:
448 448 r = repo.unfiltered().changelog
449 449 elif mf:
450 450 r = repo.manifest
451 451 elif file_:
452 452 filelog = repo.file(file_)
453 453 if len(filelog):
454 454 r = filelog
455 455 if not r:
456 456 if not file_:
457 457 raise error.CommandError(cmd, _('invalid arguments'))
458 458 if not os.path.isfile(file_):
459 459 raise util.Abort(_("revlog '%s' not found") % file_)
460 460 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
461 461 file_[:-2] + ".i")
462 462 return r
463 463
464 464 def copy(ui, repo, pats, opts, rename=False):
465 465 # called with the repo lock held
466 466 #
467 467 # hgsep => pathname that uses "/" to separate directories
468 468 # ossep => pathname that uses os.sep to separate directories
469 469 cwd = repo.getcwd()
470 470 targets = {}
471 471 after = opts.get("after")
472 472 dryrun = opts.get("dry_run")
473 473 wctx = repo[None]
474 474
475 475 def walkpat(pat):
476 476 srcs = []
477 477 badstates = after and '?' or '?r'
478 478 m = scmutil.match(repo[None], [pat], opts, globbed=True)
479 479 for abs in repo.walk(m):
480 480 state = repo.dirstate[abs]
481 481 rel = m.rel(abs)
482 482 exact = m.exact(abs)
483 483 if state in badstates:
484 484 if exact and state == '?':
485 485 ui.warn(_('%s: not copying - file is not managed\n') % rel)
486 486 if exact and state == 'r':
487 487 ui.warn(_('%s: not copying - file has been marked for'
488 488 ' remove\n') % rel)
489 489 continue
490 490 # abs: hgsep
491 491 # rel: ossep
492 492 srcs.append((abs, rel, exact))
493 493 return srcs
494 494
495 495 # abssrc: hgsep
496 496 # relsrc: ossep
497 497 # otarget: ossep
498 498 def copyfile(abssrc, relsrc, otarget, exact):
499 499 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
500 500 if '/' in abstarget:
501 501 # We cannot normalize abstarget itself, this would prevent
502 502 # case only renames, like a => A.
503 503 abspath, absname = abstarget.rsplit('/', 1)
504 504 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
505 505 reltarget = repo.pathto(abstarget, cwd)
506 506 target = repo.wjoin(abstarget)
507 507 src = repo.wjoin(abssrc)
508 508 state = repo.dirstate[abstarget]
509 509
510 510 scmutil.checkportable(ui, abstarget)
511 511
512 512 # check for collisions
513 513 prevsrc = targets.get(abstarget)
514 514 if prevsrc is not None:
515 515 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
516 516 (reltarget, repo.pathto(abssrc, cwd),
517 517 repo.pathto(prevsrc, cwd)))
518 518 return
519 519
520 520 # check for overwrites
521 521 exists = os.path.lexists(target)
522 522 samefile = False
523 523 if exists and abssrc != abstarget:
524 524 if (repo.dirstate.normalize(abssrc) ==
525 525 repo.dirstate.normalize(abstarget)):
526 526 if not rename:
527 527 ui.warn(_("%s: can't copy - same file\n") % reltarget)
528 528 return
529 529 exists = False
530 530 samefile = True
531 531
532 532 if not after and exists or after and state in 'mn':
533 533 if not opts['force']:
534 534 ui.warn(_('%s: not overwriting - file exists\n') %
535 535 reltarget)
536 536 return
537 537
538 538 if after:
539 539 if not exists:
540 540 if rename:
541 541 ui.warn(_('%s: not recording move - %s does not exist\n') %
542 542 (relsrc, reltarget))
543 543 else:
544 544 ui.warn(_('%s: not recording copy - %s does not exist\n') %
545 545 (relsrc, reltarget))
546 546 return
547 547 elif not dryrun:
548 548 try:
549 549 if exists:
550 550 os.unlink(target)
551 551 targetdir = os.path.dirname(target) or '.'
552 552 if not os.path.isdir(targetdir):
553 553 os.makedirs(targetdir)
554 554 if samefile:
555 555 tmp = target + "~hgrename"
556 556 os.rename(src, tmp)
557 557 os.rename(tmp, target)
558 558 else:
559 559 util.copyfile(src, target)
560 560 srcexists = True
561 561 except IOError, inst:
562 562 if inst.errno == errno.ENOENT:
563 563 ui.warn(_('%s: deleted in working copy\n') % relsrc)
564 564 srcexists = False
565 565 else:
566 566 ui.warn(_('%s: cannot copy - %s\n') %
567 567 (relsrc, inst.strerror))
568 568 return True # report a failure
569 569
570 570 if ui.verbose or not exact:
571 571 if rename:
572 572 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
573 573 else:
574 574 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
575 575
576 576 targets[abstarget] = abssrc
577 577
578 578 # fix up dirstate
579 579 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
580 580 dryrun=dryrun, cwd=cwd)
581 581 if rename and not dryrun:
582 582 if not after and srcexists and not samefile:
583 583 util.unlinkpath(repo.wjoin(abssrc))
584 584 wctx.forget([abssrc])
585 585
586 586 # pat: ossep
587 587 # dest ossep
588 588 # srcs: list of (hgsep, hgsep, ossep, bool)
589 589 # return: function that takes hgsep and returns ossep
590 590 def targetpathfn(pat, dest, srcs):
591 591 if os.path.isdir(pat):
592 592 abspfx = pathutil.canonpath(repo.root, cwd, pat)
593 593 abspfx = util.localpath(abspfx)
594 594 if destdirexists:
595 595 striplen = len(os.path.split(abspfx)[0])
596 596 else:
597 597 striplen = len(abspfx)
598 598 if striplen:
599 599 striplen += len(os.sep)
600 600 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
601 601 elif destdirexists:
602 602 res = lambda p: os.path.join(dest,
603 603 os.path.basename(util.localpath(p)))
604 604 else:
605 605 res = lambda p: dest
606 606 return res
607 607
608 608 # pat: ossep
609 609 # dest ossep
610 610 # srcs: list of (hgsep, hgsep, ossep, bool)
611 611 # return: function that takes hgsep and returns ossep
612 612 def targetpathafterfn(pat, dest, srcs):
613 613 if matchmod.patkind(pat):
614 614 # a mercurial pattern
615 615 res = lambda p: os.path.join(dest,
616 616 os.path.basename(util.localpath(p)))
617 617 else:
618 618 abspfx = pathutil.canonpath(repo.root, cwd, pat)
619 619 if len(abspfx) < len(srcs[0][0]):
620 620 # A directory. Either the target path contains the last
621 621 # component of the source path or it does not.
622 622 def evalpath(striplen):
623 623 score = 0
624 624 for s in srcs:
625 625 t = os.path.join(dest, util.localpath(s[0])[striplen:])
626 626 if os.path.lexists(t):
627 627 score += 1
628 628 return score
629 629
630 630 abspfx = util.localpath(abspfx)
631 631 striplen = len(abspfx)
632 632 if striplen:
633 633 striplen += len(os.sep)
634 634 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
635 635 score = evalpath(striplen)
636 636 striplen1 = len(os.path.split(abspfx)[0])
637 637 if striplen1:
638 638 striplen1 += len(os.sep)
639 639 if evalpath(striplen1) > score:
640 640 striplen = striplen1
641 641 res = lambda p: os.path.join(dest,
642 642 util.localpath(p)[striplen:])
643 643 else:
644 644 # a file
645 645 if destdirexists:
646 646 res = lambda p: os.path.join(dest,
647 647 os.path.basename(util.localpath(p)))
648 648 else:
649 649 res = lambda p: dest
650 650 return res
651 651
652 652
653 653 pats = scmutil.expandpats(pats)
654 654 if not pats:
655 655 raise util.Abort(_('no source or destination specified'))
656 656 if len(pats) == 1:
657 657 raise util.Abort(_('no destination specified'))
658 658 dest = pats.pop()
659 659 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
660 660 if not destdirexists:
661 661 if len(pats) > 1 or matchmod.patkind(pats[0]):
662 662 raise util.Abort(_('with multiple sources, destination must be an '
663 663 'existing directory'))
664 664 if util.endswithsep(dest):
665 665 raise util.Abort(_('destination %s is not a directory') % dest)
666 666
667 667 tfn = targetpathfn
668 668 if after:
669 669 tfn = targetpathafterfn
670 670 copylist = []
671 671 for pat in pats:
672 672 srcs = walkpat(pat)
673 673 if not srcs:
674 674 continue
675 675 copylist.append((tfn(pat, dest, srcs), srcs))
676 676 if not copylist:
677 677 raise util.Abort(_('no files to copy'))
678 678
679 679 errors = 0
680 680 for targetpath, srcs in copylist:
681 681 for abssrc, relsrc, exact in srcs:
682 682 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
683 683 errors += 1
684 684
685 685 if errors:
686 686 ui.warn(_('(consider using --after)\n'))
687 687
688 688 return errors != 0
689 689
690 690 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
691 691 runargs=None, appendpid=False):
692 692 '''Run a command as a service.'''
693 693
694 694 def writepid(pid):
695 695 if opts['pid_file']:
696 696 mode = appendpid and 'a' or 'w'
697 697 fp = open(opts['pid_file'], mode)
698 698 fp.write(str(pid) + '\n')
699 699 fp.close()
700 700
701 701 if opts['daemon'] and not opts['daemon_pipefds']:
702 702 # Signal child process startup with file removal
703 703 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
704 704 os.close(lockfd)
705 705 try:
706 706 if not runargs:
707 707 runargs = util.hgcmd() + sys.argv[1:]
708 708 runargs.append('--daemon-pipefds=%s' % lockpath)
709 709 # Don't pass --cwd to the child process, because we've already
710 710 # changed directory.
711 711 for i in xrange(1, len(runargs)):
712 712 if runargs[i].startswith('--cwd='):
713 713 del runargs[i]
714 714 break
715 715 elif runargs[i].startswith('--cwd'):
716 716 del runargs[i:i + 2]
717 717 break
718 718 def condfn():
719 719 return not os.path.exists(lockpath)
720 720 pid = util.rundetached(runargs, condfn)
721 721 if pid < 0:
722 722 raise util.Abort(_('child process failed to start'))
723 723 writepid(pid)
724 724 finally:
725 725 try:
726 726 os.unlink(lockpath)
727 727 except OSError, e:
728 728 if e.errno != errno.ENOENT:
729 729 raise
730 730 if parentfn:
731 731 return parentfn(pid)
732 732 else:
733 733 return
734 734
735 735 if initfn:
736 736 initfn()
737 737
738 738 if not opts['daemon']:
739 739 writepid(os.getpid())
740 740
741 741 if opts['daemon_pipefds']:
742 742 lockpath = opts['daemon_pipefds']
743 743 try:
744 744 os.setsid()
745 745 except AttributeError:
746 746 pass
747 747 os.unlink(lockpath)
748 748 util.hidewindow()
749 749 sys.stdout.flush()
750 750 sys.stderr.flush()
751 751
752 752 nullfd = os.open(os.devnull, os.O_RDWR)
753 753 logfilefd = nullfd
754 754 if logfile:
755 755 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
756 756 os.dup2(nullfd, 0)
757 757 os.dup2(logfilefd, 1)
758 758 os.dup2(logfilefd, 2)
759 759 if nullfd not in (0, 1, 2):
760 760 os.close(nullfd)
761 761 if logfile and logfilefd not in (0, 1, 2):
762 762 os.close(logfilefd)
763 763
764 764 if runfn:
765 765 return runfn()
766 766
767 767 def tryimportone(ui, repo, hunk, parents, opts, msgs, updatefunc):
768 768 """Utility function used by commands.import to import a single patch
769 769
770 770 This function is explicitly defined here to help the evolve extension to
771 771 wrap this part of the import logic.
772 772
773 773 The API is currently a bit ugly because it a simple code translation from
774 774 the import command. Feel free to make it better.
775 775
776 776 :hunk: a patch (as a binary string)
777 777 :parents: nodes that will be parent of the created commit
778 778 :opts: the full dict of option passed to the import command
779 779 :msgs: list to save commit message to.
780 780 (used in case we need to save it when failing)
781 781 :updatefunc: a function that update a repo to a given node
782 782 updatefunc(<repo>, <node>)
783 783 """
784 784 tmpname, message, user, date, branch, nodeid, p1, p2 = \
785 785 patch.extract(ui, hunk)
786 786
787 787 update = not opts.get('bypass')
788 788 strip = opts["strip"]
789 789 prefix = opts["prefix"]
790 790 sim = float(opts.get('similarity') or 0)
791 791 if not tmpname:
792 792 return (None, None, False)
793 793 msg = _('applied to working directory')
794 794
795 795 rejects = False
796 796
797 797 try:
798 798 cmdline_message = logmessage(ui, opts)
799 799 if cmdline_message:
800 800 # pickup the cmdline msg
801 801 message = cmdline_message
802 802 elif message:
803 803 # pickup the patch msg
804 804 message = message.strip()
805 805 else:
806 806 # launch the editor
807 807 message = None
808 808 ui.debug('message:\n%s\n' % message)
809 809
810 810 if len(parents) == 1:
811 811 parents.append(repo[nullid])
812 812 if opts.get('exact'):
813 813 if not nodeid or not p1:
814 814 raise util.Abort(_('not a Mercurial patch'))
815 815 p1 = repo[p1]
816 816 p2 = repo[p2 or nullid]
817 817 elif p2:
818 818 try:
819 819 p1 = repo[p1]
820 820 p2 = repo[p2]
821 821 # Without any options, consider p2 only if the
822 822 # patch is being applied on top of the recorded
823 823 # first parent.
824 824 if p1 != parents[0]:
825 825 p1 = parents[0]
826 826 p2 = repo[nullid]
827 827 except error.RepoError:
828 828 p1, p2 = parents
829 829 if p2.node() == nullid:
830 830 ui.warn(_("warning: import the patch as a normal revision\n"
831 831 "(use --exact to import the patch as a merge)\n"))
832 832 else:
833 833 p1, p2 = parents
834 834
835 835 n = None
836 836 if update:
837 837 repo.dirstate.beginparentchange()
838 838 if p1 != parents[0]:
839 839 updatefunc(repo, p1.node())
840 840 if p2 != parents[1]:
841 841 repo.setparents(p1.node(), p2.node())
842 842
843 843 if opts.get('exact') or opts.get('import_branch'):
844 844 repo.dirstate.setbranch(branch or 'default')
845 845
846 846 partial = opts.get('partial', False)
847 847 files = set()
848 848 try:
849 849 patch.patch(ui, repo, tmpname, strip=strip, prefix=prefix,
850 850 files=files, eolmode=None, similarity=sim / 100.0)
851 851 except patch.PatchError, e:
852 852 if not partial:
853 853 raise util.Abort(str(e))
854 854 if partial:
855 855 rejects = True
856 856
857 857 files = list(files)
858 858 if opts.get('no_commit'):
859 859 if message:
860 860 msgs.append(message)
861 861 else:
862 862 if opts.get('exact') or p2:
863 863 # If you got here, you either use --force and know what
864 864 # you are doing or used --exact or a merge patch while
865 865 # being updated to its first parent.
866 866 m = None
867 867 else:
868 868 m = scmutil.matchfiles(repo, files or [])
869 869 editform = mergeeditform(repo[None], 'import.normal')
870 870 if opts.get('exact'):
871 871 editor = None
872 872 else:
873 873 editor = getcommiteditor(editform=editform, **opts)
874 874 n = repo.commit(message, opts.get('user') or user,
875 875 opts.get('date') or date, match=m,
876 876 editor=editor, force=partial)
877 877 repo.dirstate.endparentchange()
878 878 else:
879 879 if opts.get('exact') or opts.get('import_branch'):
880 880 branch = branch or 'default'
881 881 else:
882 882 branch = p1.branch()
883 883 store = patch.filestore()
884 884 try:
885 885 files = set()
886 886 try:
887 887 patch.patchrepo(ui, repo, p1, store, tmpname, strip, prefix,
888 888 files, eolmode=None)
889 889 except patch.PatchError, e:
890 890 raise util.Abort(str(e))
891 891 if opts.get('exact'):
892 892 editor = None
893 893 else:
894 894 editor = getcommiteditor(editform='import.bypass')
895 895 memctx = context.makememctx(repo, (p1.node(), p2.node()),
896 896 message,
897 897 opts.get('user') or user,
898 898 opts.get('date') or date,
899 899 branch, files, store,
900 900 editor=editor)
901 901 n = memctx.commit()
902 902 finally:
903 903 store.close()
904 904 if opts.get('exact') and opts.get('no_commit'):
905 905 # --exact with --no-commit is still useful in that it does merge
906 906 # and branch bits
907 907 ui.warn(_("warning: can't check exact import with --no-commit\n"))
908 908 elif opts.get('exact') and hex(n) != nodeid:
909 909 raise util.Abort(_('patch is damaged or loses information'))
910 910 if n:
911 911 # i18n: refers to a short changeset id
912 912 msg = _('created %s') % short(n)
913 913 return (msg, n, rejects)
914 914 finally:
915 915 os.unlink(tmpname)
916 916
917 917 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
918 918 opts=None):
919 919 '''export changesets as hg patches.'''
920 920
921 921 total = len(revs)
922 922 revwidth = max([len(str(rev)) for rev in revs])
923 923 filemode = {}
924 924
925 925 def single(rev, seqno, fp):
926 926 ctx = repo[rev]
927 927 node = ctx.node()
928 928 parents = [p.node() for p in ctx.parents() if p]
929 929 branch = ctx.branch()
930 930 if switch_parent:
931 931 parents.reverse()
932 932 prev = (parents and parents[0]) or nullid
933 933
934 934 shouldclose = False
935 935 if not fp and len(template) > 0:
936 936 desc_lines = ctx.description().rstrip().split('\n')
937 937 desc = desc_lines[0] #Commit always has a first line.
938 938 fp = makefileobj(repo, template, node, desc=desc, total=total,
939 939 seqno=seqno, revwidth=revwidth, mode='wb',
940 940 modemap=filemode)
941 941 if fp != template:
942 942 shouldclose = True
943 943 if fp and fp != sys.stdout and util.safehasattr(fp, 'name'):
944 944 repo.ui.note("%s\n" % fp.name)
945 945
946 946 if not fp:
947 947 write = repo.ui.write
948 948 else:
949 949 def write(s, **kw):
950 950 fp.write(s)
951 951
952 952
953 953 write("# HG changeset patch\n")
954 954 write("# User %s\n" % ctx.user())
955 955 write("# Date %d %d\n" % ctx.date())
956 956 write("# %s\n" % util.datestr(ctx.date()))
957 957 if branch and branch != 'default':
958 958 write("# Branch %s\n" % branch)
959 959 write("# Node ID %s\n" % hex(node))
960 960 write("# Parent %s\n" % hex(prev))
961 961 if len(parents) > 1:
962 962 write("# Parent %s\n" % hex(parents[1]))
963 963 write(ctx.description().rstrip())
964 964 write("\n\n")
965 965
966 966 for chunk, label in patch.diffui(repo, prev, node, opts=opts):
967 967 write(chunk, label=label)
968 968
969 969 if shouldclose:
970 970 fp.close()
971 971
972 972 for seqno, rev in enumerate(revs):
973 973 single(rev, seqno + 1, fp)
974 974
975 975 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
976 976 changes=None, stat=False, fp=None, prefix='',
977 977 listsubrepos=False):
978 978 '''show diff or diffstat.'''
979 979 if fp is None:
980 980 write = ui.write
981 981 else:
982 982 def write(s, **kw):
983 983 fp.write(s)
984 984
985 985 if stat:
986 986 diffopts = diffopts.copy(context=0)
987 987 width = 80
988 988 if not ui.plain():
989 989 width = ui.termwidth()
990 990 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
991 991 prefix=prefix)
992 992 for chunk, label in patch.diffstatui(util.iterlines(chunks),
993 993 width=width,
994 994 git=diffopts.git):
995 995 write(chunk, label=label)
996 996 else:
997 997 for chunk, label in patch.diffui(repo, node1, node2, match,
998 998 changes, diffopts, prefix=prefix):
999 999 write(chunk, label=label)
1000 1000
1001 1001 if listsubrepos:
1002 1002 ctx1 = repo[node1]
1003 1003 ctx2 = repo[node2]
1004 1004 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
1005 1005 tempnode2 = node2
1006 1006 try:
1007 1007 if node2 is not None:
1008 1008 tempnode2 = ctx2.substate[subpath][1]
1009 1009 except KeyError:
1010 1010 # A subrepo that existed in node1 was deleted between node1 and
1011 1011 # node2 (inclusive). Thus, ctx2's substate won't contain that
1012 1012 # subpath. The best we can do is to ignore it.
1013 1013 tempnode2 = None
1014 1014 submatch = matchmod.narrowmatcher(subpath, match)
1015 1015 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
1016 1016 stat=stat, fp=fp, prefix=prefix)
1017 1017
1018 1018 class changeset_printer(object):
1019 1019 '''show changeset information when templating not requested.'''
1020 1020
1021 1021 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1022 1022 self.ui = ui
1023 1023 self.repo = repo
1024 1024 self.buffered = buffered
1025 1025 self.matchfn = matchfn
1026 1026 self.diffopts = diffopts
1027 1027 self.header = {}
1028 1028 self.hunk = {}
1029 1029 self.lastheader = None
1030 1030 self.footer = None
1031 1031
1032 1032 def flush(self, rev):
1033 1033 if rev in self.header:
1034 1034 h = self.header[rev]
1035 1035 if h != self.lastheader:
1036 1036 self.lastheader = h
1037 1037 self.ui.write(h)
1038 1038 del self.header[rev]
1039 1039 if rev in self.hunk:
1040 1040 self.ui.write(self.hunk[rev])
1041 1041 del self.hunk[rev]
1042 1042 return 1
1043 1043 return 0
1044 1044
1045 1045 def close(self):
1046 1046 if self.footer:
1047 1047 self.ui.write(self.footer)
1048 1048
1049 1049 def show(self, ctx, copies=None, matchfn=None, **props):
1050 1050 if self.buffered:
1051 1051 self.ui.pushbuffer()
1052 1052 self._show(ctx, copies, matchfn, props)
1053 1053 self.hunk[ctx.rev()] = self.ui.popbuffer(labeled=True)
1054 1054 else:
1055 1055 self._show(ctx, copies, matchfn, props)
1056 1056
1057 1057 def _show(self, ctx, copies, matchfn, props):
1058 1058 '''show a single changeset or file revision'''
1059 1059 changenode = ctx.node()
1060 1060 rev = ctx.rev()
1061 1061
1062 1062 if self.ui.quiet:
1063 1063 self.ui.write("%d:%s\n" % (rev, short(changenode)),
1064 1064 label='log.node')
1065 1065 return
1066 1066
1067 1067 log = self.repo.changelog
1068 1068 date = util.datestr(ctx.date())
1069 1069
1070 1070 hexfunc = self.ui.debugflag and hex or short
1071 1071
1072 1072 parents = [(p, hexfunc(log.node(p)))
1073 1073 for p in self._meaningful_parentrevs(log, rev)]
1074 1074
1075 1075 # i18n: column positioning for "hg log"
1076 1076 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)),
1077 1077 label='log.changeset changeset.%s' % ctx.phasestr())
1078 1078
1079 1079 # branches are shown first before any other names due to backwards
1080 1080 # compatibility
1081 1081 branch = ctx.branch()
1082 1082 # don't show the default branch name
1083 1083 if branch != 'default':
1084 1084 # i18n: column positioning for "hg log"
1085 1085 self.ui.write(_("branch: %s\n") % branch,
1086 1086 label='log.branch')
1087 1087
1088 1088 for name, ns in self.repo.names.iteritems():
1089 1089 # branches has special logic already handled above, so here we just
1090 1090 # skip it
1091 1091 if name == 'branches':
1092 1092 continue
1093 1093 # we will use the templatename as the color name since those two
1094 1094 # should be the same
1095 1095 for name in ns.names(self.repo, changenode):
1096 1096 self.ui.write(ns.logfmt % name,
1097 1097 label='log.%s' % ns.colorname)
1098 1098 if self.ui.debugflag:
1099 1099 # i18n: column positioning for "hg log"
1100 1100 self.ui.write(_("phase: %s\n") % _(ctx.phasestr()),
1101 1101 label='log.phase')
1102 1102 for parent in parents:
1103 1103 label = 'log.parent changeset.%s' % self.repo[parent[0]].phasestr()
1104 1104 # i18n: column positioning for "hg log"
1105 1105 self.ui.write(_("parent: %d:%s\n") % parent,
1106 1106 label=label)
1107 1107
1108 1108 if self.ui.debugflag:
1109 1109 mnode = ctx.manifestnode()
1110 1110 # i18n: column positioning for "hg log"
1111 1111 self.ui.write(_("manifest: %d:%s\n") %
1112 1112 (self.repo.manifest.rev(mnode), hex(mnode)),
1113 1113 label='ui.debug log.manifest')
1114 1114 # i18n: column positioning for "hg log"
1115 1115 self.ui.write(_("user: %s\n") % ctx.user(),
1116 1116 label='log.user')
1117 1117 # i18n: column positioning for "hg log"
1118 1118 self.ui.write(_("date: %s\n") % date,
1119 1119 label='log.date')
1120 1120
1121 1121 if self.ui.debugflag:
1122 1122 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
1123 1123 for key, value in zip([# i18n: column positioning for "hg log"
1124 1124 _("files:"),
1125 1125 # i18n: column positioning for "hg log"
1126 1126 _("files+:"),
1127 1127 # i18n: column positioning for "hg log"
1128 1128 _("files-:")], files):
1129 1129 if value:
1130 1130 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
1131 1131 label='ui.debug log.files')
1132 1132 elif ctx.files() and self.ui.verbose:
1133 1133 # i18n: column positioning for "hg log"
1134 1134 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
1135 1135 label='ui.note log.files')
1136 1136 if copies and self.ui.verbose:
1137 1137 copies = ['%s (%s)' % c for c in copies]
1138 1138 # i18n: column positioning for "hg log"
1139 1139 self.ui.write(_("copies: %s\n") % ' '.join(copies),
1140 1140 label='ui.note log.copies')
1141 1141
1142 1142 extra = ctx.extra()
1143 1143 if extra and self.ui.debugflag:
1144 1144 for key, value in sorted(extra.items()):
1145 1145 # i18n: column positioning for "hg log"
1146 1146 self.ui.write(_("extra: %s=%s\n")
1147 1147 % (key, value.encode('string_escape')),
1148 1148 label='ui.debug log.extra')
1149 1149
1150 1150 description = ctx.description().strip()
1151 1151 if description:
1152 1152 if self.ui.verbose:
1153 1153 self.ui.write(_("description:\n"),
1154 1154 label='ui.note log.description')
1155 1155 self.ui.write(description,
1156 1156 label='ui.note log.description')
1157 1157 self.ui.write("\n\n")
1158 1158 else:
1159 1159 # i18n: column positioning for "hg log"
1160 1160 self.ui.write(_("summary: %s\n") %
1161 1161 description.splitlines()[0],
1162 1162 label='log.summary')
1163 1163 self.ui.write("\n")
1164 1164
1165 1165 self.showpatch(changenode, matchfn)
1166 1166
1167 1167 def showpatch(self, node, matchfn):
1168 1168 if not matchfn:
1169 1169 matchfn = self.matchfn
1170 1170 if matchfn:
1171 1171 stat = self.diffopts.get('stat')
1172 1172 diff = self.diffopts.get('patch')
1173 1173 diffopts = patch.diffallopts(self.ui, self.diffopts)
1174 1174 prev = self.repo.changelog.parents(node)[0]
1175 1175 if stat:
1176 1176 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1177 1177 match=matchfn, stat=True)
1178 1178 if diff:
1179 1179 if stat:
1180 1180 self.ui.write("\n")
1181 1181 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1182 1182 match=matchfn, stat=False)
1183 1183 self.ui.write("\n")
1184 1184
1185 1185 def _meaningful_parentrevs(self, log, rev):
1186 1186 """Return list of meaningful (or all if debug) parentrevs for rev.
1187 1187
1188 1188 For merges (two non-nullrev revisions) both parents are meaningful.
1189 1189 Otherwise the first parent revision is considered meaningful if it
1190 1190 is not the preceding revision.
1191 1191 """
1192 1192 parents = log.parentrevs(rev)
1193 1193 if not self.ui.debugflag and parents[1] == nullrev:
1194 1194 if parents[0] >= rev - 1:
1195 1195 parents = []
1196 1196 else:
1197 1197 parents = [parents[0]]
1198 1198 return parents
1199 1199
1200 1200 class jsonchangeset(changeset_printer):
1201 1201 '''format changeset information.'''
1202 1202
1203 1203 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1204 1204 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1205 1205 self.cache = {}
1206 1206 self._first = True
1207 1207
1208 1208 def close(self):
1209 1209 if not self._first:
1210 1210 self.ui.write("\n]\n")
1211 1211 else:
1212 1212 self.ui.write("[]\n")
1213 1213
1214 1214 def _show(self, ctx, copies, matchfn, props):
1215 1215 '''show a single changeset or file revision'''
1216 1216 hexnode = hex(ctx.node())
1217 1217 rev = ctx.rev()
1218 1218 j = encoding.jsonescape
1219 1219
1220 1220 if self._first:
1221 1221 self.ui.write("[\n {")
1222 1222 self._first = False
1223 1223 else:
1224 1224 self.ui.write(",\n {")
1225 1225
1226 1226 if self.ui.quiet:
1227 1227 self.ui.write('\n "rev": %d' % rev)
1228 1228 self.ui.write(',\n "node": "%s"' % hexnode)
1229 1229 self.ui.write('\n }')
1230 1230 return
1231 1231
1232 1232 self.ui.write('\n "rev": %d' % rev)
1233 1233 self.ui.write(',\n "node": "%s"' % hexnode)
1234 1234 self.ui.write(',\n "branch": "%s"' % j(ctx.branch()))
1235 1235 self.ui.write(',\n "phase": "%s"' % ctx.phasestr())
1236 1236 self.ui.write(',\n "user": "%s"' % j(ctx.user()))
1237 1237 self.ui.write(',\n "date": [%d, %d]' % ctx.date())
1238 1238 self.ui.write(',\n "desc": "%s"' % j(ctx.description()))
1239 1239
1240 1240 self.ui.write(',\n "bookmarks": [%s]' %
1241 1241 ", ".join('"%s"' % j(b) for b in ctx.bookmarks()))
1242 1242 self.ui.write(',\n "tags": [%s]' %
1243 1243 ", ".join('"%s"' % j(t) for t in ctx.tags()))
1244 1244 self.ui.write(',\n "parents": [%s]' %
1245 1245 ", ".join('"%s"' % c.hex() for c in ctx.parents()))
1246 1246
1247 1247 if self.ui.debugflag:
1248 1248 self.ui.write(',\n "manifest": "%s"' % hex(ctx.manifestnode()))
1249 1249
1250 1250 self.ui.write(',\n "extra": {%s}' %
1251 1251 ", ".join('"%s": "%s"' % (j(k), j(v))
1252 1252 for k, v in ctx.extra().items()))
1253 1253
1254 1254 files = ctx.p1().status(ctx)
1255 1255 self.ui.write(',\n "modified": [%s]' %
1256 1256 ", ".join('"%s"' % j(f) for f in files[0]))
1257 1257 self.ui.write(',\n "added": [%s]' %
1258 1258 ", ".join('"%s"' % j(f) for f in files[1]))
1259 1259 self.ui.write(',\n "removed": [%s]' %
1260 1260 ", ".join('"%s"' % j(f) for f in files[2]))
1261 1261
1262 1262 elif self.ui.verbose:
1263 1263 self.ui.write(',\n "files": [%s]' %
1264 1264 ", ".join('"%s"' % j(f) for f in ctx.files()))
1265 1265
1266 1266 if copies:
1267 1267 self.ui.write(',\n "copies": {%s}' %
1268 1268 ", ".join('"%s": "%s"' % (j(k), j(v))
1269 1269 for k, v in copies))
1270 1270
1271 1271 matchfn = self.matchfn
1272 1272 if matchfn:
1273 1273 stat = self.diffopts.get('stat')
1274 1274 diff = self.diffopts.get('patch')
1275 1275 diffopts = patch.difffeatureopts(self.ui, self.diffopts, git=True)
1276 1276 node, prev = ctx.node(), ctx.p1().node()
1277 1277 if stat:
1278 1278 self.ui.pushbuffer()
1279 1279 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1280 1280 match=matchfn, stat=True)
1281 1281 self.ui.write(',\n "diffstat": "%s"' % j(self.ui.popbuffer()))
1282 1282 if diff:
1283 1283 self.ui.pushbuffer()
1284 1284 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1285 1285 match=matchfn, stat=False)
1286 1286 self.ui.write(',\n "diff": "%s"' % j(self.ui.popbuffer()))
1287 1287
1288 1288 self.ui.write("\n }")
1289 1289
1290 1290 class changeset_templater(changeset_printer):
1291 1291 '''format changeset information.'''
1292 1292
1293 1293 def __init__(self, ui, repo, matchfn, diffopts, tmpl, mapfile, buffered):
1294 1294 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1295 1295 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
1296 1296 defaulttempl = {
1297 1297 'parent': '{rev}:{node|formatnode} ',
1298 1298 'manifest': '{rev}:{node|formatnode}',
1299 1299 'file_copy': '{name} ({source})',
1300 1300 'extra': '{key}={value|stringescape}'
1301 1301 }
1302 1302 # filecopy is preserved for compatibility reasons
1303 1303 defaulttempl['filecopy'] = defaulttempl['file_copy']
1304 1304 self.t = templater.templater(mapfile, {'formatnode': formatnode},
1305 1305 cache=defaulttempl)
1306 1306 if tmpl:
1307 1307 self.t.cache['changeset'] = tmpl
1308 1308
1309 1309 self.cache = {}
1310 1310
1311 1311 def _meaningful_parentrevs(self, ctx):
1312 1312 """Return list of meaningful (or all if debug) parentrevs for rev.
1313 1313 """
1314 1314 parents = ctx.parents()
1315 1315 if len(parents) > 1:
1316 1316 return parents
1317 1317 if self.ui.debugflag:
1318 1318 return [parents[0], self.repo['null']]
1319 1319 if parents[0].rev() >= ctx.rev() - 1:
1320 1320 return []
1321 1321 return parents
1322 1322
1323 1323 def _show(self, ctx, copies, matchfn, props):
1324 1324 '''show a single changeset or file revision'''
1325 1325
1326 1326 showlist = templatekw.showlist
1327 1327
1328 1328 # showparents() behaviour depends on ui trace level which
1329 1329 # causes unexpected behaviours at templating level and makes
1330 1330 # it harder to extract it in a standalone function. Its
1331 1331 # behaviour cannot be changed so leave it here for now.
1332 1332 def showparents(**args):
1333 1333 ctx = args['ctx']
1334 1334 parents = [[('rev', p.rev()),
1335 1335 ('node', p.hex()),
1336 1336 ('phase', p.phasestr())]
1337 1337 for p in self._meaningful_parentrevs(ctx)]
1338 1338 return showlist('parent', parents, **args)
1339 1339
1340 1340 props = props.copy()
1341 1341 props.update(templatekw.keywords)
1342 1342 props['parents'] = showparents
1343 1343 props['templ'] = self.t
1344 1344 props['ctx'] = ctx
1345 1345 props['repo'] = self.repo
1346 1346 props['revcache'] = {'copies': copies}
1347 1347 props['cache'] = self.cache
1348 1348
1349 1349 # find correct templates for current mode
1350 1350
1351 1351 tmplmodes = [
1352 1352 (True, None),
1353 1353 (self.ui.verbose, 'verbose'),
1354 1354 (self.ui.quiet, 'quiet'),
1355 1355 (self.ui.debugflag, 'debug'),
1356 1356 ]
1357 1357
1358 1358 types = {'header': '', 'footer':'', 'changeset': 'changeset'}
1359 1359 for mode, postfix in tmplmodes:
1360 1360 for type in types:
1361 1361 cur = postfix and ('%s_%s' % (type, postfix)) or type
1362 1362 if mode and cur in self.t:
1363 1363 types[type] = cur
1364 1364
1365 1365 try:
1366 1366
1367 1367 # write header
1368 1368 if types['header']:
1369 1369 h = templater.stringify(self.t(types['header'], **props))
1370 1370 if self.buffered:
1371 1371 self.header[ctx.rev()] = h
1372 1372 else:
1373 1373 if self.lastheader != h:
1374 1374 self.lastheader = h
1375 1375 self.ui.write(h)
1376 1376
1377 1377 # write changeset metadata, then patch if requested
1378 1378 key = types['changeset']
1379 1379 self.ui.write(templater.stringify(self.t(key, **props)))
1380 1380 self.showpatch(ctx.node(), matchfn)
1381 1381
1382 1382 if types['footer']:
1383 1383 if not self.footer:
1384 1384 self.footer = templater.stringify(self.t(types['footer'],
1385 1385 **props))
1386 1386
1387 1387 except KeyError, inst:
1388 1388 msg = _("%s: no key named '%s'")
1389 1389 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
1390 1390 except SyntaxError, inst:
1391 1391 raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
1392 1392
1393 1393 def gettemplate(ui, tmpl, style):
1394 1394 """
1395 1395 Find the template matching the given template spec or style.
1396 1396 """
1397 1397
1398 1398 # ui settings
1399 1399 if not tmpl and not style: # template are stronger than style
1400 1400 tmpl = ui.config('ui', 'logtemplate')
1401 1401 if tmpl:
1402 1402 try:
1403 1403 tmpl = templater.parsestring(tmpl)
1404 1404 except SyntaxError:
1405 1405 tmpl = templater.parsestring(tmpl, quoted=False)
1406 1406 return tmpl, None
1407 1407 else:
1408 1408 style = util.expandpath(ui.config('ui', 'style', ''))
1409 1409
1410 1410 if not tmpl and style:
1411 1411 mapfile = style
1412 1412 if not os.path.split(mapfile)[0]:
1413 1413 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1414 1414 or templater.templatepath(mapfile))
1415 1415 if mapname:
1416 1416 mapfile = mapname
1417 1417 return None, mapfile
1418 1418
1419 1419 if not tmpl:
1420 1420 return None, None
1421 1421
1422 1422 # looks like a literal template?
1423 1423 if '{' in tmpl:
1424 1424 return tmpl, None
1425 1425
1426 1426 # perhaps a stock style?
1427 1427 if not os.path.split(tmpl)[0]:
1428 1428 mapname = (templater.templatepath('map-cmdline.' + tmpl)
1429 1429 or templater.templatepath(tmpl))
1430 1430 if mapname and os.path.isfile(mapname):
1431 1431 return None, mapname
1432 1432
1433 1433 # perhaps it's a reference to [templates]
1434 1434 t = ui.config('templates', tmpl)
1435 1435 if t:
1436 1436 try:
1437 1437 tmpl = templater.parsestring(t)
1438 1438 except SyntaxError:
1439 1439 tmpl = templater.parsestring(t, quoted=False)
1440 1440 return tmpl, None
1441 1441
1442 1442 if tmpl == 'list':
1443 1443 ui.write(_("available styles: %s\n") % templater.stylelist())
1444 1444 raise util.Abort(_("specify a template"))
1445 1445
1446 1446 # perhaps it's a path to a map or a template
1447 1447 if ('/' in tmpl or '\\' in tmpl) and os.path.isfile(tmpl):
1448 1448 # is it a mapfile for a style?
1449 1449 if os.path.basename(tmpl).startswith("map-"):
1450 1450 return None, os.path.realpath(tmpl)
1451 1451 tmpl = open(tmpl).read()
1452 1452 return tmpl, None
1453 1453
1454 1454 # constant string?
1455 1455 return tmpl, None
1456 1456
1457 1457 def show_changeset(ui, repo, opts, buffered=False):
1458 1458 """show one changeset using template or regular display.
1459 1459
1460 1460 Display format will be the first non-empty hit of:
1461 1461 1. option 'template'
1462 1462 2. option 'style'
1463 1463 3. [ui] setting 'logtemplate'
1464 1464 4. [ui] setting 'style'
1465 1465 If all of these values are either the unset or the empty string,
1466 1466 regular display via changeset_printer() is done.
1467 1467 """
1468 1468 # options
1469 1469 matchfn = None
1470 1470 if opts.get('patch') or opts.get('stat'):
1471 1471 matchfn = scmutil.matchall(repo)
1472 1472
1473 1473 if opts.get('template') == 'json':
1474 1474 return jsonchangeset(ui, repo, matchfn, opts, buffered)
1475 1475
1476 1476 tmpl, mapfile = gettemplate(ui, opts.get('template'), opts.get('style'))
1477 1477
1478 1478 if not tmpl and not mapfile:
1479 1479 return changeset_printer(ui, repo, matchfn, opts, buffered)
1480 1480
1481 1481 try:
1482 1482 t = changeset_templater(ui, repo, matchfn, opts, tmpl, mapfile,
1483 1483 buffered)
1484 1484 except SyntaxError, inst:
1485 1485 raise util.Abort(inst.args[0])
1486 1486 return t
1487 1487
1488 1488 def showmarker(ui, marker):
1489 1489 """utility function to display obsolescence marker in a readable way
1490 1490
1491 1491 To be used by debug function."""
1492 1492 ui.write(hex(marker.precnode()))
1493 1493 for repl in marker.succnodes():
1494 1494 ui.write(' ')
1495 1495 ui.write(hex(repl))
1496 1496 ui.write(' %X ' % marker.flags())
1497 1497 parents = marker.parentnodes()
1498 1498 if parents is not None:
1499 1499 ui.write('{%s} ' % ', '.join(hex(p) for p in parents))
1500 1500 ui.write('(%s) ' % util.datestr(marker.date()))
1501 1501 ui.write('{%s}' % (', '.join('%r: %r' % t for t in
1502 1502 sorted(marker.metadata().items())
1503 1503 if t[0] != 'date')))
1504 1504 ui.write('\n')
1505 1505
1506 1506 def finddate(ui, repo, date):
1507 1507 """Find the tipmost changeset that matches the given date spec"""
1508 1508
1509 1509 df = util.matchdate(date)
1510 1510 m = scmutil.matchall(repo)
1511 1511 results = {}
1512 1512
1513 1513 def prep(ctx, fns):
1514 1514 d = ctx.date()
1515 1515 if df(d[0]):
1516 1516 results[ctx.rev()] = d
1517 1517
1518 1518 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
1519 1519 rev = ctx.rev()
1520 1520 if rev in results:
1521 1521 ui.status(_("found revision %s from %s\n") %
1522 1522 (rev, util.datestr(results[rev])))
1523 1523 return str(rev)
1524 1524
1525 1525 raise util.Abort(_("revision matching date not found"))
1526 1526
1527 1527 def increasingwindows(windowsize=8, sizelimit=512):
1528 1528 while True:
1529 1529 yield windowsize
1530 1530 if windowsize < sizelimit:
1531 1531 windowsize *= 2
1532 1532
1533 1533 class FileWalkError(Exception):
1534 1534 pass
1535 1535
1536 1536 def walkfilerevs(repo, match, follow, revs, fncache):
1537 1537 '''Walks the file history for the matched files.
1538 1538
1539 1539 Returns the changeset revs that are involved in the file history.
1540 1540
1541 1541 Throws FileWalkError if the file history can't be walked using
1542 1542 filelogs alone.
1543 1543 '''
1544 1544 wanted = set()
1545 1545 copies = []
1546 1546 minrev, maxrev = min(revs), max(revs)
1547 1547 def filerevgen(filelog, last):
1548 1548 """
1549 1549 Only files, no patterns. Check the history of each file.
1550 1550
1551 1551 Examines filelog entries within minrev, maxrev linkrev range
1552 1552 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1553 1553 tuples in backwards order
1554 1554 """
1555 1555 cl_count = len(repo)
1556 1556 revs = []
1557 1557 for j in xrange(0, last + 1):
1558 1558 linkrev = filelog.linkrev(j)
1559 1559 if linkrev < minrev:
1560 1560 continue
1561 1561 # only yield rev for which we have the changelog, it can
1562 1562 # happen while doing "hg log" during a pull or commit
1563 1563 if linkrev >= cl_count:
1564 1564 break
1565 1565
1566 1566 parentlinkrevs = []
1567 1567 for p in filelog.parentrevs(j):
1568 1568 if p != nullrev:
1569 1569 parentlinkrevs.append(filelog.linkrev(p))
1570 1570 n = filelog.node(j)
1571 1571 revs.append((linkrev, parentlinkrevs,
1572 1572 follow and filelog.renamed(n)))
1573 1573
1574 1574 return reversed(revs)
1575 1575 def iterfiles():
1576 1576 pctx = repo['.']
1577 1577 for filename in match.files():
1578 1578 if follow:
1579 1579 if filename not in pctx:
1580 1580 raise util.Abort(_('cannot follow file not in parent '
1581 1581 'revision: "%s"') % filename)
1582 1582 yield filename, pctx[filename].filenode()
1583 1583 else:
1584 1584 yield filename, None
1585 1585 for filename_node in copies:
1586 1586 yield filename_node
1587 1587
1588 1588 for file_, node in iterfiles():
1589 1589 filelog = repo.file(file_)
1590 1590 if not len(filelog):
1591 1591 if node is None:
1592 1592 # A zero count may be a directory or deleted file, so
1593 1593 # try to find matching entries on the slow path.
1594 1594 if follow:
1595 1595 raise util.Abort(
1596 1596 _('cannot follow nonexistent file: "%s"') % file_)
1597 1597 raise FileWalkError("Cannot walk via filelog")
1598 1598 else:
1599 1599 continue
1600 1600
1601 1601 if node is None:
1602 1602 last = len(filelog) - 1
1603 1603 else:
1604 1604 last = filelog.rev(node)
1605 1605
1606 1606
1607 1607 # keep track of all ancestors of the file
1608 1608 ancestors = set([filelog.linkrev(last)])
1609 1609
1610 1610 # iterate from latest to oldest revision
1611 1611 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1612 1612 if not follow:
1613 1613 if rev > maxrev:
1614 1614 continue
1615 1615 else:
1616 1616 # Note that last might not be the first interesting
1617 1617 # rev to us:
1618 1618 # if the file has been changed after maxrev, we'll
1619 1619 # have linkrev(last) > maxrev, and we still need
1620 1620 # to explore the file graph
1621 1621 if rev not in ancestors:
1622 1622 continue
1623 1623 # XXX insert 1327 fix here
1624 1624 if flparentlinkrevs:
1625 1625 ancestors.update(flparentlinkrevs)
1626 1626
1627 1627 fncache.setdefault(rev, []).append(file_)
1628 1628 wanted.add(rev)
1629 1629 if copied:
1630 1630 copies.append(copied)
1631 1631
1632 1632 return wanted
1633 1633
1634 1634 def walkchangerevs(repo, match, opts, prepare):
1635 1635 '''Iterate over files and the revs in which they changed.
1636 1636
1637 1637 Callers most commonly need to iterate backwards over the history
1638 1638 in which they are interested. Doing so has awful (quadratic-looking)
1639 1639 performance, so we use iterators in a "windowed" way.
1640 1640
1641 1641 We walk a window of revisions in the desired order. Within the
1642 1642 window, we first walk forwards to gather data, then in the desired
1643 1643 order (usually backwards) to display it.
1644 1644
1645 1645 This function returns an iterator yielding contexts. Before
1646 1646 yielding each context, the iterator will first call the prepare
1647 1647 function on each context in the window in forward order.'''
1648 1648
1649 1649 follow = opts.get('follow') or opts.get('follow_first')
1650 1650 revs = _logrevs(repo, opts)
1651 1651 if not revs:
1652 1652 return []
1653 1653 wanted = set()
1654 1654 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1655 1655 fncache = {}
1656 1656 change = repo.changectx
1657 1657
1658 1658 # First step is to fill wanted, the set of revisions that we want to yield.
1659 1659 # When it does not induce extra cost, we also fill fncache for revisions in
1660 1660 # wanted: a cache of filenames that were changed (ctx.files()) and that
1661 1661 # match the file filtering conditions.
1662 1662
1663 1663 if not slowpath and not match.files():
1664 1664 # No files, no patterns. Display all revs.
1665 1665 wanted = revs
1666 1666
1667 1667 if not slowpath and match.files():
1668 1668 # We only have to read through the filelog to find wanted revisions
1669 1669
1670 1670 try:
1671 1671 wanted = walkfilerevs(repo, match, follow, revs, fncache)
1672 1672 except FileWalkError:
1673 1673 slowpath = True
1674 1674
1675 1675 # We decided to fall back to the slowpath because at least one
1676 1676 # of the paths was not a file. Check to see if at least one of them
1677 1677 # existed in history, otherwise simply return
1678 1678 for path in match.files():
1679 1679 if path == '.' or path in repo.store:
1680 1680 break
1681 1681 else:
1682 1682 return []
1683 1683
1684 1684 if slowpath:
1685 1685 # We have to read the changelog to match filenames against
1686 1686 # changed files
1687 1687
1688 1688 if follow:
1689 1689 raise util.Abort(_('can only follow copies/renames for explicit '
1690 1690 'filenames'))
1691 1691
1692 1692 # The slow path checks files modified in every changeset.
1693 1693 # This is really slow on large repos, so compute the set lazily.
1694 1694 class lazywantedset(object):
1695 1695 def __init__(self):
1696 1696 self.set = set()
1697 1697 self.revs = set(revs)
1698 1698
1699 1699 # No need to worry about locality here because it will be accessed
1700 1700 # in the same order as the increasing window below.
1701 1701 def __contains__(self, value):
1702 1702 if value in self.set:
1703 1703 return True
1704 1704 elif not value in self.revs:
1705 1705 return False
1706 1706 else:
1707 1707 self.revs.discard(value)
1708 1708 ctx = change(value)
1709 1709 matches = filter(match, ctx.files())
1710 1710 if matches:
1711 1711 fncache[value] = matches
1712 1712 self.set.add(value)
1713 1713 return True
1714 1714 return False
1715 1715
1716 1716 def discard(self, value):
1717 1717 self.revs.discard(value)
1718 1718 self.set.discard(value)
1719 1719
1720 1720 wanted = lazywantedset()
1721 1721
1722 1722 class followfilter(object):
1723 1723 def __init__(self, onlyfirst=False):
1724 1724 self.startrev = nullrev
1725 1725 self.roots = set()
1726 1726 self.onlyfirst = onlyfirst
1727 1727
1728 1728 def match(self, rev):
1729 1729 def realparents(rev):
1730 1730 if self.onlyfirst:
1731 1731 return repo.changelog.parentrevs(rev)[0:1]
1732 1732 else:
1733 1733 return filter(lambda x: x != nullrev,
1734 1734 repo.changelog.parentrevs(rev))
1735 1735
1736 1736 if self.startrev == nullrev:
1737 1737 self.startrev = rev
1738 1738 return True
1739 1739
1740 1740 if rev > self.startrev:
1741 1741 # forward: all descendants
1742 1742 if not self.roots:
1743 1743 self.roots.add(self.startrev)
1744 1744 for parent in realparents(rev):
1745 1745 if parent in self.roots:
1746 1746 self.roots.add(rev)
1747 1747 return True
1748 1748 else:
1749 1749 # backwards: all parents
1750 1750 if not self.roots:
1751 1751 self.roots.update(realparents(self.startrev))
1752 1752 if rev in self.roots:
1753 1753 self.roots.remove(rev)
1754 1754 self.roots.update(realparents(rev))
1755 1755 return True
1756 1756
1757 1757 return False
1758 1758
1759 1759 # it might be worthwhile to do this in the iterator if the rev range
1760 1760 # is descending and the prune args are all within that range
1761 1761 for rev in opts.get('prune', ()):
1762 1762 rev = repo[rev].rev()
1763 1763 ff = followfilter()
1764 1764 stop = min(revs[0], revs[-1])
1765 1765 for x in xrange(rev, stop - 1, -1):
1766 1766 if ff.match(x):
1767 1767 wanted = wanted - [x]
1768 1768
1769 1769 # Now that wanted is correctly initialized, we can iterate over the
1770 1770 # revision range, yielding only revisions in wanted.
1771 1771 def iterate():
1772 1772 if follow and not match.files():
1773 1773 ff = followfilter(onlyfirst=opts.get('follow_first'))
1774 1774 def want(rev):
1775 1775 return ff.match(rev) and rev in wanted
1776 1776 else:
1777 1777 def want(rev):
1778 1778 return rev in wanted
1779 1779
1780 1780 it = iter(revs)
1781 1781 stopiteration = False
1782 1782 for windowsize in increasingwindows():
1783 1783 nrevs = []
1784 1784 for i in xrange(windowsize):
1785 1785 try:
1786 1786 rev = it.next()
1787 1787 if want(rev):
1788 1788 nrevs.append(rev)
1789 1789 except (StopIteration):
1790 1790 stopiteration = True
1791 1791 break
1792 1792 for rev in sorted(nrevs):
1793 1793 fns = fncache.get(rev)
1794 1794 ctx = change(rev)
1795 1795 if not fns:
1796 1796 def fns_generator():
1797 1797 for f in ctx.files():
1798 1798 if match(f):
1799 1799 yield f
1800 1800 fns = fns_generator()
1801 1801 prepare(ctx, fns)
1802 1802 for rev in nrevs:
1803 1803 yield change(rev)
1804 1804
1805 1805 if stopiteration:
1806 1806 break
1807 1807
1808 1808 return iterate()
1809 1809
1810 1810 def _makefollowlogfilematcher(repo, files, followfirst):
1811 1811 # When displaying a revision with --patch --follow FILE, we have
1812 1812 # to know which file of the revision must be diffed. With
1813 1813 # --follow, we want the names of the ancestors of FILE in the
1814 1814 # revision, stored in "fcache". "fcache" is populated by
1815 1815 # reproducing the graph traversal already done by --follow revset
1816 1816 # and relating linkrevs to file names (which is not "correct" but
1817 1817 # good enough).
1818 1818 fcache = {}
1819 1819 fcacheready = [False]
1820 1820 pctx = repo['.']
1821 1821
1822 1822 def populate():
1823 1823 for fn in files:
1824 1824 for i in ((pctx[fn],), pctx[fn].ancestors(followfirst=followfirst)):
1825 1825 for c in i:
1826 1826 fcache.setdefault(c.linkrev(), set()).add(c.path())
1827 1827
1828 1828 def filematcher(rev):
1829 1829 if not fcacheready[0]:
1830 1830 # Lazy initialization
1831 1831 fcacheready[0] = True
1832 1832 populate()
1833 1833 return scmutil.matchfiles(repo, fcache.get(rev, []))
1834 1834
1835 1835 return filematcher
1836 1836
1837 1837 def _makenofollowlogfilematcher(repo, pats, opts):
1838 1838 '''hook for extensions to override the filematcher for non-follow cases'''
1839 1839 return None
1840 1840
1841 1841 def _makelogrevset(repo, pats, opts, revs):
1842 1842 """Return (expr, filematcher) where expr is a revset string built
1843 1843 from log options and file patterns or None. If --stat or --patch
1844 1844 are not passed filematcher is None. Otherwise it is a callable
1845 1845 taking a revision number and returning a match objects filtering
1846 1846 the files to be detailed when displaying the revision.
1847 1847 """
1848 1848 opt2revset = {
1849 1849 'no_merges': ('not merge()', None),
1850 1850 'only_merges': ('merge()', None),
1851 1851 '_ancestors': ('ancestors(%(val)s)', None),
1852 1852 '_fancestors': ('_firstancestors(%(val)s)', None),
1853 1853 '_descendants': ('descendants(%(val)s)', None),
1854 1854 '_fdescendants': ('_firstdescendants(%(val)s)', None),
1855 1855 '_matchfiles': ('_matchfiles(%(val)s)', None),
1856 1856 'date': ('date(%(val)r)', None),
1857 1857 'branch': ('branch(%(val)r)', ' or '),
1858 1858 '_patslog': ('filelog(%(val)r)', ' or '),
1859 1859 '_patsfollow': ('follow(%(val)r)', ' or '),
1860 1860 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
1861 1861 'keyword': ('keyword(%(val)r)', ' or '),
1862 1862 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
1863 1863 'user': ('user(%(val)r)', ' or '),
1864 1864 }
1865 1865
1866 1866 opts = dict(opts)
1867 1867 # follow or not follow?
1868 1868 follow = opts.get('follow') or opts.get('follow_first')
1869 1869 followfirst = opts.get('follow_first') and 1 or 0
1870 1870 # --follow with FILE behaviour depends on revs...
1871 1871 it = iter(revs)
1872 1872 startrev = it.next()
1873 1873 try:
1874 1874 followdescendants = startrev < it.next()
1875 1875 except (StopIteration):
1876 1876 followdescendants = False
1877 1877
1878 1878 # branch and only_branch are really aliases and must be handled at
1879 1879 # the same time
1880 1880 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
1881 1881 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
1882 1882 # pats/include/exclude are passed to match.match() directly in
1883 1883 # _matchfiles() revset but walkchangerevs() builds its matcher with
1884 1884 # scmutil.match(). The difference is input pats are globbed on
1885 1885 # platforms without shell expansion (windows).
1886 1886 pctx = repo[None]
1887 1887 match, pats = scmutil.matchandpats(pctx, pats, opts)
1888 1888 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1889 1889 if not slowpath:
1890 1890 for f in match.files():
1891 1891 if follow and f not in pctx:
1892 1892 # If the file exists, it may be a directory, so let it
1893 1893 # take the slow path.
1894 1894 if os.path.exists(repo.wjoin(f)):
1895 1895 slowpath = True
1896 1896 continue
1897 1897 else:
1898 1898 raise util.Abort(_('cannot follow file not in parent '
1899 1899 'revision: "%s"') % f)
1900 1900 filelog = repo.file(f)
1901 1901 if not filelog:
1902 1902 # A zero count may be a directory or deleted file, so
1903 1903 # try to find matching entries on the slow path.
1904 1904 if follow:
1905 1905 raise util.Abort(
1906 1906 _('cannot follow nonexistent file: "%s"') % f)
1907 1907 slowpath = True
1908 1908
1909 1909 # We decided to fall back to the slowpath because at least one
1910 1910 # of the paths was not a file. Check to see if at least one of them
1911 1911 # existed in history - in that case, we'll continue down the
1912 1912 # slowpath; otherwise, we can turn off the slowpath
1913 1913 if slowpath:
1914 1914 for path in match.files():
1915 1915 if path == '.' or path in repo.store:
1916 1916 break
1917 1917 else:
1918 1918 slowpath = False
1919 1919
1920 1920 fpats = ('_patsfollow', '_patsfollowfirst')
1921 1921 fnopats = (('_ancestors', '_fancestors'),
1922 1922 ('_descendants', '_fdescendants'))
1923 1923 if slowpath:
1924 1924 # See walkchangerevs() slow path.
1925 1925 #
1926 1926 # pats/include/exclude cannot be represented as separate
1927 1927 # revset expressions as their filtering logic applies at file
1928 1928 # level. For instance "-I a -X a" matches a revision touching
1929 1929 # "a" and "b" while "file(a) and not file(b)" does
1930 1930 # not. Besides, filesets are evaluated against the working
1931 1931 # directory.
1932 1932 matchargs = ['r:', 'd:relpath']
1933 1933 for p in pats:
1934 1934 matchargs.append('p:' + p)
1935 1935 for p in opts.get('include', []):
1936 1936 matchargs.append('i:' + p)
1937 1937 for p in opts.get('exclude', []):
1938 1938 matchargs.append('x:' + p)
1939 1939 matchargs = ','.join(('%r' % p) for p in matchargs)
1940 1940 opts['_matchfiles'] = matchargs
1941 1941 if follow:
1942 1942 opts[fnopats[0][followfirst]] = '.'
1943 1943 else:
1944 1944 if follow:
1945 1945 if pats:
1946 1946 # follow() revset interprets its file argument as a
1947 1947 # manifest entry, so use match.files(), not pats.
1948 1948 opts[fpats[followfirst]] = list(match.files())
1949 1949 else:
1950 1950 op = fnopats[followdescendants][followfirst]
1951 1951 opts[op] = 'rev(%d)' % startrev
1952 1952 else:
1953 1953 opts['_patslog'] = list(pats)
1954 1954
1955 1955 filematcher = None
1956 1956 if opts.get('patch') or opts.get('stat'):
1957 1957 # When following files, track renames via a special matcher.
1958 1958 # If we're forced to take the slowpath it means we're following
1959 1959 # at least one pattern/directory, so don't bother with rename tracking.
1960 1960 if follow and not match.always() and not slowpath:
1961 1961 # _makefollowlogfilematcher expects its files argument to be
1962 1962 # relative to the repo root, so use match.files(), not pats.
1963 1963 filematcher = _makefollowlogfilematcher(repo, match.files(),
1964 1964 followfirst)
1965 1965 else:
1966 1966 filematcher = _makenofollowlogfilematcher(repo, pats, opts)
1967 1967 if filematcher is None:
1968 1968 filematcher = lambda rev: match
1969 1969
1970 1970 expr = []
1971 1971 for op, val in sorted(opts.iteritems()):
1972 1972 if not val:
1973 1973 continue
1974 1974 if op not in opt2revset:
1975 1975 continue
1976 1976 revop, andor = opt2revset[op]
1977 1977 if '%(val)' not in revop:
1978 1978 expr.append(revop)
1979 1979 else:
1980 1980 if not isinstance(val, list):
1981 1981 e = revop % {'val': val}
1982 1982 else:
1983 1983 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
1984 1984 expr.append(e)
1985 1985
1986 1986 if expr:
1987 1987 expr = '(' + ' and '.join(expr) + ')'
1988 1988 else:
1989 1989 expr = None
1990 1990 return expr, filematcher
1991 1991
1992 1992 def _logrevs(repo, opts):
1993 1993 # Default --rev value depends on --follow but --follow behaviour
1994 1994 # depends on revisions resolved from --rev...
1995 1995 follow = opts.get('follow') or opts.get('follow_first')
1996 1996 if opts.get('rev'):
1997 1997 revs = scmutil.revrange(repo, opts['rev'])
1998 1998 elif follow and repo.dirstate.p1() == nullid:
1999 1999 revs = revset.baseset()
2000 2000 elif follow:
2001 2001 revs = repo.revs('reverse(:.)')
2002 2002 else:
2003 2003 revs = revset.spanset(repo)
2004 2004 revs.reverse()
2005 2005 return revs
2006 2006
2007 2007 def getgraphlogrevs(repo, pats, opts):
2008 2008 """Return (revs, expr, filematcher) where revs is an iterable of
2009 2009 revision numbers, expr is a revset string built from log options
2010 2010 and file patterns or None, and used to filter 'revs'. If --stat or
2011 2011 --patch are not passed filematcher is None. Otherwise it is a
2012 2012 callable taking a revision number and returning a match objects
2013 2013 filtering the files to be detailed when displaying the revision.
2014 2014 """
2015 2015 limit = loglimit(opts)
2016 2016 revs = _logrevs(repo, opts)
2017 2017 if not revs:
2018 2018 return revset.baseset(), None, None
2019 2019 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2020 2020 if opts.get('rev'):
2021 2021 # User-specified revs might be unsorted, but don't sort before
2022 2022 # _makelogrevset because it might depend on the order of revs
2023 2023 revs.sort(reverse=True)
2024 2024 if expr:
2025 2025 # Revset matchers often operate faster on revisions in changelog
2026 2026 # order, because most filters deal with the changelog.
2027 2027 revs.reverse()
2028 2028 matcher = revset.match(repo.ui, expr)
2029 2029 # Revset matches can reorder revisions. "A or B" typically returns
2030 2030 # returns the revision matching A then the revision matching B. Sort
2031 2031 # again to fix that.
2032 2032 revs = matcher(repo, revs)
2033 2033 revs.sort(reverse=True)
2034 2034 if limit is not None:
2035 2035 limitedrevs = []
2036 2036 for idx, rev in enumerate(revs):
2037 2037 if idx >= limit:
2038 2038 break
2039 2039 limitedrevs.append(rev)
2040 2040 revs = revset.baseset(limitedrevs)
2041 2041
2042 2042 return revs, expr, filematcher
2043 2043
2044 2044 def getlogrevs(repo, pats, opts):
2045 2045 """Return (revs, expr, filematcher) where revs is an iterable of
2046 2046 revision numbers, expr is a revset string built from log options
2047 2047 and file patterns or None, and used to filter 'revs'. If --stat or
2048 2048 --patch are not passed filematcher is None. Otherwise it is a
2049 2049 callable taking a revision number and returning a match objects
2050 2050 filtering the files to be detailed when displaying the revision.
2051 2051 """
2052 2052 limit = loglimit(opts)
2053 2053 revs = _logrevs(repo, opts)
2054 2054 if not revs:
2055 2055 return revset.baseset([]), None, None
2056 2056 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2057 2057 if expr:
2058 2058 # Revset matchers often operate faster on revisions in changelog
2059 2059 # order, because most filters deal with the changelog.
2060 2060 if not opts.get('rev'):
2061 2061 revs.reverse()
2062 2062 matcher = revset.match(repo.ui, expr)
2063 2063 # Revset matches can reorder revisions. "A or B" typically returns
2064 2064 # returns the revision matching A then the revision matching B. Sort
2065 2065 # again to fix that.
2066 2066 revs = matcher(repo, revs)
2067 2067 if not opts.get('rev'):
2068 2068 revs.sort(reverse=True)
2069 2069 if limit is not None:
2070 2070 count = 0
2071 2071 limitedrevs = []
2072 2072 it = iter(revs)
2073 2073 while count < limit:
2074 2074 try:
2075 2075 limitedrevs.append(it.next())
2076 2076 except (StopIteration):
2077 2077 break
2078 2078 count += 1
2079 2079 revs = revset.baseset(limitedrevs)
2080 2080
2081 2081 return revs, expr, filematcher
2082 2082
2083 2083 def displaygraph(ui, dag, displayer, showparents, edgefn, getrenamed=None,
2084 2084 filematcher=None):
2085 2085 seen, state = [], graphmod.asciistate()
2086 2086 for rev, type, ctx, parents in dag:
2087 2087 char = 'o'
2088 2088 if ctx.node() in showparents:
2089 2089 char = '@'
2090 2090 elif ctx.obsolete():
2091 2091 char = 'x'
2092 2092 elif ctx.closesbranch():
2093 2093 char = '_'
2094 2094 copies = None
2095 2095 if getrenamed and ctx.rev():
2096 2096 copies = []
2097 2097 for fn in ctx.files():
2098 2098 rename = getrenamed(fn, ctx.rev())
2099 2099 if rename:
2100 2100 copies.append((fn, rename[0]))
2101 2101 revmatchfn = None
2102 2102 if filematcher is not None:
2103 2103 revmatchfn = filematcher(ctx.rev())
2104 2104 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2105 2105 lines = displayer.hunk.pop(rev).split('\n')
2106 2106 if not lines[-1]:
2107 2107 del lines[-1]
2108 2108 displayer.flush(rev)
2109 2109 edges = edgefn(type, char, lines, seen, rev, parents)
2110 2110 for type, char, lines, coldata in edges:
2111 2111 graphmod.ascii(ui, state, type, char, lines, coldata)
2112 2112 displayer.close()
2113 2113
2114 2114 def graphlog(ui, repo, *pats, **opts):
2115 2115 # Parameters are identical to log command ones
2116 2116 revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
2117 2117 revdag = graphmod.dagwalker(repo, revs)
2118 2118
2119 2119 getrenamed = None
2120 2120 if opts.get('copies'):
2121 2121 endrev = None
2122 2122 if opts.get('rev'):
2123 2123 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
2124 2124 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2125 2125 displayer = show_changeset(ui, repo, opts, buffered=True)
2126 2126 showparents = [ctx.node() for ctx in repo[None].parents()]
2127 2127 displaygraph(ui, revdag, displayer, showparents,
2128 2128 graphmod.asciiedges, getrenamed, filematcher)
2129 2129
2130 2130 def checkunsupportedgraphflags(pats, opts):
2131 2131 for op in ["newest_first"]:
2132 2132 if op in opts and opts[op]:
2133 2133 raise util.Abort(_("-G/--graph option is incompatible with --%s")
2134 2134 % op.replace("_", "-"))
2135 2135
2136 2136 def graphrevs(repo, nodes, opts):
2137 2137 limit = loglimit(opts)
2138 2138 nodes.reverse()
2139 2139 if limit is not None:
2140 2140 nodes = nodes[:limit]
2141 2141 return graphmod.nodes(repo, nodes)
2142 2142
2143 2143 def add(ui, repo, match, prefix, explicitonly, **opts):
2144 2144 join = lambda f: os.path.join(prefix, f)
2145 2145 bad = []
2146 2146 oldbad = match.bad
2147 2147 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
2148 2148 names = []
2149 2149 wctx = repo[None]
2150 2150 cca = None
2151 2151 abort, warn = scmutil.checkportabilityalert(ui)
2152 2152 if abort or warn:
2153 2153 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2154 2154 for f in wctx.walk(match):
2155 2155 exact = match.exact(f)
2156 2156 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2157 2157 if cca:
2158 2158 cca(f)
2159 2159 names.append(f)
2160 2160 if ui.verbose or not exact:
2161 2161 ui.status(_('adding %s\n') % match.rel(f))
2162 2162
2163 2163 for subpath in sorted(wctx.substate):
2164 2164 sub = wctx.sub(subpath)
2165 2165 try:
2166 2166 submatch = matchmod.narrowmatcher(subpath, match)
2167 2167 if opts.get('subrepos'):
2168 2168 bad.extend(sub.add(ui, submatch, prefix, False, **opts))
2169 2169 else:
2170 2170 bad.extend(sub.add(ui, submatch, prefix, True, **opts))
2171 2171 except error.LookupError:
2172 2172 ui.status(_("skipping missing subrepository: %s\n")
2173 2173 % join(subpath))
2174 2174
2175 2175 if not opts.get('dry_run'):
2176 2176 rejected = wctx.add(names, prefix)
2177 2177 bad.extend(f for f in rejected if f in match.files())
2178 2178 return bad
2179 2179
2180 2180 def forget(ui, repo, match, prefix, explicitonly):
2181 2181 join = lambda f: os.path.join(prefix, f)
2182 2182 bad = []
2183 2183 oldbad = match.bad
2184 2184 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
2185 2185 wctx = repo[None]
2186 2186 forgot = []
2187 2187 s = repo.status(match=match, clean=True)
2188 2188 forget = sorted(s[0] + s[1] + s[3] + s[6])
2189 2189 if explicitonly:
2190 2190 forget = [f for f in forget if match.exact(f)]
2191 2191
2192 2192 for subpath in sorted(wctx.substate):
2193 2193 sub = wctx.sub(subpath)
2194 2194 try:
2195 2195 submatch = matchmod.narrowmatcher(subpath, match)
2196 2196 subbad, subforgot = sub.forget(submatch, prefix)
2197 2197 bad.extend([subpath + '/' + f for f in subbad])
2198 2198 forgot.extend([subpath + '/' + f for f in subforgot])
2199 2199 except error.LookupError:
2200 2200 ui.status(_("skipping missing subrepository: %s\n")
2201 2201 % join(subpath))
2202 2202
2203 2203 if not explicitonly:
2204 2204 for f in match.files():
2205 2205 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2206 2206 if f not in forgot:
2207 2207 if repo.wvfs.exists(f):
2208 2208 ui.warn(_('not removing %s: '
2209 2209 'file is already untracked\n')
2210 2210 % match.rel(f))
2211 2211 bad.append(f)
2212 2212
2213 2213 for f in forget:
2214 2214 if ui.verbose or not match.exact(f):
2215 2215 ui.status(_('removing %s\n') % match.rel(f))
2216 2216
2217 2217 rejected = wctx.forget(forget, prefix)
2218 2218 bad.extend(f for f in rejected if f in match.files())
2219 2219 forgot.extend(f for f in forget if f not in rejected)
2220 2220 return bad, forgot
2221 2221
2222 def files(ui, ctx, m, fm, fmt):
2223 rev = ctx.rev()
2224 ret = 1
2225 ds = ctx._repo.dirstate
2226
2227 for f in ctx.matches(m):
2228 if rev is None and ds[f] == 'r':
2229 continue
2230 fm.startitem()
2231 if ui.verbose:
2232 fc = ctx[f]
2233 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags())
2234 fm.data(abspath=f)
2235 fm.write('path', fmt, m.rel(f))
2236 ret = 0
2237
2238 return ret
2239
2222 2240 def remove(ui, repo, m, prefix, after, force, subrepos):
2223 2241 join = lambda f: os.path.join(prefix, f)
2224 2242 ret = 0
2225 2243 s = repo.status(match=m, clean=True)
2226 2244 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2227 2245
2228 2246 wctx = repo[None]
2229 2247
2230 2248 for subpath in sorted(wctx.substate):
2231 2249 def matchessubrepo(matcher, subpath):
2232 2250 if matcher.exact(subpath):
2233 2251 return True
2234 2252 for f in matcher.files():
2235 2253 if f.startswith(subpath):
2236 2254 return True
2237 2255 return False
2238 2256
2239 2257 if subrepos or matchessubrepo(m, subpath):
2240 2258 sub = wctx.sub(subpath)
2241 2259 try:
2242 2260 submatch = matchmod.narrowmatcher(subpath, m)
2243 2261 if sub.removefiles(submatch, prefix, after, force, subrepos):
2244 2262 ret = 1
2245 2263 except error.LookupError:
2246 2264 ui.status(_("skipping missing subrepository: %s\n")
2247 2265 % join(subpath))
2248 2266
2249 2267 # warn about failure to delete explicit files/dirs
2250 2268 deleteddirs = scmutil.dirs(deleted)
2251 2269 for f in m.files():
2252 2270 def insubrepo():
2253 2271 for subpath in wctx.substate:
2254 2272 if f.startswith(subpath):
2255 2273 return True
2256 2274 return False
2257 2275
2258 2276 isdir = f in deleteddirs or f in wctx.dirs()
2259 2277 if f in repo.dirstate or isdir or f == '.' or insubrepo():
2260 2278 continue
2261 2279
2262 2280 if repo.wvfs.exists(f):
2263 2281 if repo.wvfs.isdir(f):
2264 2282 ui.warn(_('not removing %s: no tracked files\n')
2265 2283 % m.rel(f))
2266 2284 else:
2267 2285 ui.warn(_('not removing %s: file is untracked\n')
2268 2286 % m.rel(f))
2269 2287 # missing files will generate a warning elsewhere
2270 2288 ret = 1
2271 2289
2272 2290 if force:
2273 2291 list = modified + deleted + clean + added
2274 2292 elif after:
2275 2293 list = deleted
2276 2294 for f in modified + added + clean:
2277 2295 ui.warn(_('not removing %s: file still exists\n') % m.rel(f))
2278 2296 ret = 1
2279 2297 else:
2280 2298 list = deleted + clean
2281 2299 for f in modified:
2282 2300 ui.warn(_('not removing %s: file is modified (use -f'
2283 2301 ' to force removal)\n') % m.rel(f))
2284 2302 ret = 1
2285 2303 for f in added:
2286 2304 ui.warn(_('not removing %s: file has been marked for add'
2287 2305 ' (use forget to undo)\n') % m.rel(f))
2288 2306 ret = 1
2289 2307
2290 2308 for f in sorted(list):
2291 2309 if ui.verbose or not m.exact(f):
2292 2310 ui.status(_('removing %s\n') % m.rel(f))
2293 2311
2294 2312 wlock = repo.wlock()
2295 2313 try:
2296 2314 if not after:
2297 2315 for f in list:
2298 2316 if f in added:
2299 2317 continue # we never unlink added files on remove
2300 2318 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
2301 2319 repo[None].forget(list)
2302 2320 finally:
2303 2321 wlock.release()
2304 2322
2305 2323 return ret
2306 2324
2307 2325 def cat(ui, repo, ctx, matcher, prefix, **opts):
2308 2326 err = 1
2309 2327
2310 2328 def write(path):
2311 2329 fp = makefileobj(repo, opts.get('output'), ctx.node(),
2312 2330 pathname=os.path.join(prefix, path))
2313 2331 data = ctx[path].data()
2314 2332 if opts.get('decode'):
2315 2333 data = repo.wwritedata(path, data)
2316 2334 fp.write(data)
2317 2335 fp.close()
2318 2336
2319 2337 # Automation often uses hg cat on single files, so special case it
2320 2338 # for performance to avoid the cost of parsing the manifest.
2321 2339 if len(matcher.files()) == 1 and not matcher.anypats():
2322 2340 file = matcher.files()[0]
2323 2341 mf = repo.manifest
2324 2342 mfnode = ctx._changeset[0]
2325 2343 if mf.find(mfnode, file)[0]:
2326 2344 write(file)
2327 2345 return 0
2328 2346
2329 2347 # Don't warn about "missing" files that are really in subrepos
2330 2348 bad = matcher.bad
2331 2349
2332 2350 def badfn(path, msg):
2333 2351 for subpath in ctx.substate:
2334 2352 if path.startswith(subpath):
2335 2353 return
2336 2354 bad(path, msg)
2337 2355
2338 2356 matcher.bad = badfn
2339 2357
2340 2358 for abs in ctx.walk(matcher):
2341 2359 write(abs)
2342 2360 err = 0
2343 2361
2344 2362 matcher.bad = bad
2345 2363
2346 2364 for subpath in sorted(ctx.substate):
2347 2365 sub = ctx.sub(subpath)
2348 2366 try:
2349 2367 submatch = matchmod.narrowmatcher(subpath, matcher)
2350 2368
2351 2369 if not sub.cat(submatch, os.path.join(prefix, sub._path),
2352 2370 **opts):
2353 2371 err = 0
2354 2372 except error.RepoLookupError:
2355 2373 ui.status(_("skipping missing subrepository: %s\n")
2356 2374 % os.path.join(prefix, subpath))
2357 2375
2358 2376 return err
2359 2377
2360 2378 def commit(ui, repo, commitfunc, pats, opts):
2361 2379 '''commit the specified files or all outstanding changes'''
2362 2380 date = opts.get('date')
2363 2381 if date:
2364 2382 opts['date'] = util.parsedate(date)
2365 2383 message = logmessage(ui, opts)
2366 2384 matcher = scmutil.match(repo[None], pats, opts)
2367 2385
2368 2386 # extract addremove carefully -- this function can be called from a command
2369 2387 # that doesn't support addremove
2370 2388 if opts.get('addremove'):
2371 2389 if scmutil.addremove(repo, matcher, "", opts) != 0:
2372 2390 raise util.Abort(
2373 2391 _("failed to mark all new/missing files as added/removed"))
2374 2392
2375 2393 return commitfunc(ui, repo, message, matcher, opts)
2376 2394
2377 2395 def amend(ui, repo, commitfunc, old, extra, pats, opts):
2378 2396 # amend will reuse the existing user if not specified, but the obsolete
2379 2397 # marker creation requires that the current user's name is specified.
2380 2398 if obsolete._enabled:
2381 2399 ui.username() # raise exception if username not set
2382 2400
2383 2401 ui.note(_('amending changeset %s\n') % old)
2384 2402 base = old.p1()
2385 2403
2386 2404 wlock = lock = newid = None
2387 2405 try:
2388 2406 wlock = repo.wlock()
2389 2407 lock = repo.lock()
2390 2408 tr = repo.transaction('amend')
2391 2409 try:
2392 2410 # See if we got a message from -m or -l, if not, open the editor
2393 2411 # with the message of the changeset to amend
2394 2412 message = logmessage(ui, opts)
2395 2413 # ensure logfile does not conflict with later enforcement of the
2396 2414 # message. potential logfile content has been processed by
2397 2415 # `logmessage` anyway.
2398 2416 opts.pop('logfile')
2399 2417 # First, do a regular commit to record all changes in the working
2400 2418 # directory (if there are any)
2401 2419 ui.callhooks = False
2402 2420 currentbookmark = repo._bookmarkcurrent
2403 2421 try:
2404 2422 repo._bookmarkcurrent = None
2405 2423 opts['message'] = 'temporary amend commit for %s' % old
2406 2424 node = commit(ui, repo, commitfunc, pats, opts)
2407 2425 finally:
2408 2426 repo._bookmarkcurrent = currentbookmark
2409 2427 ui.callhooks = True
2410 2428 ctx = repo[node]
2411 2429
2412 2430 # Participating changesets:
2413 2431 #
2414 2432 # node/ctx o - new (intermediate) commit that contains changes
2415 2433 # | from working dir to go into amending commit
2416 2434 # | (or a workingctx if there were no changes)
2417 2435 # |
2418 2436 # old o - changeset to amend
2419 2437 # |
2420 2438 # base o - parent of amending changeset
2421 2439
2422 2440 # Update extra dict from amended commit (e.g. to preserve graft
2423 2441 # source)
2424 2442 extra.update(old.extra())
2425 2443
2426 2444 # Also update it from the intermediate commit or from the wctx
2427 2445 extra.update(ctx.extra())
2428 2446
2429 2447 if len(old.parents()) > 1:
2430 2448 # ctx.files() isn't reliable for merges, so fall back to the
2431 2449 # slower repo.status() method
2432 2450 files = set([fn for st in repo.status(base, old)[:3]
2433 2451 for fn in st])
2434 2452 else:
2435 2453 files = set(old.files())
2436 2454
2437 2455 # Second, we use either the commit we just did, or if there were no
2438 2456 # changes the parent of the working directory as the version of the
2439 2457 # files in the final amend commit
2440 2458 if node:
2441 2459 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
2442 2460
2443 2461 user = ctx.user()
2444 2462 date = ctx.date()
2445 2463 # Recompute copies (avoid recording a -> b -> a)
2446 2464 copied = copies.pathcopies(base, ctx)
2447 2465 if old.p2:
2448 2466 copied.update(copies.pathcopies(old.p2(), ctx))
2449 2467
2450 2468 # Prune files which were reverted by the updates: if old
2451 2469 # introduced file X and our intermediate commit, node,
2452 2470 # renamed that file, then those two files are the same and
2453 2471 # we can discard X from our list of files. Likewise if X
2454 2472 # was deleted, it's no longer relevant
2455 2473 files.update(ctx.files())
2456 2474
2457 2475 def samefile(f):
2458 2476 if f in ctx.manifest():
2459 2477 a = ctx.filectx(f)
2460 2478 if f in base.manifest():
2461 2479 b = base.filectx(f)
2462 2480 return (not a.cmp(b)
2463 2481 and a.flags() == b.flags())
2464 2482 else:
2465 2483 return False
2466 2484 else:
2467 2485 return f not in base.manifest()
2468 2486 files = [f for f in files if not samefile(f)]
2469 2487
2470 2488 def filectxfn(repo, ctx_, path):
2471 2489 try:
2472 2490 fctx = ctx[path]
2473 2491 flags = fctx.flags()
2474 2492 mctx = context.memfilectx(repo,
2475 2493 fctx.path(), fctx.data(),
2476 2494 islink='l' in flags,
2477 2495 isexec='x' in flags,
2478 2496 copied=copied.get(path))
2479 2497 return mctx
2480 2498 except KeyError:
2481 2499 return None
2482 2500 else:
2483 2501 ui.note(_('copying changeset %s to %s\n') % (old, base))
2484 2502
2485 2503 # Use version of files as in the old cset
2486 2504 def filectxfn(repo, ctx_, path):
2487 2505 try:
2488 2506 return old.filectx(path)
2489 2507 except KeyError:
2490 2508 return None
2491 2509
2492 2510 user = opts.get('user') or old.user()
2493 2511 date = opts.get('date') or old.date()
2494 2512 editform = mergeeditform(old, 'commit.amend')
2495 2513 editor = getcommiteditor(editform=editform, **opts)
2496 2514 if not message:
2497 2515 editor = getcommiteditor(edit=True, editform=editform)
2498 2516 message = old.description()
2499 2517
2500 2518 pureextra = extra.copy()
2501 2519 extra['amend_source'] = old.hex()
2502 2520
2503 2521 new = context.memctx(repo,
2504 2522 parents=[base.node(), old.p2().node()],
2505 2523 text=message,
2506 2524 files=files,
2507 2525 filectxfn=filectxfn,
2508 2526 user=user,
2509 2527 date=date,
2510 2528 extra=extra,
2511 2529 editor=editor)
2512 2530
2513 2531 newdesc = changelog.stripdesc(new.description())
2514 2532 if ((not node)
2515 2533 and newdesc == old.description()
2516 2534 and user == old.user()
2517 2535 and date == old.date()
2518 2536 and pureextra == old.extra()):
2519 2537 # nothing changed. continuing here would create a new node
2520 2538 # anyway because of the amend_source noise.
2521 2539 #
2522 2540 # This not what we expect from amend.
2523 2541 return old.node()
2524 2542
2525 2543 ph = repo.ui.config('phases', 'new-commit', phases.draft)
2526 2544 try:
2527 2545 if opts.get('secret'):
2528 2546 commitphase = 'secret'
2529 2547 else:
2530 2548 commitphase = old.phase()
2531 2549 repo.ui.setconfig('phases', 'new-commit', commitphase, 'amend')
2532 2550 newid = repo.commitctx(new)
2533 2551 finally:
2534 2552 repo.ui.setconfig('phases', 'new-commit', ph, 'amend')
2535 2553 if newid != old.node():
2536 2554 # Reroute the working copy parent to the new changeset
2537 2555 repo.setparents(newid, nullid)
2538 2556
2539 2557 # Move bookmarks from old parent to amend commit
2540 2558 bms = repo.nodebookmarks(old.node())
2541 2559 if bms:
2542 2560 marks = repo._bookmarks
2543 2561 for bm in bms:
2544 2562 marks[bm] = newid
2545 2563 marks.write()
2546 2564 #commit the whole amend process
2547 2565 createmarkers = obsolete.isenabled(repo, obsolete.createmarkersopt)
2548 2566 if createmarkers and newid != old.node():
2549 2567 # mark the new changeset as successor of the rewritten one
2550 2568 new = repo[newid]
2551 2569 obs = [(old, (new,))]
2552 2570 if node:
2553 2571 obs.append((ctx, ()))
2554 2572
2555 2573 obsolete.createmarkers(repo, obs)
2556 2574 tr.close()
2557 2575 finally:
2558 2576 tr.release()
2559 2577 if not createmarkers and newid != old.node():
2560 2578 # Strip the intermediate commit (if there was one) and the amended
2561 2579 # commit
2562 2580 if node:
2563 2581 ui.note(_('stripping intermediate changeset %s\n') % ctx)
2564 2582 ui.note(_('stripping amended changeset %s\n') % old)
2565 2583 repair.strip(ui, repo, old.node(), topic='amend-backup')
2566 2584 finally:
2567 2585 if newid is None:
2568 2586 repo.dirstate.invalidate()
2569 2587 lockmod.release(lock, wlock)
2570 2588 return newid
2571 2589
2572 2590 def commiteditor(repo, ctx, subs, editform=''):
2573 2591 if ctx.description():
2574 2592 return ctx.description()
2575 2593 return commitforceeditor(repo, ctx, subs, editform=editform)
2576 2594
2577 2595 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
2578 2596 editform=''):
2579 2597 if not extramsg:
2580 2598 extramsg = _("Leave message empty to abort commit.")
2581 2599
2582 2600 forms = [e for e in editform.split('.') if e]
2583 2601 forms.insert(0, 'changeset')
2584 2602 while forms:
2585 2603 tmpl = repo.ui.config('committemplate', '.'.join(forms))
2586 2604 if tmpl:
2587 2605 committext = buildcommittemplate(repo, ctx, subs, extramsg, tmpl)
2588 2606 break
2589 2607 forms.pop()
2590 2608 else:
2591 2609 committext = buildcommittext(repo, ctx, subs, extramsg)
2592 2610
2593 2611 # run editor in the repository root
2594 2612 olddir = os.getcwd()
2595 2613 os.chdir(repo.root)
2596 2614 text = repo.ui.edit(committext, ctx.user(), ctx.extra(), editform=editform)
2597 2615 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
2598 2616 os.chdir(olddir)
2599 2617
2600 2618 if finishdesc:
2601 2619 text = finishdesc(text)
2602 2620 if not text.strip():
2603 2621 raise util.Abort(_("empty commit message"))
2604 2622
2605 2623 return text
2606 2624
2607 2625 def buildcommittemplate(repo, ctx, subs, extramsg, tmpl):
2608 2626 ui = repo.ui
2609 2627 tmpl, mapfile = gettemplate(ui, tmpl, None)
2610 2628
2611 2629 try:
2612 2630 t = changeset_templater(ui, repo, None, {}, tmpl, mapfile, False)
2613 2631 except SyntaxError, inst:
2614 2632 raise util.Abort(inst.args[0])
2615 2633
2616 2634 for k, v in repo.ui.configitems('committemplate'):
2617 2635 if k != 'changeset':
2618 2636 t.t.cache[k] = v
2619 2637
2620 2638 if not extramsg:
2621 2639 extramsg = '' # ensure that extramsg is string
2622 2640
2623 2641 ui.pushbuffer()
2624 2642 t.show(ctx, extramsg=extramsg)
2625 2643 return ui.popbuffer()
2626 2644
2627 2645 def buildcommittext(repo, ctx, subs, extramsg):
2628 2646 edittext = []
2629 2647 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
2630 2648 if ctx.description():
2631 2649 edittext.append(ctx.description())
2632 2650 edittext.append("")
2633 2651 edittext.append("") # Empty line between message and comments.
2634 2652 edittext.append(_("HG: Enter commit message."
2635 2653 " Lines beginning with 'HG:' are removed."))
2636 2654 edittext.append("HG: %s" % extramsg)
2637 2655 edittext.append("HG: --")
2638 2656 edittext.append(_("HG: user: %s") % ctx.user())
2639 2657 if ctx.p2():
2640 2658 edittext.append(_("HG: branch merge"))
2641 2659 if ctx.branch():
2642 2660 edittext.append(_("HG: branch '%s'") % ctx.branch())
2643 2661 if bookmarks.iscurrent(repo):
2644 2662 edittext.append(_("HG: bookmark '%s'") % repo._bookmarkcurrent)
2645 2663 edittext.extend([_("HG: subrepo %s") % s for s in subs])
2646 2664 edittext.extend([_("HG: added %s") % f for f in added])
2647 2665 edittext.extend([_("HG: changed %s") % f for f in modified])
2648 2666 edittext.extend([_("HG: removed %s") % f for f in removed])
2649 2667 if not added and not modified and not removed:
2650 2668 edittext.append(_("HG: no files changed"))
2651 2669 edittext.append("")
2652 2670
2653 2671 return "\n".join(edittext)
2654 2672
2655 2673 def commitstatus(repo, node, branch, bheads=None, opts={}):
2656 2674 ctx = repo[node]
2657 2675 parents = ctx.parents()
2658 2676
2659 2677 if (not opts.get('amend') and bheads and node not in bheads and not
2660 2678 [x for x in parents if x.node() in bheads and x.branch() == branch]):
2661 2679 repo.ui.status(_('created new head\n'))
2662 2680 # The message is not printed for initial roots. For the other
2663 2681 # changesets, it is printed in the following situations:
2664 2682 #
2665 2683 # Par column: for the 2 parents with ...
2666 2684 # N: null or no parent
2667 2685 # B: parent is on another named branch
2668 2686 # C: parent is a regular non head changeset
2669 2687 # H: parent was a branch head of the current branch
2670 2688 # Msg column: whether we print "created new head" message
2671 2689 # In the following, it is assumed that there already exists some
2672 2690 # initial branch heads of the current branch, otherwise nothing is
2673 2691 # printed anyway.
2674 2692 #
2675 2693 # Par Msg Comment
2676 2694 # N N y additional topo root
2677 2695 #
2678 2696 # B N y additional branch root
2679 2697 # C N y additional topo head
2680 2698 # H N n usual case
2681 2699 #
2682 2700 # B B y weird additional branch root
2683 2701 # C B y branch merge
2684 2702 # H B n merge with named branch
2685 2703 #
2686 2704 # C C y additional head from merge
2687 2705 # C H n merge with a head
2688 2706 #
2689 2707 # H H n head merge: head count decreases
2690 2708
2691 2709 if not opts.get('close_branch'):
2692 2710 for r in parents:
2693 2711 if r.closesbranch() and r.branch() == branch:
2694 2712 repo.ui.status(_('reopening closed branch head %d\n') % r)
2695 2713
2696 2714 if repo.ui.debugflag:
2697 2715 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
2698 2716 elif repo.ui.verbose:
2699 2717 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
2700 2718
2701 2719 def revert(ui, repo, ctx, parents, *pats, **opts):
2702 2720 parent, p2 = parents
2703 2721 node = ctx.node()
2704 2722
2705 2723 mf = ctx.manifest()
2706 2724 if node == p2:
2707 2725 parent = p2
2708 2726 if node == parent:
2709 2727 pmf = mf
2710 2728 else:
2711 2729 pmf = None
2712 2730
2713 2731 # need all matching names in dirstate and manifest of target rev,
2714 2732 # so have to walk both. do not print errors if files exist in one
2715 2733 # but not other.
2716 2734
2717 2735 # `names` is a mapping for all elements in working copy and target revision
2718 2736 # The mapping is in the form:
2719 2737 # <asb path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
2720 2738 names = {}
2721 2739
2722 2740 wlock = repo.wlock()
2723 2741 try:
2724 2742 ## filling of the `names` mapping
2725 2743 # walk dirstate to fill `names`
2726 2744
2727 2745 m = scmutil.match(repo[None], pats, opts)
2728 2746 if not m.always() or node != parent:
2729 2747 m.bad = lambda x, y: False
2730 2748 for abs in repo.walk(m):
2731 2749 names[abs] = m.rel(abs), m.exact(abs)
2732 2750
2733 2751 # walk target manifest to fill `names`
2734 2752
2735 2753 def badfn(path, msg):
2736 2754 if path in names:
2737 2755 return
2738 2756 if path in ctx.substate:
2739 2757 return
2740 2758 path_ = path + '/'
2741 2759 for f in names:
2742 2760 if f.startswith(path_):
2743 2761 return
2744 2762 ui.warn("%s: %s\n" % (m.rel(path), msg))
2745 2763
2746 2764 m = scmutil.match(ctx, pats, opts)
2747 2765 m.bad = badfn
2748 2766 for abs in ctx.walk(m):
2749 2767 if abs not in names:
2750 2768 names[abs] = m.rel(abs), m.exact(abs)
2751 2769
2752 2770 # Find status of all file in `names`.
2753 2771 m = scmutil.matchfiles(repo, names)
2754 2772
2755 2773 changes = repo.status(node1=node, match=m,
2756 2774 unknown=True, ignored=True, clean=True)
2757 2775 else:
2758 2776 changes = repo.status(match=m)
2759 2777 for kind in changes:
2760 2778 for abs in kind:
2761 2779 names[abs] = m.rel(abs), m.exact(abs)
2762 2780
2763 2781 m = scmutil.matchfiles(repo, names)
2764 2782
2765 2783 modified = set(changes.modified)
2766 2784 added = set(changes.added)
2767 2785 removed = set(changes.removed)
2768 2786 _deleted = set(changes.deleted)
2769 2787 unknown = set(changes.unknown)
2770 2788 unknown.update(changes.ignored)
2771 2789 clean = set(changes.clean)
2772 2790 modadded = set()
2773 2791
2774 2792 # split between files known in target manifest and the others
2775 2793 smf = set(mf)
2776 2794
2777 2795 # determine the exact nature of the deleted changesets
2778 2796 deladded = _deleted - smf
2779 2797 deleted = _deleted - deladded
2780 2798
2781 2799 # We need to account for the state of the file in the dirstate,
2782 2800 # even when we revert against something else than parent. This will
2783 2801 # slightly alter the behavior of revert (doing back up or not, delete
2784 2802 # or just forget etc).
2785 2803 if parent == node:
2786 2804 dsmodified = modified
2787 2805 dsadded = added
2788 2806 dsremoved = removed
2789 2807 # store all local modifications, useful later for rename detection
2790 2808 localchanges = dsmodified | dsadded
2791 2809 modified, added, removed = set(), set(), set()
2792 2810 else:
2793 2811 changes = repo.status(node1=parent, match=m)
2794 2812 dsmodified = set(changes.modified)
2795 2813 dsadded = set(changes.added)
2796 2814 dsremoved = set(changes.removed)
2797 2815 # store all local modifications, useful later for rename detection
2798 2816 localchanges = dsmodified | dsadded
2799 2817
2800 2818 # only take into account for removes between wc and target
2801 2819 clean |= dsremoved - removed
2802 2820 dsremoved &= removed
2803 2821 # distinct between dirstate remove and other
2804 2822 removed -= dsremoved
2805 2823
2806 2824 modadded = added & dsmodified
2807 2825 added -= modadded
2808 2826
2809 2827 # tell newly modified apart.
2810 2828 dsmodified &= modified
2811 2829 dsmodified |= modified & dsadded # dirstate added may needs backup
2812 2830 modified -= dsmodified
2813 2831
2814 2832 # We need to wait for some post-processing to update this set
2815 2833 # before making the distinction. The dirstate will be used for
2816 2834 # that purpose.
2817 2835 dsadded = added
2818 2836
2819 2837 # in case of merge, files that are actually added can be reported as
2820 2838 # modified, we need to post process the result
2821 2839 if p2 != nullid:
2822 2840 if pmf is None:
2823 2841 # only need parent manifest in the merge case,
2824 2842 # so do not read by default
2825 2843 pmf = repo[parent].manifest()
2826 2844 mergeadd = dsmodified - set(pmf)
2827 2845 dsadded |= mergeadd
2828 2846 dsmodified -= mergeadd
2829 2847
2830 2848 # if f is a rename, update `names` to also revert the source
2831 2849 cwd = repo.getcwd()
2832 2850 for f in localchanges:
2833 2851 src = repo.dirstate.copied(f)
2834 2852 # XXX should we check for rename down to target node?
2835 2853 if src and src not in names and repo.dirstate[src] == 'r':
2836 2854 dsremoved.add(src)
2837 2855 names[src] = (repo.pathto(src, cwd), True)
2838 2856
2839 2857 # distinguish between file to forget and the other
2840 2858 added = set()
2841 2859 for abs in dsadded:
2842 2860 if repo.dirstate[abs] != 'a':
2843 2861 added.add(abs)
2844 2862 dsadded -= added
2845 2863
2846 2864 for abs in deladded:
2847 2865 if repo.dirstate[abs] == 'a':
2848 2866 dsadded.add(abs)
2849 2867 deladded -= dsadded
2850 2868
2851 2869 # For files marked as removed, we check if an unknown file is present at
2852 2870 # the same path. If a such file exists it may need to be backed up.
2853 2871 # Making the distinction at this stage helps have simpler backup
2854 2872 # logic.
2855 2873 removunk = set()
2856 2874 for abs in removed:
2857 2875 target = repo.wjoin(abs)
2858 2876 if os.path.lexists(target):
2859 2877 removunk.add(abs)
2860 2878 removed -= removunk
2861 2879
2862 2880 dsremovunk = set()
2863 2881 for abs in dsremoved:
2864 2882 target = repo.wjoin(abs)
2865 2883 if os.path.lexists(target):
2866 2884 dsremovunk.add(abs)
2867 2885 dsremoved -= dsremovunk
2868 2886
2869 2887 # action to be actually performed by revert
2870 2888 # (<list of file>, message>) tuple
2871 2889 actions = {'revert': ([], _('reverting %s\n')),
2872 2890 'add': ([], _('adding %s\n')),
2873 2891 'remove': ([], _('removing %s\n')),
2874 2892 'drop': ([], _('removing %s\n')),
2875 2893 'forget': ([], _('forgetting %s\n')),
2876 2894 'undelete': ([], _('undeleting %s\n')),
2877 2895 'noop': (None, _('no changes needed to %s\n')),
2878 2896 'unknown': (None, _('file not managed: %s\n')),
2879 2897 }
2880 2898
2881 2899 # "constant" that convey the backup strategy.
2882 2900 # All set to `discard` if `no-backup` is set do avoid checking
2883 2901 # no_backup lower in the code.
2884 2902 # These values are ordered for comparison purposes
2885 2903 backup = 2 # unconditionally do backup
2886 2904 check = 1 # check if the existing file differs from target
2887 2905 discard = 0 # never do backup
2888 2906 if opts.get('no_backup'):
2889 2907 backup = check = discard
2890 2908
2891 2909 backupanddel = actions['remove']
2892 2910 if not opts.get('no_backup'):
2893 2911 backupanddel = actions['drop']
2894 2912
2895 2913 disptable = (
2896 2914 # dispatch table:
2897 2915 # file state
2898 2916 # action
2899 2917 # make backup
2900 2918
2901 2919 ## Sets that results that will change file on disk
2902 2920 # Modified compared to target, no local change
2903 2921 (modified, actions['revert'], discard),
2904 2922 # Modified compared to target, but local file is deleted
2905 2923 (deleted, actions['revert'], discard),
2906 2924 # Modified compared to target, local change
2907 2925 (dsmodified, actions['revert'], backup),
2908 2926 # Added since target
2909 2927 (added, actions['remove'], discard),
2910 2928 # Added in working directory
2911 2929 (dsadded, actions['forget'], discard),
2912 2930 # Added since target, have local modification
2913 2931 (modadded, backupanddel, backup),
2914 2932 # Added since target but file is missing in working directory
2915 2933 (deladded, actions['drop'], discard),
2916 2934 # Removed since target, before working copy parent
2917 2935 (removed, actions['add'], discard),
2918 2936 # Same as `removed` but an unknown file exists at the same path
2919 2937 (removunk, actions['add'], check),
2920 2938 # Removed since targe, marked as such in working copy parent
2921 2939 (dsremoved, actions['undelete'], discard),
2922 2940 # Same as `dsremoved` but an unknown file exists at the same path
2923 2941 (dsremovunk, actions['undelete'], check),
2924 2942 ## the following sets does not result in any file changes
2925 2943 # File with no modification
2926 2944 (clean, actions['noop'], discard),
2927 2945 # Existing file, not tracked anywhere
2928 2946 (unknown, actions['unknown'], discard),
2929 2947 )
2930 2948
2931 2949 wctx = repo[None]
2932 2950 for abs, (rel, exact) in sorted(names.items()):
2933 2951 # target file to be touch on disk (relative to cwd)
2934 2952 target = repo.wjoin(abs)
2935 2953 # search the entry in the dispatch table.
2936 2954 # if the file is in any of these sets, it was touched in the working
2937 2955 # directory parent and we are sure it needs to be reverted.
2938 2956 for table, (xlist, msg), dobackup in disptable:
2939 2957 if abs not in table:
2940 2958 continue
2941 2959 if xlist is not None:
2942 2960 xlist.append(abs)
2943 2961 if dobackup and (backup <= dobackup
2944 2962 or wctx[abs].cmp(ctx[abs])):
2945 2963 bakname = "%s.orig" % rel
2946 2964 ui.note(_('saving current version of %s as %s\n') %
2947 2965 (rel, bakname))
2948 2966 if not opts.get('dry_run'):
2949 2967 util.rename(target, bakname)
2950 2968 if ui.verbose or not exact:
2951 2969 if not isinstance(msg, basestring):
2952 2970 msg = msg(abs)
2953 2971 ui.status(msg % rel)
2954 2972 elif exact:
2955 2973 ui.warn(msg % rel)
2956 2974 break
2957 2975
2958 2976
2959 2977 if not opts.get('dry_run'):
2960 2978 needdata = ('revert', 'add', 'undelete')
2961 2979 _revertprefetch(repo, ctx, *[actions[name][0] for name in needdata])
2962 2980
2963 2981 _performrevert(repo, parents, ctx, actions)
2964 2982
2965 2983 # get the list of subrepos that must be reverted
2966 2984 subrepomatch = scmutil.match(ctx, pats, opts)
2967 2985 targetsubs = sorted(s for s in ctx.substate if subrepomatch(s))
2968 2986
2969 2987 if targetsubs:
2970 2988 # Revert the subrepos on the revert list
2971 2989 for sub in targetsubs:
2972 2990 ctx.sub(sub).revert(ctx.substate[sub], *pats, **opts)
2973 2991 finally:
2974 2992 wlock.release()
2975 2993
2976 2994 def _revertprefetch(repo, ctx, *files):
2977 2995 """Let extension changing the storage layer prefetch content"""
2978 2996 pass
2979 2997
2980 2998 def _performrevert(repo, parents, ctx, actions):
2981 2999 """function that actually perform all the actions computed for revert
2982 3000
2983 3001 This is an independent function to let extension to plug in and react to
2984 3002 the imminent revert.
2985 3003
2986 3004 Make sure you have the working directory locked when calling this function.
2987 3005 """
2988 3006 parent, p2 = parents
2989 3007 node = ctx.node()
2990 3008 def checkout(f):
2991 3009 fc = ctx[f]
2992 3010 repo.wwrite(f, fc.data(), fc.flags())
2993 3011
2994 3012 audit_path = pathutil.pathauditor(repo.root)
2995 3013 for f in actions['forget'][0]:
2996 3014 repo.dirstate.drop(f)
2997 3015 for f in actions['remove'][0]:
2998 3016 audit_path(f)
2999 3017 util.unlinkpath(repo.wjoin(f))
3000 3018 repo.dirstate.remove(f)
3001 3019 for f in actions['drop'][0]:
3002 3020 audit_path(f)
3003 3021 repo.dirstate.remove(f)
3004 3022
3005 3023 normal = None
3006 3024 if node == parent:
3007 3025 # We're reverting to our parent. If possible, we'd like status
3008 3026 # to report the file as clean. We have to use normallookup for
3009 3027 # merges to avoid losing information about merged/dirty files.
3010 3028 if p2 != nullid:
3011 3029 normal = repo.dirstate.normallookup
3012 3030 else:
3013 3031 normal = repo.dirstate.normal
3014 3032 for f in actions['revert'][0]:
3015 3033 checkout(f)
3016 3034 if normal:
3017 3035 normal(f)
3018 3036
3019 3037 for f in actions['add'][0]:
3020 3038 checkout(f)
3021 3039 repo.dirstate.add(f)
3022 3040
3023 3041 normal = repo.dirstate.normallookup
3024 3042 if node == parent and p2 == nullid:
3025 3043 normal = repo.dirstate.normal
3026 3044 for f in actions['undelete'][0]:
3027 3045 checkout(f)
3028 3046 normal(f)
3029 3047
3030 3048 copied = copies.pathcopies(repo[parent], ctx)
3031 3049
3032 3050 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
3033 3051 if f in copied:
3034 3052 repo.dirstate.copy(copied[f], f)
3035 3053
3036 3054 def command(table):
3037 3055 """Returns a function object to be used as a decorator for making commands.
3038 3056
3039 3057 This function receives a command table as its argument. The table should
3040 3058 be a dict.
3041 3059
3042 3060 The returned function can be used as a decorator for adding commands
3043 3061 to that command table. This function accepts multiple arguments to define
3044 3062 a command.
3045 3063
3046 3064 The first argument is the command name.
3047 3065
3048 3066 The options argument is an iterable of tuples defining command arguments.
3049 3067 See ``mercurial.fancyopts.fancyopts()`` for the format of each tuple.
3050 3068
3051 3069 The synopsis argument defines a short, one line summary of how to use the
3052 3070 command. This shows up in the help output.
3053 3071
3054 3072 The norepo argument defines whether the command does not require a
3055 3073 local repository. Most commands operate against a repository, thus the
3056 3074 default is False.
3057 3075
3058 3076 The optionalrepo argument defines whether the command optionally requires
3059 3077 a local repository.
3060 3078
3061 3079 The inferrepo argument defines whether to try to find a repository from the
3062 3080 command line arguments. If True, arguments will be examined for potential
3063 3081 repository locations. See ``findrepo()``. If a repository is found, it
3064 3082 will be used.
3065 3083 """
3066 3084 def cmd(name, options=(), synopsis=None, norepo=False, optionalrepo=False,
3067 3085 inferrepo=False):
3068 3086 def decorator(func):
3069 3087 if synopsis:
3070 3088 table[name] = func, list(options), synopsis
3071 3089 else:
3072 3090 table[name] = func, list(options)
3073 3091
3074 3092 if norepo:
3075 3093 # Avoid import cycle.
3076 3094 import commands
3077 3095 commands.norepo += ' %s' % ' '.join(parsealiases(name))
3078 3096
3079 3097 if optionalrepo:
3080 3098 import commands
3081 3099 commands.optionalrepo += ' %s' % ' '.join(parsealiases(name))
3082 3100
3083 3101 if inferrepo:
3084 3102 import commands
3085 3103 commands.inferrepo += ' %s' % ' '.join(parsealiases(name))
3086 3104
3087 3105 return func
3088 3106 return decorator
3089 3107
3090 3108 return cmd
3091 3109
3092 3110 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3093 3111 # commands.outgoing. "missing" is "missing" of the result of
3094 3112 # "findcommonoutgoing()"
3095 3113 outgoinghooks = util.hooks()
3096 3114
3097 3115 # a list of (ui, repo) functions called by commands.summary
3098 3116 summaryhooks = util.hooks()
3099 3117
3100 3118 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3101 3119 #
3102 3120 # functions should return tuple of booleans below, if 'changes' is None:
3103 3121 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3104 3122 #
3105 3123 # otherwise, 'changes' is a tuple of tuples below:
3106 3124 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3107 3125 # - (desturl, destbranch, destpeer, outgoing)
3108 3126 summaryremotehooks = util.hooks()
3109 3127
3110 3128 # A list of state files kept by multistep operations like graft.
3111 3129 # Since graft cannot be aborted, it is considered 'clearable' by update.
3112 3130 # note: bisect is intentionally excluded
3113 3131 # (state file, clearable, allowcommit, error, hint)
3114 3132 unfinishedstates = [
3115 3133 ('graftstate', True, False, _('graft in progress'),
3116 3134 _("use 'hg graft --continue' or 'hg update' to abort")),
3117 3135 ('updatestate', True, False, _('last update was interrupted'),
3118 3136 _("use 'hg update' to get a consistent checkout"))
3119 3137 ]
3120 3138
3121 3139 def checkunfinished(repo, commit=False):
3122 3140 '''Look for an unfinished multistep operation, like graft, and abort
3123 3141 if found. It's probably good to check this right before
3124 3142 bailifchanged().
3125 3143 '''
3126 3144 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3127 3145 if commit and allowcommit:
3128 3146 continue
3129 3147 if repo.vfs.exists(f):
3130 3148 raise util.Abort(msg, hint=hint)
3131 3149
3132 3150 def clearunfinished(repo):
3133 3151 '''Check for unfinished operations (as above), and clear the ones
3134 3152 that are clearable.
3135 3153 '''
3136 3154 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3137 3155 if not clearable and repo.vfs.exists(f):
3138 3156 raise util.Abort(msg, hint=hint)
3139 3157 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3140 3158 if clearable and repo.vfs.exists(f):
3141 3159 util.unlink(repo.join(f))
@@ -1,6338 +1,6326 b''
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from node import hex, bin, nullid, nullrev, short
9 9 from lock import release
10 10 from i18n import _
11 11 import os, re, difflib, time, tempfile, errno, shlex
12 12 import sys, socket
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 import extensions
18 18 from hgweb import server as hgweb_server
19 19 import merge as mergemod
20 20 import minirst, revset, fileset
21 21 import dagparser, context, simplemerge, graphmod, copies
22 22 import random
23 23 import setdiscovery, treediscovery, dagutil, pvec, localrepo
24 24 import phases, obsolete, exchange, bundle2
25 25 import ui as uimod
26 26
27 27 table = {}
28 28
29 29 command = cmdutil.command(table)
30 30
31 31 # Space delimited list of commands that don't require local repositories.
32 32 # This should be populated by passing norepo=True into the @command decorator.
33 33 norepo = ''
34 34 # Space delimited list of commands that optionally require local repositories.
35 35 # This should be populated by passing optionalrepo=True into the @command
36 36 # decorator.
37 37 optionalrepo = ''
38 38 # Space delimited list of commands that will examine arguments looking for
39 39 # a repository. This should be populated by passing inferrepo=True into the
40 40 # @command decorator.
41 41 inferrepo = ''
42 42
43 43 # common command options
44 44
45 45 globalopts = [
46 46 ('R', 'repository', '',
47 47 _('repository root directory or name of overlay bundle file'),
48 48 _('REPO')),
49 49 ('', 'cwd', '',
50 50 _('change working directory'), _('DIR')),
51 51 ('y', 'noninteractive', None,
52 52 _('do not prompt, automatically pick the first choice for all prompts')),
53 53 ('q', 'quiet', None, _('suppress output')),
54 54 ('v', 'verbose', None, _('enable additional output')),
55 55 ('', 'config', [],
56 56 _('set/override config option (use \'section.name=value\')'),
57 57 _('CONFIG')),
58 58 ('', 'debug', None, _('enable debugging output')),
59 59 ('', 'debugger', None, _('start debugger')),
60 60 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
61 61 _('ENCODE')),
62 62 ('', 'encodingmode', encoding.encodingmode,
63 63 _('set the charset encoding mode'), _('MODE')),
64 64 ('', 'traceback', None, _('always print a traceback on exception')),
65 65 ('', 'time', None, _('time how long the command takes')),
66 66 ('', 'profile', None, _('print command execution profile')),
67 67 ('', 'version', None, _('output version information and exit')),
68 68 ('h', 'help', None, _('display help and exit')),
69 69 ('', 'hidden', False, _('consider hidden changesets')),
70 70 ]
71 71
72 72 dryrunopts = [('n', 'dry-run', None,
73 73 _('do not perform actions, just print output'))]
74 74
75 75 remoteopts = [
76 76 ('e', 'ssh', '',
77 77 _('specify ssh command to use'), _('CMD')),
78 78 ('', 'remotecmd', '',
79 79 _('specify hg command to run on the remote side'), _('CMD')),
80 80 ('', 'insecure', None,
81 81 _('do not verify server certificate (ignoring web.cacerts config)')),
82 82 ]
83 83
84 84 walkopts = [
85 85 ('I', 'include', [],
86 86 _('include names matching the given patterns'), _('PATTERN')),
87 87 ('X', 'exclude', [],
88 88 _('exclude names matching the given patterns'), _('PATTERN')),
89 89 ]
90 90
91 91 commitopts = [
92 92 ('m', 'message', '',
93 93 _('use text as commit message'), _('TEXT')),
94 94 ('l', 'logfile', '',
95 95 _('read commit message from file'), _('FILE')),
96 96 ]
97 97
98 98 commitopts2 = [
99 99 ('d', 'date', '',
100 100 _('record the specified date as commit date'), _('DATE')),
101 101 ('u', 'user', '',
102 102 _('record the specified user as committer'), _('USER')),
103 103 ]
104 104
105 105 # hidden for now
106 106 formatteropts = [
107 107 ('T', 'template', '',
108 108 _('display with template (DEPRECATED)'), _('TEMPLATE')),
109 109 ]
110 110
111 111 templateopts = [
112 112 ('', 'style', '',
113 113 _('display using template map file (DEPRECATED)'), _('STYLE')),
114 114 ('T', 'template', '',
115 115 _('display with template'), _('TEMPLATE')),
116 116 ]
117 117
118 118 logopts = [
119 119 ('p', 'patch', None, _('show patch')),
120 120 ('g', 'git', None, _('use git extended diff format')),
121 121 ('l', 'limit', '',
122 122 _('limit number of changes displayed'), _('NUM')),
123 123 ('M', 'no-merges', None, _('do not show merges')),
124 124 ('', 'stat', None, _('output diffstat-style summary of changes')),
125 125 ('G', 'graph', None, _("show the revision DAG")),
126 126 ] + templateopts
127 127
128 128 diffopts = [
129 129 ('a', 'text', None, _('treat all files as text')),
130 130 ('g', 'git', None, _('use git extended diff format')),
131 131 ('', 'nodates', None, _('omit dates from diff headers'))
132 132 ]
133 133
134 134 diffwsopts = [
135 135 ('w', 'ignore-all-space', None,
136 136 _('ignore white space when comparing lines')),
137 137 ('b', 'ignore-space-change', None,
138 138 _('ignore changes in the amount of white space')),
139 139 ('B', 'ignore-blank-lines', None,
140 140 _('ignore changes whose lines are all blank')),
141 141 ]
142 142
143 143 diffopts2 = [
144 144 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
145 145 ('p', 'show-function', None, _('show which function each change is in')),
146 146 ('', 'reverse', None, _('produce a diff that undoes the changes')),
147 147 ] + diffwsopts + [
148 148 ('U', 'unified', '',
149 149 _('number of lines of context to show'), _('NUM')),
150 150 ('', 'stat', None, _('output diffstat-style summary of changes')),
151 151 ]
152 152
153 153 mergetoolopts = [
154 154 ('t', 'tool', '', _('specify merge tool')),
155 155 ]
156 156
157 157 similarityopts = [
158 158 ('s', 'similarity', '',
159 159 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
160 160 ]
161 161
162 162 subrepoopts = [
163 163 ('S', 'subrepos', None,
164 164 _('recurse into subrepositories'))
165 165 ]
166 166
167 167 # Commands start here, listed alphabetically
168 168
169 169 @command('^add',
170 170 walkopts + subrepoopts + dryrunopts,
171 171 _('[OPTION]... [FILE]...'),
172 172 inferrepo=True)
173 173 def add(ui, repo, *pats, **opts):
174 174 """add the specified files on the next commit
175 175
176 176 Schedule files to be version controlled and added to the
177 177 repository.
178 178
179 179 The files will be added to the repository at the next commit. To
180 180 undo an add before that, see :hg:`forget`.
181 181
182 182 If no names are given, add all files to the repository.
183 183
184 184 .. container:: verbose
185 185
186 186 An example showing how new (unknown) files are added
187 187 automatically by :hg:`add`::
188 188
189 189 $ ls
190 190 foo.c
191 191 $ hg status
192 192 ? foo.c
193 193 $ hg add
194 194 adding foo.c
195 195 $ hg status
196 196 A foo.c
197 197
198 198 Returns 0 if all files are successfully added.
199 199 """
200 200
201 201 m = scmutil.match(repo[None], pats, opts)
202 202 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
203 203 return rejected and 1 or 0
204 204
205 205 @command('addremove',
206 206 similarityopts + subrepoopts + walkopts + dryrunopts,
207 207 _('[OPTION]... [FILE]...'),
208 208 inferrepo=True)
209 209 def addremove(ui, repo, *pats, **opts):
210 210 """add all new files, delete all missing files
211 211
212 212 Add all new files and remove all missing files from the
213 213 repository.
214 214
215 215 New files are ignored if they match any of the patterns in
216 216 ``.hgignore``. As with add, these changes take effect at the next
217 217 commit.
218 218
219 219 Use the -s/--similarity option to detect renamed files. This
220 220 option takes a percentage between 0 (disabled) and 100 (files must
221 221 be identical) as its parameter. With a parameter greater than 0,
222 222 this compares every removed file with every added file and records
223 223 those similar enough as renames. Detecting renamed files this way
224 224 can be expensive. After using this option, :hg:`status -C` can be
225 225 used to check which files were identified as moved or renamed. If
226 226 not specified, -s/--similarity defaults to 100 and only renames of
227 227 identical files are detected.
228 228
229 229 Returns 0 if all files are successfully added.
230 230 """
231 231 try:
232 232 sim = float(opts.get('similarity') or 100)
233 233 except ValueError:
234 234 raise util.Abort(_('similarity must be a number'))
235 235 if sim < 0 or sim > 100:
236 236 raise util.Abort(_('similarity must be between 0 and 100'))
237 237 matcher = scmutil.match(repo[None], pats, opts)
238 238 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
239 239
240 240 @command('^annotate|blame',
241 241 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
242 242 ('', 'follow', None,
243 243 _('follow copies/renames and list the filename (DEPRECATED)')),
244 244 ('', 'no-follow', None, _("don't follow copies and renames")),
245 245 ('a', 'text', None, _('treat all files as text')),
246 246 ('u', 'user', None, _('list the author (long with -v)')),
247 247 ('f', 'file', None, _('list the filename')),
248 248 ('d', 'date', None, _('list the date (short with -q)')),
249 249 ('n', 'number', None, _('list the revision number (default)')),
250 250 ('c', 'changeset', None, _('list the changeset')),
251 251 ('l', 'line-number', None, _('show line number at the first appearance'))
252 252 ] + diffwsopts + walkopts + formatteropts,
253 253 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
254 254 inferrepo=True)
255 255 def annotate(ui, repo, *pats, **opts):
256 256 """show changeset information by line for each file
257 257
258 258 List changes in files, showing the revision id responsible for
259 259 each line
260 260
261 261 This command is useful for discovering when a change was made and
262 262 by whom.
263 263
264 264 Without the -a/--text option, annotate will avoid processing files
265 265 it detects as binary. With -a, annotate will annotate the file
266 266 anyway, although the results will probably be neither useful
267 267 nor desirable.
268 268
269 269 Returns 0 on success.
270 270 """
271 271 if not pats:
272 272 raise util.Abort(_('at least one filename or pattern is required'))
273 273
274 274 if opts.get('follow'):
275 275 # --follow is deprecated and now just an alias for -f/--file
276 276 # to mimic the behavior of Mercurial before version 1.5
277 277 opts['file'] = True
278 278
279 279 fm = ui.formatter('annotate', opts)
280 280 datefunc = ui.quiet and util.shortdate or util.datestr
281 281 hexfn = fm.hexfunc
282 282
283 283 opmap = [('user', ' ', lambda x: x[0].user(), ui.shortuser),
284 284 ('number', ' ', lambda x: x[0].rev(), str),
285 285 ('changeset', ' ', lambda x: hexfn(x[0].node()), str),
286 286 ('date', ' ', lambda x: x[0].date(), util.cachefunc(datefunc)),
287 287 ('file', ' ', lambda x: x[0].path(), str),
288 288 ('line_number', ':', lambda x: x[1], str),
289 289 ]
290 290 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
291 291
292 292 if (not opts.get('user') and not opts.get('changeset')
293 293 and not opts.get('date') and not opts.get('file')):
294 294 opts['number'] = True
295 295
296 296 linenumber = opts.get('line_number') is not None
297 297 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
298 298 raise util.Abort(_('at least one of -n/-c is required for -l'))
299 299
300 300 if fm:
301 301 def makefunc(get, fmt):
302 302 return get
303 303 else:
304 304 def makefunc(get, fmt):
305 305 return lambda x: fmt(get(x))
306 306 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
307 307 if opts.get(op)]
308 308 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
309 309 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
310 310 if opts.get(op))
311 311
312 312 def bad(x, y):
313 313 raise util.Abort("%s: %s" % (x, y))
314 314
315 315 ctx = scmutil.revsingle(repo, opts.get('rev'))
316 316 m = scmutil.match(ctx, pats, opts)
317 317 m.bad = bad
318 318 follow = not opts.get('no_follow')
319 319 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
320 320 whitespace=True)
321 321 for abs in ctx.walk(m):
322 322 fctx = ctx[abs]
323 323 if not opts.get('text') and util.binary(fctx.data()):
324 324 fm.plain(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
325 325 continue
326 326
327 327 lines = fctx.annotate(follow=follow, linenumber=linenumber,
328 328 diffopts=diffopts)
329 329 formats = []
330 330 pieces = []
331 331
332 332 for f, sep in funcmap:
333 333 l = [f(n) for n, dummy in lines]
334 334 if l:
335 335 if fm:
336 336 formats.append(['%s' for x in l])
337 337 else:
338 338 sizes = [encoding.colwidth(x) for x in l]
339 339 ml = max(sizes)
340 340 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
341 341 pieces.append(l)
342 342
343 343 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
344 344 fm.startitem()
345 345 fm.write(fields, "".join(f), *p)
346 346 fm.write('line', ": %s", l[1])
347 347
348 348 if lines and not lines[-1][1].endswith('\n'):
349 349 fm.plain('\n')
350 350
351 351 fm.end()
352 352
353 353 @command('archive',
354 354 [('', 'no-decode', None, _('do not pass files through decoders')),
355 355 ('p', 'prefix', '', _('directory prefix for files in archive'),
356 356 _('PREFIX')),
357 357 ('r', 'rev', '', _('revision to distribute'), _('REV')),
358 358 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
359 359 ] + subrepoopts + walkopts,
360 360 _('[OPTION]... DEST'))
361 361 def archive(ui, repo, dest, **opts):
362 362 '''create an unversioned archive of a repository revision
363 363
364 364 By default, the revision used is the parent of the working
365 365 directory; use -r/--rev to specify a different revision.
366 366
367 367 The archive type is automatically detected based on file
368 368 extension (or override using -t/--type).
369 369
370 370 .. container:: verbose
371 371
372 372 Examples:
373 373
374 374 - create a zip file containing the 1.0 release::
375 375
376 376 hg archive -r 1.0 project-1.0.zip
377 377
378 378 - create a tarball excluding .hg files::
379 379
380 380 hg archive project.tar.gz -X ".hg*"
381 381
382 382 Valid types are:
383 383
384 384 :``files``: a directory full of files (default)
385 385 :``tar``: tar archive, uncompressed
386 386 :``tbz2``: tar archive, compressed using bzip2
387 387 :``tgz``: tar archive, compressed using gzip
388 388 :``uzip``: zip archive, uncompressed
389 389 :``zip``: zip archive, compressed using deflate
390 390
391 391 The exact name of the destination archive or directory is given
392 392 using a format string; see :hg:`help export` for details.
393 393
394 394 Each member added to an archive file has a directory prefix
395 395 prepended. Use -p/--prefix to specify a format string for the
396 396 prefix. The default is the basename of the archive, with suffixes
397 397 removed.
398 398
399 399 Returns 0 on success.
400 400 '''
401 401
402 402 ctx = scmutil.revsingle(repo, opts.get('rev'))
403 403 if not ctx:
404 404 raise util.Abort(_('no working directory: please specify a revision'))
405 405 node = ctx.node()
406 406 dest = cmdutil.makefilename(repo, dest, node)
407 407 if os.path.realpath(dest) == repo.root:
408 408 raise util.Abort(_('repository root cannot be destination'))
409 409
410 410 kind = opts.get('type') or archival.guesskind(dest) or 'files'
411 411 prefix = opts.get('prefix')
412 412
413 413 if dest == '-':
414 414 if kind == 'files':
415 415 raise util.Abort(_('cannot archive plain files to stdout'))
416 416 dest = cmdutil.makefileobj(repo, dest)
417 417 if not prefix:
418 418 prefix = os.path.basename(repo.root) + '-%h'
419 419
420 420 prefix = cmdutil.makefilename(repo, prefix, node)
421 421 matchfn = scmutil.match(ctx, [], opts)
422 422 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
423 423 matchfn, prefix, subrepos=opts.get('subrepos'))
424 424
425 425 @command('backout',
426 426 [('', 'merge', None, _('merge with old dirstate parent after backout')),
427 427 ('', 'commit', None, _('commit if no conflicts were encountered')),
428 428 ('', 'parent', '',
429 429 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
430 430 ('r', 'rev', '', _('revision to backout'), _('REV')),
431 431 ('e', 'edit', False, _('invoke editor on commit messages')),
432 432 ] + mergetoolopts + walkopts + commitopts + commitopts2,
433 433 _('[OPTION]... [-r] REV'))
434 434 def backout(ui, repo, node=None, rev=None, commit=False, **opts):
435 435 '''reverse effect of earlier changeset
436 436
437 437 Prepare a new changeset with the effect of REV undone in the
438 438 current working directory.
439 439
440 440 If REV is the parent of the working directory, then this new changeset
441 441 is committed automatically. Otherwise, hg needs to merge the
442 442 changes and the merged result is left uncommitted.
443 443
444 444 .. note::
445 445
446 446 backout cannot be used to fix either an unwanted or
447 447 incorrect merge.
448 448
449 449 .. container:: verbose
450 450
451 451 By default, the pending changeset will have one parent,
452 452 maintaining a linear history. With --merge, the pending
453 453 changeset will instead have two parents: the old parent of the
454 454 working directory and a new child of REV that simply undoes REV.
455 455
456 456 Before version 1.7, the behavior without --merge was equivalent
457 457 to specifying --merge followed by :hg:`update --clean .` to
458 458 cancel the merge and leave the child of REV as a head to be
459 459 merged separately.
460 460
461 461 See :hg:`help dates` for a list of formats valid for -d/--date.
462 462
463 463 Returns 0 on success, 1 if nothing to backout or there are unresolved
464 464 files.
465 465 '''
466 466 if rev and node:
467 467 raise util.Abort(_("please specify just one revision"))
468 468
469 469 if not rev:
470 470 rev = node
471 471
472 472 if not rev:
473 473 raise util.Abort(_("please specify a revision to backout"))
474 474
475 475 date = opts.get('date')
476 476 if date:
477 477 opts['date'] = util.parsedate(date)
478 478
479 479 cmdutil.checkunfinished(repo)
480 480 cmdutil.bailifchanged(repo)
481 481 node = scmutil.revsingle(repo, rev).node()
482 482
483 483 op1, op2 = repo.dirstate.parents()
484 484 if not repo.changelog.isancestor(node, op1):
485 485 raise util.Abort(_('cannot backout change that is not an ancestor'))
486 486
487 487 p1, p2 = repo.changelog.parents(node)
488 488 if p1 == nullid:
489 489 raise util.Abort(_('cannot backout a change with no parents'))
490 490 if p2 != nullid:
491 491 if not opts.get('parent'):
492 492 raise util.Abort(_('cannot backout a merge changeset'))
493 493 p = repo.lookup(opts['parent'])
494 494 if p not in (p1, p2):
495 495 raise util.Abort(_('%s is not a parent of %s') %
496 496 (short(p), short(node)))
497 497 parent = p
498 498 else:
499 499 if opts.get('parent'):
500 500 raise util.Abort(_('cannot use --parent on non-merge changeset'))
501 501 parent = p1
502 502
503 503 # the backout should appear on the same branch
504 504 wlock = repo.wlock()
505 505 try:
506 506 branch = repo.dirstate.branch()
507 507 bheads = repo.branchheads(branch)
508 508 rctx = scmutil.revsingle(repo, hex(parent))
509 509 if not opts.get('merge') and op1 != node:
510 510 try:
511 511 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
512 512 'backout')
513 513 repo.dirstate.beginparentchange()
514 514 stats = mergemod.update(repo, parent, True, True, False,
515 515 node, False)
516 516 repo.setparents(op1, op2)
517 517 repo.dirstate.endparentchange()
518 518 hg._showstats(repo, stats)
519 519 if stats[3]:
520 520 repo.ui.status(_("use 'hg resolve' to retry unresolved "
521 521 "file merges\n"))
522 522 return 1
523 523 elif not commit:
524 524 msg = _("changeset %s backed out, "
525 525 "don't forget to commit.\n")
526 526 ui.status(msg % short(node))
527 527 return 0
528 528 finally:
529 529 ui.setconfig('ui', 'forcemerge', '', '')
530 530 else:
531 531 hg.clean(repo, node, show_stats=False)
532 532 repo.dirstate.setbranch(branch)
533 533 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
534 534
535 535
536 536 def commitfunc(ui, repo, message, match, opts):
537 537 editform = 'backout'
538 538 e = cmdutil.getcommiteditor(editform=editform, **opts)
539 539 if not message:
540 540 # we don't translate commit messages
541 541 message = "Backed out changeset %s" % short(node)
542 542 e = cmdutil.getcommiteditor(edit=True, editform=editform)
543 543 return repo.commit(message, opts.get('user'), opts.get('date'),
544 544 match, editor=e)
545 545 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
546 546 if not newnode:
547 547 ui.status(_("nothing changed\n"))
548 548 return 1
549 549 cmdutil.commitstatus(repo, newnode, branch, bheads)
550 550
551 551 def nice(node):
552 552 return '%d:%s' % (repo.changelog.rev(node), short(node))
553 553 ui.status(_('changeset %s backs out changeset %s\n') %
554 554 (nice(repo.changelog.tip()), nice(node)))
555 555 if opts.get('merge') and op1 != node:
556 556 hg.clean(repo, op1, show_stats=False)
557 557 ui.status(_('merging with changeset %s\n')
558 558 % nice(repo.changelog.tip()))
559 559 try:
560 560 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
561 561 'backout')
562 562 return hg.merge(repo, hex(repo.changelog.tip()))
563 563 finally:
564 564 ui.setconfig('ui', 'forcemerge', '', '')
565 565 finally:
566 566 wlock.release()
567 567 return 0
568 568
569 569 @command('bisect',
570 570 [('r', 'reset', False, _('reset bisect state')),
571 571 ('g', 'good', False, _('mark changeset good')),
572 572 ('b', 'bad', False, _('mark changeset bad')),
573 573 ('s', 'skip', False, _('skip testing changeset')),
574 574 ('e', 'extend', False, _('extend the bisect range')),
575 575 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
576 576 ('U', 'noupdate', False, _('do not update to target'))],
577 577 _("[-gbsr] [-U] [-c CMD] [REV]"))
578 578 def bisect(ui, repo, rev=None, extra=None, command=None,
579 579 reset=None, good=None, bad=None, skip=None, extend=None,
580 580 noupdate=None):
581 581 """subdivision search of changesets
582 582
583 583 This command helps to find changesets which introduce problems. To
584 584 use, mark the earliest changeset you know exhibits the problem as
585 585 bad, then mark the latest changeset which is free from the problem
586 586 as good. Bisect will update your working directory to a revision
587 587 for testing (unless the -U/--noupdate option is specified). Once
588 588 you have performed tests, mark the working directory as good or
589 589 bad, and bisect will either update to another candidate changeset
590 590 or announce that it has found the bad revision.
591 591
592 592 As a shortcut, you can also use the revision argument to mark a
593 593 revision as good or bad without checking it out first.
594 594
595 595 If you supply a command, it will be used for automatic bisection.
596 596 The environment variable HG_NODE will contain the ID of the
597 597 changeset being tested. The exit status of the command will be
598 598 used to mark revisions as good or bad: status 0 means good, 125
599 599 means to skip the revision, 127 (command not found) will abort the
600 600 bisection, and any other non-zero exit status means the revision
601 601 is bad.
602 602
603 603 .. container:: verbose
604 604
605 605 Some examples:
606 606
607 607 - start a bisection with known bad revision 34, and good revision 12::
608 608
609 609 hg bisect --bad 34
610 610 hg bisect --good 12
611 611
612 612 - advance the current bisection by marking current revision as good or
613 613 bad::
614 614
615 615 hg bisect --good
616 616 hg bisect --bad
617 617
618 618 - mark the current revision, or a known revision, to be skipped (e.g. if
619 619 that revision is not usable because of another issue)::
620 620
621 621 hg bisect --skip
622 622 hg bisect --skip 23
623 623
624 624 - skip all revisions that do not touch directories ``foo`` or ``bar``::
625 625
626 626 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
627 627
628 628 - forget the current bisection::
629 629
630 630 hg bisect --reset
631 631
632 632 - use 'make && make tests' to automatically find the first broken
633 633 revision::
634 634
635 635 hg bisect --reset
636 636 hg bisect --bad 34
637 637 hg bisect --good 12
638 638 hg bisect --command "make && make tests"
639 639
640 640 - see all changesets whose states are already known in the current
641 641 bisection::
642 642
643 643 hg log -r "bisect(pruned)"
644 644
645 645 - see the changeset currently being bisected (especially useful
646 646 if running with -U/--noupdate)::
647 647
648 648 hg log -r "bisect(current)"
649 649
650 650 - see all changesets that took part in the current bisection::
651 651
652 652 hg log -r "bisect(range)"
653 653
654 654 - you can even get a nice graph::
655 655
656 656 hg log --graph -r "bisect(range)"
657 657
658 658 See :hg:`help revsets` for more about the `bisect()` keyword.
659 659
660 660 Returns 0 on success.
661 661 """
662 662 def extendbisectrange(nodes, good):
663 663 # bisect is incomplete when it ends on a merge node and
664 664 # one of the parent was not checked.
665 665 parents = repo[nodes[0]].parents()
666 666 if len(parents) > 1:
667 667 side = good and state['bad'] or state['good']
668 668 num = len(set(i.node() for i in parents) & set(side))
669 669 if num == 1:
670 670 return parents[0].ancestor(parents[1])
671 671 return None
672 672
673 673 def print_result(nodes, good):
674 674 displayer = cmdutil.show_changeset(ui, repo, {})
675 675 if len(nodes) == 1:
676 676 # narrowed it down to a single revision
677 677 if good:
678 678 ui.write(_("The first good revision is:\n"))
679 679 else:
680 680 ui.write(_("The first bad revision is:\n"))
681 681 displayer.show(repo[nodes[0]])
682 682 extendnode = extendbisectrange(nodes, good)
683 683 if extendnode is not None:
684 684 ui.write(_('Not all ancestors of this changeset have been'
685 685 ' checked.\nUse bisect --extend to continue the '
686 686 'bisection from\nthe common ancestor, %s.\n')
687 687 % extendnode)
688 688 else:
689 689 # multiple possible revisions
690 690 if good:
691 691 ui.write(_("Due to skipped revisions, the first "
692 692 "good revision could be any of:\n"))
693 693 else:
694 694 ui.write(_("Due to skipped revisions, the first "
695 695 "bad revision could be any of:\n"))
696 696 for n in nodes:
697 697 displayer.show(repo[n])
698 698 displayer.close()
699 699
700 700 def check_state(state, interactive=True):
701 701 if not state['good'] or not state['bad']:
702 702 if (good or bad or skip or reset) and interactive:
703 703 return
704 704 if not state['good']:
705 705 raise util.Abort(_('cannot bisect (no known good revisions)'))
706 706 else:
707 707 raise util.Abort(_('cannot bisect (no known bad revisions)'))
708 708 return True
709 709
710 710 # backward compatibility
711 711 if rev in "good bad reset init".split():
712 712 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
713 713 cmd, rev, extra = rev, extra, None
714 714 if cmd == "good":
715 715 good = True
716 716 elif cmd == "bad":
717 717 bad = True
718 718 else:
719 719 reset = True
720 720 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
721 721 raise util.Abort(_('incompatible arguments'))
722 722
723 723 cmdutil.checkunfinished(repo)
724 724
725 725 if reset:
726 726 p = repo.join("bisect.state")
727 727 if os.path.exists(p):
728 728 os.unlink(p)
729 729 return
730 730
731 731 state = hbisect.load_state(repo)
732 732
733 733 if command:
734 734 changesets = 1
735 735 if noupdate:
736 736 try:
737 737 node = state['current'][0]
738 738 except LookupError:
739 739 raise util.Abort(_('current bisect revision is unknown - '
740 740 'start a new bisect to fix'))
741 741 else:
742 742 node, p2 = repo.dirstate.parents()
743 743 if p2 != nullid:
744 744 raise util.Abort(_('current bisect revision is a merge'))
745 745 try:
746 746 while changesets:
747 747 # update state
748 748 state['current'] = [node]
749 749 hbisect.save_state(repo, state)
750 750 status = ui.system(command, environ={'HG_NODE': hex(node)})
751 751 if status == 125:
752 752 transition = "skip"
753 753 elif status == 0:
754 754 transition = "good"
755 755 # status < 0 means process was killed
756 756 elif status == 127:
757 757 raise util.Abort(_("failed to execute %s") % command)
758 758 elif status < 0:
759 759 raise util.Abort(_("%s killed") % command)
760 760 else:
761 761 transition = "bad"
762 762 ctx = scmutil.revsingle(repo, rev, node)
763 763 rev = None # clear for future iterations
764 764 state[transition].append(ctx.node())
765 765 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
766 766 check_state(state, interactive=False)
767 767 # bisect
768 768 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
769 769 # update to next check
770 770 node = nodes[0]
771 771 if not noupdate:
772 772 cmdutil.bailifchanged(repo)
773 773 hg.clean(repo, node, show_stats=False)
774 774 finally:
775 775 state['current'] = [node]
776 776 hbisect.save_state(repo, state)
777 777 print_result(nodes, bgood)
778 778 return
779 779
780 780 # update state
781 781
782 782 if rev:
783 783 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
784 784 else:
785 785 nodes = [repo.lookup('.')]
786 786
787 787 if good or bad or skip:
788 788 if good:
789 789 state['good'] += nodes
790 790 elif bad:
791 791 state['bad'] += nodes
792 792 elif skip:
793 793 state['skip'] += nodes
794 794 hbisect.save_state(repo, state)
795 795
796 796 if not check_state(state):
797 797 return
798 798
799 799 # actually bisect
800 800 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
801 801 if extend:
802 802 if not changesets:
803 803 extendnode = extendbisectrange(nodes, good)
804 804 if extendnode is not None:
805 805 ui.write(_("Extending search to changeset %d:%s\n")
806 806 % (extendnode.rev(), extendnode))
807 807 state['current'] = [extendnode.node()]
808 808 hbisect.save_state(repo, state)
809 809 if noupdate:
810 810 return
811 811 cmdutil.bailifchanged(repo)
812 812 return hg.clean(repo, extendnode.node())
813 813 raise util.Abort(_("nothing to extend"))
814 814
815 815 if changesets == 0:
816 816 print_result(nodes, good)
817 817 else:
818 818 assert len(nodes) == 1 # only a single node can be tested next
819 819 node = nodes[0]
820 820 # compute the approximate number of remaining tests
821 821 tests, size = 0, 2
822 822 while size <= changesets:
823 823 tests, size = tests + 1, size * 2
824 824 rev = repo.changelog.rev(node)
825 825 ui.write(_("Testing changeset %d:%s "
826 826 "(%d changesets remaining, ~%d tests)\n")
827 827 % (rev, short(node), changesets, tests))
828 828 state['current'] = [node]
829 829 hbisect.save_state(repo, state)
830 830 if not noupdate:
831 831 cmdutil.bailifchanged(repo)
832 832 return hg.clean(repo, node)
833 833
834 834 @command('bookmarks|bookmark',
835 835 [('f', 'force', False, _('force')),
836 836 ('r', 'rev', '', _('revision'), _('REV')),
837 837 ('d', 'delete', False, _('delete a given bookmark')),
838 838 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
839 839 ('i', 'inactive', False, _('mark a bookmark inactive')),
840 840 ] + formatteropts,
841 841 _('hg bookmarks [OPTIONS]... [NAME]...'))
842 842 def bookmark(ui, repo, *names, **opts):
843 843 '''create a new bookmark or list existing bookmarks
844 844
845 845 Bookmarks are labels on changesets to help track lines of development.
846 846 Bookmarks are unversioned and can be moved, renamed and deleted.
847 847 Deleting or moving a bookmark has no effect on the associated changesets.
848 848
849 849 Creating or updating to a bookmark causes it to be marked as 'active'.
850 850 The active bookmark is indicated with a '*'.
851 851 When a commit is made, the active bookmark will advance to the new commit.
852 852 A plain :hg:`update` will also advance an active bookmark, if possible.
853 853 Updating away from a bookmark will cause it to be deactivated.
854 854
855 855 Bookmarks can be pushed and pulled between repositories (see
856 856 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
857 857 diverged, a new 'divergent bookmark' of the form 'name@path' will
858 858 be created. Using :hg:`merge` will resolve the divergence.
859 859
860 860 A bookmark named '@' has the special property that :hg:`clone` will
861 861 check it out by default if it exists.
862 862
863 863 .. container:: verbose
864 864
865 865 Examples:
866 866
867 867 - create an active bookmark for a new line of development::
868 868
869 869 hg book new-feature
870 870
871 871 - create an inactive bookmark as a place marker::
872 872
873 873 hg book -i reviewed
874 874
875 875 - create an inactive bookmark on another changeset::
876 876
877 877 hg book -r .^ tested
878 878
879 879 - move the '@' bookmark from another branch::
880 880
881 881 hg book -f @
882 882 '''
883 883 force = opts.get('force')
884 884 rev = opts.get('rev')
885 885 delete = opts.get('delete')
886 886 rename = opts.get('rename')
887 887 inactive = opts.get('inactive')
888 888
889 889 def checkformat(mark):
890 890 mark = mark.strip()
891 891 if not mark:
892 892 raise util.Abort(_("bookmark names cannot consist entirely of "
893 893 "whitespace"))
894 894 scmutil.checknewlabel(repo, mark, 'bookmark')
895 895 return mark
896 896
897 897 def checkconflict(repo, mark, cur, force=False, target=None):
898 898 if mark in marks and not force:
899 899 if target:
900 900 if marks[mark] == target and target == cur:
901 901 # re-activating a bookmark
902 902 return
903 903 anc = repo.changelog.ancestors([repo[target].rev()])
904 904 bmctx = repo[marks[mark]]
905 905 divs = [repo[b].node() for b in marks
906 906 if b.split('@', 1)[0] == mark.split('@', 1)[0]]
907 907
908 908 # allow resolving a single divergent bookmark even if moving
909 909 # the bookmark across branches when a revision is specified
910 910 # that contains a divergent bookmark
911 911 if bmctx.rev() not in anc and target in divs:
912 912 bookmarks.deletedivergent(repo, [target], mark)
913 913 return
914 914
915 915 deletefrom = [b for b in divs
916 916 if repo[b].rev() in anc or b == target]
917 917 bookmarks.deletedivergent(repo, deletefrom, mark)
918 918 if bookmarks.validdest(repo, bmctx, repo[target]):
919 919 ui.status(_("moving bookmark '%s' forward from %s\n") %
920 920 (mark, short(bmctx.node())))
921 921 return
922 922 raise util.Abort(_("bookmark '%s' already exists "
923 923 "(use -f to force)") % mark)
924 924 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
925 925 and not force):
926 926 raise util.Abort(
927 927 _("a bookmark cannot have the name of an existing branch"))
928 928
929 929 if delete and rename:
930 930 raise util.Abort(_("--delete and --rename are incompatible"))
931 931 if delete and rev:
932 932 raise util.Abort(_("--rev is incompatible with --delete"))
933 933 if rename and rev:
934 934 raise util.Abort(_("--rev is incompatible with --rename"))
935 935 if not names and (delete or rev):
936 936 raise util.Abort(_("bookmark name required"))
937 937
938 938 if delete or rename or names or inactive:
939 939 wlock = repo.wlock()
940 940 try:
941 941 cur = repo.changectx('.').node()
942 942 marks = repo._bookmarks
943 943 if delete:
944 944 for mark in names:
945 945 if mark not in marks:
946 946 raise util.Abort(_("bookmark '%s' does not exist") %
947 947 mark)
948 948 if mark == repo._bookmarkcurrent:
949 949 bookmarks.unsetcurrent(repo)
950 950 del marks[mark]
951 951 marks.write()
952 952
953 953 elif rename:
954 954 if not names:
955 955 raise util.Abort(_("new bookmark name required"))
956 956 elif len(names) > 1:
957 957 raise util.Abort(_("only one new bookmark name allowed"))
958 958 mark = checkformat(names[0])
959 959 if rename not in marks:
960 960 raise util.Abort(_("bookmark '%s' does not exist") % rename)
961 961 checkconflict(repo, mark, cur, force)
962 962 marks[mark] = marks[rename]
963 963 if repo._bookmarkcurrent == rename and not inactive:
964 964 bookmarks.setcurrent(repo, mark)
965 965 del marks[rename]
966 966 marks.write()
967 967
968 968 elif names:
969 969 newact = None
970 970 for mark in names:
971 971 mark = checkformat(mark)
972 972 if newact is None:
973 973 newact = mark
974 974 if inactive and mark == repo._bookmarkcurrent:
975 975 bookmarks.unsetcurrent(repo)
976 976 return
977 977 tgt = cur
978 978 if rev:
979 979 tgt = scmutil.revsingle(repo, rev).node()
980 980 checkconflict(repo, mark, cur, force, tgt)
981 981 marks[mark] = tgt
982 982 if not inactive and cur == marks[newact] and not rev:
983 983 bookmarks.setcurrent(repo, newact)
984 984 elif cur != tgt and newact == repo._bookmarkcurrent:
985 985 bookmarks.unsetcurrent(repo)
986 986 marks.write()
987 987
988 988 elif inactive:
989 989 if len(marks) == 0:
990 990 ui.status(_("no bookmarks set\n"))
991 991 elif not repo._bookmarkcurrent:
992 992 ui.status(_("no active bookmark\n"))
993 993 else:
994 994 bookmarks.unsetcurrent(repo)
995 995 finally:
996 996 wlock.release()
997 997 else: # show bookmarks
998 998 fm = ui.formatter('bookmarks', opts)
999 999 hexfn = fm.hexfunc
1000 1000 marks = repo._bookmarks
1001 1001 if len(marks) == 0 and not fm:
1002 1002 ui.status(_("no bookmarks set\n"))
1003 1003 for bmark, n in sorted(marks.iteritems()):
1004 1004 current = repo._bookmarkcurrent
1005 1005 if bmark == current:
1006 1006 prefix, label = '*', 'bookmarks.current'
1007 1007 else:
1008 1008 prefix, label = ' ', ''
1009 1009
1010 1010 fm.startitem()
1011 1011 if not ui.quiet:
1012 1012 fm.plain(' %s ' % prefix, label=label)
1013 1013 fm.write('bookmark', '%s', bmark, label=label)
1014 1014 pad = " " * (25 - encoding.colwidth(bmark))
1015 1015 fm.condwrite(not ui.quiet, 'rev node', pad + ' %d:%s',
1016 1016 repo.changelog.rev(n), hexfn(n), label=label)
1017 1017 fm.data(active=(bmark == current))
1018 1018 fm.plain('\n')
1019 1019 fm.end()
1020 1020
1021 1021 @command('branch',
1022 1022 [('f', 'force', None,
1023 1023 _('set branch name even if it shadows an existing branch')),
1024 1024 ('C', 'clean', None, _('reset branch name to parent branch name'))],
1025 1025 _('[-fC] [NAME]'))
1026 1026 def branch(ui, repo, label=None, **opts):
1027 1027 """set or show the current branch name
1028 1028
1029 1029 .. note::
1030 1030
1031 1031 Branch names are permanent and global. Use :hg:`bookmark` to create a
1032 1032 light-weight bookmark instead. See :hg:`help glossary` for more
1033 1033 information about named branches and bookmarks.
1034 1034
1035 1035 With no argument, show the current branch name. With one argument,
1036 1036 set the working directory branch name (the branch will not exist
1037 1037 in the repository until the next commit). Standard practice
1038 1038 recommends that primary development take place on the 'default'
1039 1039 branch.
1040 1040
1041 1041 Unless -f/--force is specified, branch will not let you set a
1042 1042 branch name that already exists.
1043 1043
1044 1044 Use -C/--clean to reset the working directory branch to that of
1045 1045 the parent of the working directory, negating a previous branch
1046 1046 change.
1047 1047
1048 1048 Use the command :hg:`update` to switch to an existing branch. Use
1049 1049 :hg:`commit --close-branch` to mark this branch as closed.
1050 1050
1051 1051 Returns 0 on success.
1052 1052 """
1053 1053 if label:
1054 1054 label = label.strip()
1055 1055
1056 1056 if not opts.get('clean') and not label:
1057 1057 ui.write("%s\n" % repo.dirstate.branch())
1058 1058 return
1059 1059
1060 1060 wlock = repo.wlock()
1061 1061 try:
1062 1062 if opts.get('clean'):
1063 1063 label = repo[None].p1().branch()
1064 1064 repo.dirstate.setbranch(label)
1065 1065 ui.status(_('reset working directory to branch %s\n') % label)
1066 1066 elif label:
1067 1067 if not opts.get('force') and label in repo.branchmap():
1068 1068 if label not in [p.branch() for p in repo.parents()]:
1069 1069 raise util.Abort(_('a branch of the same name already'
1070 1070 ' exists'),
1071 1071 # i18n: "it" refers to an existing branch
1072 1072 hint=_("use 'hg update' to switch to it"))
1073 1073 scmutil.checknewlabel(repo, label, 'branch')
1074 1074 repo.dirstate.setbranch(label)
1075 1075 ui.status(_('marked working directory as branch %s\n') % label)
1076 1076 ui.status(_('(branches are permanent and global, '
1077 1077 'did you want a bookmark?)\n'))
1078 1078 finally:
1079 1079 wlock.release()
1080 1080
1081 1081 @command('branches',
1082 1082 [('a', 'active', False,
1083 1083 _('show only branches that have unmerged heads (DEPRECATED)')),
1084 1084 ('c', 'closed', False, _('show normal and closed branches')),
1085 1085 ] + formatteropts,
1086 1086 _('[-ac]'))
1087 1087 def branches(ui, repo, active=False, closed=False, **opts):
1088 1088 """list repository named branches
1089 1089
1090 1090 List the repository's named branches, indicating which ones are
1091 1091 inactive. If -c/--closed is specified, also list branches which have
1092 1092 been marked closed (see :hg:`commit --close-branch`).
1093 1093
1094 1094 Use the command :hg:`update` to switch to an existing branch.
1095 1095
1096 1096 Returns 0.
1097 1097 """
1098 1098
1099 1099 fm = ui.formatter('branches', opts)
1100 1100 hexfunc = fm.hexfunc
1101 1101
1102 1102 allheads = set(repo.heads())
1103 1103 branches = []
1104 1104 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1105 1105 isactive = not isclosed and bool(set(heads) & allheads)
1106 1106 branches.append((tag, repo[tip], isactive, not isclosed))
1107 1107 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1108 1108 reverse=True)
1109 1109
1110 1110 for tag, ctx, isactive, isopen in branches:
1111 1111 if active and not isactive:
1112 1112 continue
1113 1113 if isactive:
1114 1114 label = 'branches.active'
1115 1115 notice = ''
1116 1116 elif not isopen:
1117 1117 if not closed:
1118 1118 continue
1119 1119 label = 'branches.closed'
1120 1120 notice = _(' (closed)')
1121 1121 else:
1122 1122 label = 'branches.inactive'
1123 1123 notice = _(' (inactive)')
1124 1124 current = (tag == repo.dirstate.branch())
1125 1125 if current:
1126 1126 label = 'branches.current'
1127 1127
1128 1128 fm.startitem()
1129 1129 fm.write('branch', '%s', tag, label=label)
1130 1130 rev = ctx.rev()
1131 1131 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1132 1132 fmt = ' ' * padsize + ' %d:%s'
1133 1133 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1134 1134 label='log.changeset changeset.%s' % ctx.phasestr())
1135 1135 fm.data(active=isactive, closed=not isopen, current=current)
1136 1136 if not ui.quiet:
1137 1137 fm.plain(notice)
1138 1138 fm.plain('\n')
1139 1139 fm.end()
1140 1140
1141 1141 @command('bundle',
1142 1142 [('f', 'force', None, _('run even when the destination is unrelated')),
1143 1143 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1144 1144 _('REV')),
1145 1145 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1146 1146 _('BRANCH')),
1147 1147 ('', 'base', [],
1148 1148 _('a base changeset assumed to be available at the destination'),
1149 1149 _('REV')),
1150 1150 ('a', 'all', None, _('bundle all changesets in the repository')),
1151 1151 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1152 1152 ] + remoteopts,
1153 1153 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1154 1154 def bundle(ui, repo, fname, dest=None, **opts):
1155 1155 """create a changegroup file
1156 1156
1157 1157 Generate a compressed changegroup file collecting changesets not
1158 1158 known to be in another repository.
1159 1159
1160 1160 If you omit the destination repository, then hg assumes the
1161 1161 destination will have all the nodes you specify with --base
1162 1162 parameters. To create a bundle containing all changesets, use
1163 1163 -a/--all (or --base null).
1164 1164
1165 1165 You can change compression method with the -t/--type option.
1166 1166 The available compression methods are: none, bzip2, and
1167 1167 gzip (by default, bundles are compressed using bzip2).
1168 1168
1169 1169 The bundle file can then be transferred using conventional means
1170 1170 and applied to another repository with the unbundle or pull
1171 1171 command. This is useful when direct push and pull are not
1172 1172 available or when exporting an entire repository is undesirable.
1173 1173
1174 1174 Applying bundles preserves all changeset contents including
1175 1175 permissions, copy/rename information, and revision history.
1176 1176
1177 1177 Returns 0 on success, 1 if no changes found.
1178 1178 """
1179 1179 revs = None
1180 1180 if 'rev' in opts:
1181 1181 revs = scmutil.revrange(repo, opts['rev'])
1182 1182
1183 1183 bundletype = opts.get('type', 'bzip2').lower()
1184 1184 btypes = {'none': 'HG10UN',
1185 1185 'bzip2': 'HG10BZ',
1186 1186 'gzip': 'HG10GZ',
1187 1187 'bundle2': 'HG2Y'}
1188 1188 bundletype = btypes.get(bundletype)
1189 1189 if bundletype not in changegroup.bundletypes:
1190 1190 raise util.Abort(_('unknown bundle type specified with --type'))
1191 1191
1192 1192 if opts.get('all'):
1193 1193 base = ['null']
1194 1194 else:
1195 1195 base = scmutil.revrange(repo, opts.get('base'))
1196 1196 # TODO: get desired bundlecaps from command line.
1197 1197 bundlecaps = None
1198 1198 if base:
1199 1199 if dest:
1200 1200 raise util.Abort(_("--base is incompatible with specifying "
1201 1201 "a destination"))
1202 1202 common = [repo.lookup(rev) for rev in base]
1203 1203 heads = revs and map(repo.lookup, revs) or revs
1204 1204 cg = changegroup.getchangegroup(repo, 'bundle', heads=heads,
1205 1205 common=common, bundlecaps=bundlecaps)
1206 1206 outgoing = None
1207 1207 else:
1208 1208 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1209 1209 dest, branches = hg.parseurl(dest, opts.get('branch'))
1210 1210 other = hg.peer(repo, opts, dest)
1211 1211 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1212 1212 heads = revs and map(repo.lookup, revs) or revs
1213 1213 outgoing = discovery.findcommonoutgoing(repo, other,
1214 1214 onlyheads=heads,
1215 1215 force=opts.get('force'),
1216 1216 portable=True)
1217 1217 cg = changegroup.getlocalchangegroup(repo, 'bundle', outgoing,
1218 1218 bundlecaps)
1219 1219 if not cg:
1220 1220 scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
1221 1221 return 1
1222 1222
1223 1223 changegroup.writebundle(ui, cg, fname, bundletype)
1224 1224
1225 1225 @command('cat',
1226 1226 [('o', 'output', '',
1227 1227 _('print output to file with formatted name'), _('FORMAT')),
1228 1228 ('r', 'rev', '', _('print the given revision'), _('REV')),
1229 1229 ('', 'decode', None, _('apply any matching decode filter')),
1230 1230 ] + walkopts,
1231 1231 _('[OPTION]... FILE...'),
1232 1232 inferrepo=True)
1233 1233 def cat(ui, repo, file1, *pats, **opts):
1234 1234 """output the current or given revision of files
1235 1235
1236 1236 Print the specified files as they were at the given revision. If
1237 1237 no revision is given, the parent of the working directory is used.
1238 1238
1239 1239 Output may be to a file, in which case the name of the file is
1240 1240 given using a format string. The formatting rules as follows:
1241 1241
1242 1242 :``%%``: literal "%" character
1243 1243 :``%s``: basename of file being printed
1244 1244 :``%d``: dirname of file being printed, or '.' if in repository root
1245 1245 :``%p``: root-relative path name of file being printed
1246 1246 :``%H``: changeset hash (40 hexadecimal digits)
1247 1247 :``%R``: changeset revision number
1248 1248 :``%h``: short-form changeset hash (12 hexadecimal digits)
1249 1249 :``%r``: zero-padded changeset revision number
1250 1250 :``%b``: basename of the exporting repository
1251 1251
1252 1252 Returns 0 on success.
1253 1253 """
1254 1254 ctx = scmutil.revsingle(repo, opts.get('rev'))
1255 1255 m = scmutil.match(ctx, (file1,) + pats, opts)
1256 1256
1257 1257 return cmdutil.cat(ui, repo, ctx, m, '', **opts)
1258 1258
1259 1259 @command('^clone',
1260 1260 [('U', 'noupdate', None,
1261 1261 _('the clone will include an empty working copy (only a repository)')),
1262 1262 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
1263 1263 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1264 1264 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1265 1265 ('', 'pull', None, _('use pull protocol to copy metadata')),
1266 1266 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1267 1267 ] + remoteopts,
1268 1268 _('[OPTION]... SOURCE [DEST]'),
1269 1269 norepo=True)
1270 1270 def clone(ui, source, dest=None, **opts):
1271 1271 """make a copy of an existing repository
1272 1272
1273 1273 Create a copy of an existing repository in a new directory.
1274 1274
1275 1275 If no destination directory name is specified, it defaults to the
1276 1276 basename of the source.
1277 1277
1278 1278 The location of the source is added to the new repository's
1279 1279 ``.hg/hgrc`` file, as the default to be used for future pulls.
1280 1280
1281 1281 Only local paths and ``ssh://`` URLs are supported as
1282 1282 destinations. For ``ssh://`` destinations, no working directory or
1283 1283 ``.hg/hgrc`` will be created on the remote side.
1284 1284
1285 1285 To pull only a subset of changesets, specify one or more revisions
1286 1286 identifiers with -r/--rev or branches with -b/--branch. The
1287 1287 resulting clone will contain only the specified changesets and
1288 1288 their ancestors. These options (or 'clone src#rev dest') imply
1289 1289 --pull, even for local source repositories. Note that specifying a
1290 1290 tag will include the tagged changeset but not the changeset
1291 1291 containing the tag.
1292 1292
1293 1293 If the source repository has a bookmark called '@' set, that
1294 1294 revision will be checked out in the new repository by default.
1295 1295
1296 1296 To check out a particular version, use -u/--update, or
1297 1297 -U/--noupdate to create a clone with no working directory.
1298 1298
1299 1299 .. container:: verbose
1300 1300
1301 1301 For efficiency, hardlinks are used for cloning whenever the
1302 1302 source and destination are on the same filesystem (note this
1303 1303 applies only to the repository data, not to the working
1304 1304 directory). Some filesystems, such as AFS, implement hardlinking
1305 1305 incorrectly, but do not report errors. In these cases, use the
1306 1306 --pull option to avoid hardlinking.
1307 1307
1308 1308 In some cases, you can clone repositories and the working
1309 1309 directory using full hardlinks with ::
1310 1310
1311 1311 $ cp -al REPO REPOCLONE
1312 1312
1313 1313 This is the fastest way to clone, but it is not always safe. The
1314 1314 operation is not atomic (making sure REPO is not modified during
1315 1315 the operation is up to you) and you have to make sure your
1316 1316 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1317 1317 so). Also, this is not compatible with certain extensions that
1318 1318 place their metadata under the .hg directory, such as mq.
1319 1319
1320 1320 Mercurial will update the working directory to the first applicable
1321 1321 revision from this list:
1322 1322
1323 1323 a) null if -U or the source repository has no changesets
1324 1324 b) if -u . and the source repository is local, the first parent of
1325 1325 the source repository's working directory
1326 1326 c) the changeset specified with -u (if a branch name, this means the
1327 1327 latest head of that branch)
1328 1328 d) the changeset specified with -r
1329 1329 e) the tipmost head specified with -b
1330 1330 f) the tipmost head specified with the url#branch source syntax
1331 1331 g) the revision marked with the '@' bookmark, if present
1332 1332 h) the tipmost head of the default branch
1333 1333 i) tip
1334 1334
1335 1335 Examples:
1336 1336
1337 1337 - clone a remote repository to a new directory named hg/::
1338 1338
1339 1339 hg clone http://selenic.com/hg
1340 1340
1341 1341 - create a lightweight local clone::
1342 1342
1343 1343 hg clone project/ project-feature/
1344 1344
1345 1345 - clone from an absolute path on an ssh server (note double-slash)::
1346 1346
1347 1347 hg clone ssh://user@server//home/projects/alpha/
1348 1348
1349 1349 - do a high-speed clone over a LAN while checking out a
1350 1350 specified version::
1351 1351
1352 1352 hg clone --uncompressed http://server/repo -u 1.5
1353 1353
1354 1354 - create a repository without changesets after a particular revision::
1355 1355
1356 1356 hg clone -r 04e544 experimental/ good/
1357 1357
1358 1358 - clone (and track) a particular named branch::
1359 1359
1360 1360 hg clone http://selenic.com/hg#stable
1361 1361
1362 1362 See :hg:`help urls` for details on specifying URLs.
1363 1363
1364 1364 Returns 0 on success.
1365 1365 """
1366 1366 if opts.get('noupdate') and opts.get('updaterev'):
1367 1367 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1368 1368
1369 1369 r = hg.clone(ui, opts, source, dest,
1370 1370 pull=opts.get('pull'),
1371 1371 stream=opts.get('uncompressed'),
1372 1372 rev=opts.get('rev'),
1373 1373 update=opts.get('updaterev') or not opts.get('noupdate'),
1374 1374 branch=opts.get('branch'))
1375 1375
1376 1376 return r is None
1377 1377
1378 1378 @command('^commit|ci',
1379 1379 [('A', 'addremove', None,
1380 1380 _('mark new/missing files as added/removed before committing')),
1381 1381 ('', 'close-branch', None,
1382 1382 _('mark a branch as closed, hiding it from the branch list')),
1383 1383 ('', 'amend', None, _('amend the parent of the working dir')),
1384 1384 ('s', 'secret', None, _('use the secret phase for committing')),
1385 1385 ('e', 'edit', None, _('invoke editor on commit messages')),
1386 1386 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1387 1387 _('[OPTION]... [FILE]...'),
1388 1388 inferrepo=True)
1389 1389 def commit(ui, repo, *pats, **opts):
1390 1390 """commit the specified files or all outstanding changes
1391 1391
1392 1392 Commit changes to the given files into the repository. Unlike a
1393 1393 centralized SCM, this operation is a local operation. See
1394 1394 :hg:`push` for a way to actively distribute your changes.
1395 1395
1396 1396 If a list of files is omitted, all changes reported by :hg:`status`
1397 1397 will be committed.
1398 1398
1399 1399 If you are committing the result of a merge, do not provide any
1400 1400 filenames or -I/-X filters.
1401 1401
1402 1402 If no commit message is specified, Mercurial starts your
1403 1403 configured editor where you can enter a message. In case your
1404 1404 commit fails, you will find a backup of your message in
1405 1405 ``.hg/last-message.txt``.
1406 1406
1407 1407 The --amend flag can be used to amend the parent of the
1408 1408 working directory with a new commit that contains the changes
1409 1409 in the parent in addition to those currently reported by :hg:`status`,
1410 1410 if there are any. The old commit is stored in a backup bundle in
1411 1411 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1412 1412 on how to restore it).
1413 1413
1414 1414 Message, user and date are taken from the amended commit unless
1415 1415 specified. When a message isn't specified on the command line,
1416 1416 the editor will open with the message of the amended commit.
1417 1417
1418 1418 It is not possible to amend public changesets (see :hg:`help phases`)
1419 1419 or changesets that have children.
1420 1420
1421 1421 See :hg:`help dates` for a list of formats valid for -d/--date.
1422 1422
1423 1423 Returns 0 on success, 1 if nothing changed.
1424 1424 """
1425 1425 if opts.get('subrepos'):
1426 1426 if opts.get('amend'):
1427 1427 raise util.Abort(_('cannot amend with --subrepos'))
1428 1428 # Let --subrepos on the command line override config setting.
1429 1429 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1430 1430
1431 1431 cmdutil.checkunfinished(repo, commit=True)
1432 1432
1433 1433 branch = repo[None].branch()
1434 1434 bheads = repo.branchheads(branch)
1435 1435
1436 1436 extra = {}
1437 1437 if opts.get('close_branch'):
1438 1438 extra['close'] = 1
1439 1439
1440 1440 if not bheads:
1441 1441 raise util.Abort(_('can only close branch heads'))
1442 1442 elif opts.get('amend'):
1443 1443 if repo.parents()[0].p1().branch() != branch and \
1444 1444 repo.parents()[0].p2().branch() != branch:
1445 1445 raise util.Abort(_('can only close branch heads'))
1446 1446
1447 1447 if opts.get('amend'):
1448 1448 if ui.configbool('ui', 'commitsubrepos'):
1449 1449 raise util.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1450 1450
1451 1451 old = repo['.']
1452 1452 if not old.mutable():
1453 1453 raise util.Abort(_('cannot amend public changesets'))
1454 1454 if len(repo[None].parents()) > 1:
1455 1455 raise util.Abort(_('cannot amend while merging'))
1456 1456 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1457 1457 if not allowunstable and old.children():
1458 1458 raise util.Abort(_('cannot amend changeset with children'))
1459 1459
1460 1460 # commitfunc is used only for temporary amend commit by cmdutil.amend
1461 1461 def commitfunc(ui, repo, message, match, opts):
1462 1462 return repo.commit(message,
1463 1463 opts.get('user') or old.user(),
1464 1464 opts.get('date') or old.date(),
1465 1465 match,
1466 1466 extra=extra)
1467 1467
1468 1468 current = repo._bookmarkcurrent
1469 1469 marks = old.bookmarks()
1470 1470 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1471 1471 if node == old.node():
1472 1472 ui.status(_("nothing changed\n"))
1473 1473 return 1
1474 1474 elif marks:
1475 1475 ui.debug('moving bookmarks %r from %s to %s\n' %
1476 1476 (marks, old.hex(), hex(node)))
1477 1477 newmarks = repo._bookmarks
1478 1478 for bm in marks:
1479 1479 newmarks[bm] = node
1480 1480 if bm == current:
1481 1481 bookmarks.setcurrent(repo, bm)
1482 1482 newmarks.write()
1483 1483 else:
1484 1484 def commitfunc(ui, repo, message, match, opts):
1485 1485 backup = ui.backupconfig('phases', 'new-commit')
1486 1486 baseui = repo.baseui
1487 1487 basebackup = baseui.backupconfig('phases', 'new-commit')
1488 1488 try:
1489 1489 if opts.get('secret'):
1490 1490 ui.setconfig('phases', 'new-commit', 'secret', 'commit')
1491 1491 # Propagate to subrepos
1492 1492 baseui.setconfig('phases', 'new-commit', 'secret', 'commit')
1493 1493
1494 1494 editform = cmdutil.mergeeditform(repo[None], 'commit.normal')
1495 1495 editor = cmdutil.getcommiteditor(editform=editform, **opts)
1496 1496 return repo.commit(message, opts.get('user'), opts.get('date'),
1497 1497 match,
1498 1498 editor=editor,
1499 1499 extra=extra)
1500 1500 finally:
1501 1501 ui.restoreconfig(backup)
1502 1502 repo.baseui.restoreconfig(basebackup)
1503 1503
1504 1504
1505 1505 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1506 1506
1507 1507 if not node:
1508 1508 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1509 1509 if stat[3]:
1510 1510 ui.status(_("nothing changed (%d missing files, see "
1511 1511 "'hg status')\n") % len(stat[3]))
1512 1512 else:
1513 1513 ui.status(_("nothing changed\n"))
1514 1514 return 1
1515 1515
1516 1516 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1517 1517
1518 1518 @command('config|showconfig|debugconfig',
1519 1519 [('u', 'untrusted', None, _('show untrusted configuration options')),
1520 1520 ('e', 'edit', None, _('edit user config')),
1521 1521 ('l', 'local', None, _('edit repository config')),
1522 1522 ('g', 'global', None, _('edit global config'))],
1523 1523 _('[-u] [NAME]...'),
1524 1524 optionalrepo=True)
1525 1525 def config(ui, repo, *values, **opts):
1526 1526 """show combined config settings from all hgrc files
1527 1527
1528 1528 With no arguments, print names and values of all config items.
1529 1529
1530 1530 With one argument of the form section.name, print just the value
1531 1531 of that config item.
1532 1532
1533 1533 With multiple arguments, print names and values of all config
1534 1534 items with matching section names.
1535 1535
1536 1536 With --edit, start an editor on the user-level config file. With
1537 1537 --global, edit the system-wide config file. With --local, edit the
1538 1538 repository-level config file.
1539 1539
1540 1540 With --debug, the source (filename and line number) is printed
1541 1541 for each config item.
1542 1542
1543 1543 See :hg:`help config` for more information about config files.
1544 1544
1545 1545 Returns 0 on success, 1 if NAME does not exist.
1546 1546
1547 1547 """
1548 1548
1549 1549 if opts.get('edit') or opts.get('local') or opts.get('global'):
1550 1550 if opts.get('local') and opts.get('global'):
1551 1551 raise util.Abort(_("can't use --local and --global together"))
1552 1552
1553 1553 if opts.get('local'):
1554 1554 if not repo:
1555 1555 raise util.Abort(_("can't use --local outside a repository"))
1556 1556 paths = [repo.join('hgrc')]
1557 1557 elif opts.get('global'):
1558 1558 paths = scmutil.systemrcpath()
1559 1559 else:
1560 1560 paths = scmutil.userrcpath()
1561 1561
1562 1562 for f in paths:
1563 1563 if os.path.exists(f):
1564 1564 break
1565 1565 else:
1566 1566 if opts.get('global'):
1567 1567 samplehgrc = uimod.samplehgrcs['global']
1568 1568 elif opts.get('local'):
1569 1569 samplehgrc = uimod.samplehgrcs['local']
1570 1570 else:
1571 1571 samplehgrc = uimod.samplehgrcs['user']
1572 1572
1573 1573 f = paths[0]
1574 1574 fp = open(f, "w")
1575 1575 fp.write(samplehgrc)
1576 1576 fp.close()
1577 1577
1578 1578 editor = ui.geteditor()
1579 1579 ui.system("%s \"%s\"" % (editor, f),
1580 1580 onerr=util.Abort, errprefix=_("edit failed"))
1581 1581 return
1582 1582
1583 1583 for f in scmutil.rcpath():
1584 1584 ui.debug('read config from: %s\n' % f)
1585 1585 untrusted = bool(opts.get('untrusted'))
1586 1586 if values:
1587 1587 sections = [v for v in values if '.' not in v]
1588 1588 items = [v for v in values if '.' in v]
1589 1589 if len(items) > 1 or items and sections:
1590 1590 raise util.Abort(_('only one config item permitted'))
1591 1591 matched = False
1592 1592 for section, name, value in ui.walkconfig(untrusted=untrusted):
1593 1593 value = str(value).replace('\n', '\\n')
1594 1594 sectname = section + '.' + name
1595 1595 if values:
1596 1596 for v in values:
1597 1597 if v == section:
1598 1598 ui.debug('%s: ' %
1599 1599 ui.configsource(section, name, untrusted))
1600 1600 ui.write('%s=%s\n' % (sectname, value))
1601 1601 matched = True
1602 1602 elif v == sectname:
1603 1603 ui.debug('%s: ' %
1604 1604 ui.configsource(section, name, untrusted))
1605 1605 ui.write(value, '\n')
1606 1606 matched = True
1607 1607 else:
1608 1608 ui.debug('%s: ' %
1609 1609 ui.configsource(section, name, untrusted))
1610 1610 ui.write('%s=%s\n' % (sectname, value))
1611 1611 matched = True
1612 1612 if matched:
1613 1613 return 0
1614 1614 return 1
1615 1615
1616 1616 @command('copy|cp',
1617 1617 [('A', 'after', None, _('record a copy that has already occurred')),
1618 1618 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1619 1619 ] + walkopts + dryrunopts,
1620 1620 _('[OPTION]... [SOURCE]... DEST'))
1621 1621 def copy(ui, repo, *pats, **opts):
1622 1622 """mark files as copied for the next commit
1623 1623
1624 1624 Mark dest as having copies of source files. If dest is a
1625 1625 directory, copies are put in that directory. If dest is a file,
1626 1626 the source must be a single file.
1627 1627
1628 1628 By default, this command copies the contents of files as they
1629 1629 exist in the working directory. If invoked with -A/--after, the
1630 1630 operation is recorded, but no copying is performed.
1631 1631
1632 1632 This command takes effect with the next commit. To undo a copy
1633 1633 before that, see :hg:`revert`.
1634 1634
1635 1635 Returns 0 on success, 1 if errors are encountered.
1636 1636 """
1637 1637 wlock = repo.wlock(False)
1638 1638 try:
1639 1639 return cmdutil.copy(ui, repo, pats, opts)
1640 1640 finally:
1641 1641 wlock.release()
1642 1642
1643 1643 @command('debugancestor', [], _('[INDEX] REV1 REV2'), optionalrepo=True)
1644 1644 def debugancestor(ui, repo, *args):
1645 1645 """find the ancestor revision of two revisions in a given index"""
1646 1646 if len(args) == 3:
1647 1647 index, rev1, rev2 = args
1648 1648 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1649 1649 lookup = r.lookup
1650 1650 elif len(args) == 2:
1651 1651 if not repo:
1652 1652 raise util.Abort(_("there is no Mercurial repository here "
1653 1653 "(.hg not found)"))
1654 1654 rev1, rev2 = args
1655 1655 r = repo.changelog
1656 1656 lookup = repo.lookup
1657 1657 else:
1658 1658 raise util.Abort(_('either two or three arguments required'))
1659 1659 a = r.ancestor(lookup(rev1), lookup(rev2))
1660 1660 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1661 1661
1662 1662 @command('debugbuilddag',
1663 1663 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1664 1664 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1665 1665 ('n', 'new-file', None, _('add new file at each rev'))],
1666 1666 _('[OPTION]... [TEXT]'))
1667 1667 def debugbuilddag(ui, repo, text=None,
1668 1668 mergeable_file=False,
1669 1669 overwritten_file=False,
1670 1670 new_file=False):
1671 1671 """builds a repo with a given DAG from scratch in the current empty repo
1672 1672
1673 1673 The description of the DAG is read from stdin if not given on the
1674 1674 command line.
1675 1675
1676 1676 Elements:
1677 1677
1678 1678 - "+n" is a linear run of n nodes based on the current default parent
1679 1679 - "." is a single node based on the current default parent
1680 1680 - "$" resets the default parent to null (implied at the start);
1681 1681 otherwise the default parent is always the last node created
1682 1682 - "<p" sets the default parent to the backref p
1683 1683 - "*p" is a fork at parent p, which is a backref
1684 1684 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1685 1685 - "/p2" is a merge of the preceding node and p2
1686 1686 - ":tag" defines a local tag for the preceding node
1687 1687 - "@branch" sets the named branch for subsequent nodes
1688 1688 - "#...\\n" is a comment up to the end of the line
1689 1689
1690 1690 Whitespace between the above elements is ignored.
1691 1691
1692 1692 A backref is either
1693 1693
1694 1694 - a number n, which references the node curr-n, where curr is the current
1695 1695 node, or
1696 1696 - the name of a local tag you placed earlier using ":tag", or
1697 1697 - empty to denote the default parent.
1698 1698
1699 1699 All string valued-elements are either strictly alphanumeric, or must
1700 1700 be enclosed in double quotes ("..."), with "\\" as escape character.
1701 1701 """
1702 1702
1703 1703 if text is None:
1704 1704 ui.status(_("reading DAG from stdin\n"))
1705 1705 text = ui.fin.read()
1706 1706
1707 1707 cl = repo.changelog
1708 1708 if len(cl) > 0:
1709 1709 raise util.Abort(_('repository is not empty'))
1710 1710
1711 1711 # determine number of revs in DAG
1712 1712 total = 0
1713 1713 for type, data in dagparser.parsedag(text):
1714 1714 if type == 'n':
1715 1715 total += 1
1716 1716
1717 1717 if mergeable_file:
1718 1718 linesperrev = 2
1719 1719 # make a file with k lines per rev
1720 1720 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1721 1721 initialmergedlines.append("")
1722 1722
1723 1723 tags = []
1724 1724
1725 1725 lock = tr = None
1726 1726 try:
1727 1727 lock = repo.lock()
1728 1728 tr = repo.transaction("builddag")
1729 1729
1730 1730 at = -1
1731 1731 atbranch = 'default'
1732 1732 nodeids = []
1733 1733 id = 0
1734 1734 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1735 1735 for type, data in dagparser.parsedag(text):
1736 1736 if type == 'n':
1737 1737 ui.note(('node %s\n' % str(data)))
1738 1738 id, ps = data
1739 1739
1740 1740 files = []
1741 1741 fctxs = {}
1742 1742
1743 1743 p2 = None
1744 1744 if mergeable_file:
1745 1745 fn = "mf"
1746 1746 p1 = repo[ps[0]]
1747 1747 if len(ps) > 1:
1748 1748 p2 = repo[ps[1]]
1749 1749 pa = p1.ancestor(p2)
1750 1750 base, local, other = [x[fn].data() for x in (pa, p1,
1751 1751 p2)]
1752 1752 m3 = simplemerge.Merge3Text(base, local, other)
1753 1753 ml = [l.strip() for l in m3.merge_lines()]
1754 1754 ml.append("")
1755 1755 elif at > 0:
1756 1756 ml = p1[fn].data().split("\n")
1757 1757 else:
1758 1758 ml = initialmergedlines
1759 1759 ml[id * linesperrev] += " r%i" % id
1760 1760 mergedtext = "\n".join(ml)
1761 1761 files.append(fn)
1762 1762 fctxs[fn] = context.memfilectx(repo, fn, mergedtext)
1763 1763
1764 1764 if overwritten_file:
1765 1765 fn = "of"
1766 1766 files.append(fn)
1767 1767 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
1768 1768
1769 1769 if new_file:
1770 1770 fn = "nf%i" % id
1771 1771 files.append(fn)
1772 1772 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
1773 1773 if len(ps) > 1:
1774 1774 if not p2:
1775 1775 p2 = repo[ps[1]]
1776 1776 for fn in p2:
1777 1777 if fn.startswith("nf"):
1778 1778 files.append(fn)
1779 1779 fctxs[fn] = p2[fn]
1780 1780
1781 1781 def fctxfn(repo, cx, path):
1782 1782 return fctxs.get(path)
1783 1783
1784 1784 if len(ps) == 0 or ps[0] < 0:
1785 1785 pars = [None, None]
1786 1786 elif len(ps) == 1:
1787 1787 pars = [nodeids[ps[0]], None]
1788 1788 else:
1789 1789 pars = [nodeids[p] for p in ps]
1790 1790 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1791 1791 date=(id, 0),
1792 1792 user="debugbuilddag",
1793 1793 extra={'branch': atbranch})
1794 1794 nodeid = repo.commitctx(cx)
1795 1795 nodeids.append(nodeid)
1796 1796 at = id
1797 1797 elif type == 'l':
1798 1798 id, name = data
1799 1799 ui.note(('tag %s\n' % name))
1800 1800 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1801 1801 elif type == 'a':
1802 1802 ui.note(('branch %s\n' % data))
1803 1803 atbranch = data
1804 1804 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1805 1805 tr.close()
1806 1806
1807 1807 if tags:
1808 1808 repo.vfs.write("localtags", "".join(tags))
1809 1809 finally:
1810 1810 ui.progress(_('building'), None)
1811 1811 release(tr, lock)
1812 1812
1813 1813 @command('debugbundle',
1814 1814 [('a', 'all', None, _('show all details'))],
1815 1815 _('FILE'),
1816 1816 norepo=True)
1817 1817 def debugbundle(ui, bundlepath, all=None, **opts):
1818 1818 """lists the contents of a bundle"""
1819 1819 f = hg.openpath(ui, bundlepath)
1820 1820 try:
1821 1821 gen = exchange.readbundle(ui, f, bundlepath)
1822 1822 if isinstance(gen, bundle2.unbundle20):
1823 1823 return _debugbundle2(ui, gen, all=all, **opts)
1824 1824 if all:
1825 1825 ui.write(("format: id, p1, p2, cset, delta base, len(delta)\n"))
1826 1826
1827 1827 def showchunks(named):
1828 1828 ui.write("\n%s\n" % named)
1829 1829 chain = None
1830 1830 while True:
1831 1831 chunkdata = gen.deltachunk(chain)
1832 1832 if not chunkdata:
1833 1833 break
1834 1834 node = chunkdata['node']
1835 1835 p1 = chunkdata['p1']
1836 1836 p2 = chunkdata['p2']
1837 1837 cs = chunkdata['cs']
1838 1838 deltabase = chunkdata['deltabase']
1839 1839 delta = chunkdata['delta']
1840 1840 ui.write("%s %s %s %s %s %s\n" %
1841 1841 (hex(node), hex(p1), hex(p2),
1842 1842 hex(cs), hex(deltabase), len(delta)))
1843 1843 chain = node
1844 1844
1845 1845 chunkdata = gen.changelogheader()
1846 1846 showchunks("changelog")
1847 1847 chunkdata = gen.manifestheader()
1848 1848 showchunks("manifest")
1849 1849 while True:
1850 1850 chunkdata = gen.filelogheader()
1851 1851 if not chunkdata:
1852 1852 break
1853 1853 fname = chunkdata['filename']
1854 1854 showchunks(fname)
1855 1855 else:
1856 1856 if isinstance(gen, bundle2.unbundle20):
1857 1857 raise util.Abort(_('use debugbundle2 for this file'))
1858 1858 chunkdata = gen.changelogheader()
1859 1859 chain = None
1860 1860 while True:
1861 1861 chunkdata = gen.deltachunk(chain)
1862 1862 if not chunkdata:
1863 1863 break
1864 1864 node = chunkdata['node']
1865 1865 ui.write("%s\n" % hex(node))
1866 1866 chain = node
1867 1867 finally:
1868 1868 f.close()
1869 1869
1870 1870 def _debugbundle2(ui, gen, **opts):
1871 1871 """lists the contents of a bundle2"""
1872 1872 if not isinstance(gen, bundle2.unbundle20):
1873 1873 raise util.Abort(_('not a bundle2 file'))
1874 1874 ui.write(('Stream params: %s\n' % repr(gen.params)))
1875 1875 for part in gen.iterparts():
1876 1876 ui.write('%s -- %r\n' % (part.type, repr(part.params)))
1877 1877 if part.type == 'b2x:changegroup':
1878 1878 version = part.params.get('version', '01')
1879 1879 cg = changegroup.packermap[version][1](part, 'UN')
1880 1880 chunkdata = cg.changelogheader()
1881 1881 chain = None
1882 1882 while True:
1883 1883 chunkdata = cg.deltachunk(chain)
1884 1884 if not chunkdata:
1885 1885 break
1886 1886 node = chunkdata['node']
1887 1887 ui.write(" %s\n" % hex(node))
1888 1888 chain = node
1889 1889
1890 1890 @command('debugcheckstate', [], '')
1891 1891 def debugcheckstate(ui, repo):
1892 1892 """validate the correctness of the current dirstate"""
1893 1893 parent1, parent2 = repo.dirstate.parents()
1894 1894 m1 = repo[parent1].manifest()
1895 1895 m2 = repo[parent2].manifest()
1896 1896 errors = 0
1897 1897 for f in repo.dirstate:
1898 1898 state = repo.dirstate[f]
1899 1899 if state in "nr" and f not in m1:
1900 1900 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1901 1901 errors += 1
1902 1902 if state in "a" and f in m1:
1903 1903 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1904 1904 errors += 1
1905 1905 if state in "m" and f not in m1 and f not in m2:
1906 1906 ui.warn(_("%s in state %s, but not in either manifest\n") %
1907 1907 (f, state))
1908 1908 errors += 1
1909 1909 for f in m1:
1910 1910 state = repo.dirstate[f]
1911 1911 if state not in "nrm":
1912 1912 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1913 1913 errors += 1
1914 1914 if errors:
1915 1915 error = _(".hg/dirstate inconsistent with current parent's manifest")
1916 1916 raise util.Abort(error)
1917 1917
1918 1918 @command('debugcommands', [], _('[COMMAND]'), norepo=True)
1919 1919 def debugcommands(ui, cmd='', *args):
1920 1920 """list all available commands and options"""
1921 1921 for cmd, vals in sorted(table.iteritems()):
1922 1922 cmd = cmd.split('|')[0].strip('^')
1923 1923 opts = ', '.join([i[1] for i in vals[1]])
1924 1924 ui.write('%s: %s\n' % (cmd, opts))
1925 1925
1926 1926 @command('debugcomplete',
1927 1927 [('o', 'options', None, _('show the command options'))],
1928 1928 _('[-o] CMD'),
1929 1929 norepo=True)
1930 1930 def debugcomplete(ui, cmd='', **opts):
1931 1931 """returns the completion list associated with the given command"""
1932 1932
1933 1933 if opts.get('options'):
1934 1934 options = []
1935 1935 otables = [globalopts]
1936 1936 if cmd:
1937 1937 aliases, entry = cmdutil.findcmd(cmd, table, False)
1938 1938 otables.append(entry[1])
1939 1939 for t in otables:
1940 1940 for o in t:
1941 1941 if "(DEPRECATED)" in o[3]:
1942 1942 continue
1943 1943 if o[0]:
1944 1944 options.append('-%s' % o[0])
1945 1945 options.append('--%s' % o[1])
1946 1946 ui.write("%s\n" % "\n".join(options))
1947 1947 return
1948 1948
1949 1949 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1950 1950 if ui.verbose:
1951 1951 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1952 1952 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1953 1953
1954 1954 @command('debugdag',
1955 1955 [('t', 'tags', None, _('use tags as labels')),
1956 1956 ('b', 'branches', None, _('annotate with branch names')),
1957 1957 ('', 'dots', None, _('use dots for runs')),
1958 1958 ('s', 'spaces', None, _('separate elements by spaces'))],
1959 1959 _('[OPTION]... [FILE [REV]...]'),
1960 1960 optionalrepo=True)
1961 1961 def debugdag(ui, repo, file_=None, *revs, **opts):
1962 1962 """format the changelog or an index DAG as a concise textual description
1963 1963
1964 1964 If you pass a revlog index, the revlog's DAG is emitted. If you list
1965 1965 revision numbers, they get labeled in the output as rN.
1966 1966
1967 1967 Otherwise, the changelog DAG of the current repo is emitted.
1968 1968 """
1969 1969 spaces = opts.get('spaces')
1970 1970 dots = opts.get('dots')
1971 1971 if file_:
1972 1972 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1973 1973 revs = set((int(r) for r in revs))
1974 1974 def events():
1975 1975 for r in rlog:
1976 1976 yield 'n', (r, list(p for p in rlog.parentrevs(r)
1977 1977 if p != -1))
1978 1978 if r in revs:
1979 1979 yield 'l', (r, "r%i" % r)
1980 1980 elif repo:
1981 1981 cl = repo.changelog
1982 1982 tags = opts.get('tags')
1983 1983 branches = opts.get('branches')
1984 1984 if tags:
1985 1985 labels = {}
1986 1986 for l, n in repo.tags().items():
1987 1987 labels.setdefault(cl.rev(n), []).append(l)
1988 1988 def events():
1989 1989 b = "default"
1990 1990 for r in cl:
1991 1991 if branches:
1992 1992 newb = cl.read(cl.node(r))[5]['branch']
1993 1993 if newb != b:
1994 1994 yield 'a', newb
1995 1995 b = newb
1996 1996 yield 'n', (r, list(p for p in cl.parentrevs(r)
1997 1997 if p != -1))
1998 1998 if tags:
1999 1999 ls = labels.get(r)
2000 2000 if ls:
2001 2001 for l in ls:
2002 2002 yield 'l', (r, l)
2003 2003 else:
2004 2004 raise util.Abort(_('need repo for changelog dag'))
2005 2005
2006 2006 for line in dagparser.dagtextlines(events(),
2007 2007 addspaces=spaces,
2008 2008 wraplabels=True,
2009 2009 wrapannotations=True,
2010 2010 wrapnonlinear=dots,
2011 2011 usedots=dots,
2012 2012 maxlinewidth=70):
2013 2013 ui.write(line)
2014 2014 ui.write("\n")
2015 2015
2016 2016 @command('debugdata',
2017 2017 [('c', 'changelog', False, _('open changelog')),
2018 2018 ('m', 'manifest', False, _('open manifest'))],
2019 2019 _('-c|-m|FILE REV'))
2020 2020 def debugdata(ui, repo, file_, rev=None, **opts):
2021 2021 """dump the contents of a data file revision"""
2022 2022 if opts.get('changelog') or opts.get('manifest'):
2023 2023 file_, rev = None, file_
2024 2024 elif rev is None:
2025 2025 raise error.CommandError('debugdata', _('invalid arguments'))
2026 2026 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
2027 2027 try:
2028 2028 ui.write(r.revision(r.lookup(rev)))
2029 2029 except KeyError:
2030 2030 raise util.Abort(_('invalid revision identifier %s') % rev)
2031 2031
2032 2032 @command('debugdate',
2033 2033 [('e', 'extended', None, _('try extended date formats'))],
2034 2034 _('[-e] DATE [RANGE]'),
2035 2035 norepo=True, optionalrepo=True)
2036 2036 def debugdate(ui, date, range=None, **opts):
2037 2037 """parse and display a date"""
2038 2038 if opts["extended"]:
2039 2039 d = util.parsedate(date, util.extendeddateformats)
2040 2040 else:
2041 2041 d = util.parsedate(date)
2042 2042 ui.write(("internal: %s %s\n") % d)
2043 2043 ui.write(("standard: %s\n") % util.datestr(d))
2044 2044 if range:
2045 2045 m = util.matchdate(range)
2046 2046 ui.write(("match: %s\n") % m(d[0]))
2047 2047
2048 2048 @command('debugdiscovery',
2049 2049 [('', 'old', None, _('use old-style discovery')),
2050 2050 ('', 'nonheads', None,
2051 2051 _('use old-style discovery with non-heads included')),
2052 2052 ] + remoteopts,
2053 2053 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
2054 2054 def debugdiscovery(ui, repo, remoteurl="default", **opts):
2055 2055 """runs the changeset discovery protocol in isolation"""
2056 2056 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl),
2057 2057 opts.get('branch'))
2058 2058 remote = hg.peer(repo, opts, remoteurl)
2059 2059 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
2060 2060
2061 2061 # make sure tests are repeatable
2062 2062 random.seed(12323)
2063 2063
2064 2064 def doit(localheads, remoteheads, remote=remote):
2065 2065 if opts.get('old'):
2066 2066 if localheads:
2067 2067 raise util.Abort('cannot use localheads with old style '
2068 2068 'discovery')
2069 2069 if not util.safehasattr(remote, 'branches'):
2070 2070 # enable in-client legacy support
2071 2071 remote = localrepo.locallegacypeer(remote.local())
2072 2072 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
2073 2073 force=True)
2074 2074 common = set(common)
2075 2075 if not opts.get('nonheads'):
2076 2076 ui.write(("unpruned common: %s\n") %
2077 2077 " ".join(sorted(short(n) for n in common)))
2078 2078 dag = dagutil.revlogdag(repo.changelog)
2079 2079 all = dag.ancestorset(dag.internalizeall(common))
2080 2080 common = dag.externalizeall(dag.headsetofconnecteds(all))
2081 2081 else:
2082 2082 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
2083 2083 common = set(common)
2084 2084 rheads = set(hds)
2085 2085 lheads = set(repo.heads())
2086 2086 ui.write(("common heads: %s\n") %
2087 2087 " ".join(sorted(short(n) for n in common)))
2088 2088 if lheads <= common:
2089 2089 ui.write(("local is subset\n"))
2090 2090 elif rheads <= common:
2091 2091 ui.write(("remote is subset\n"))
2092 2092
2093 2093 serverlogs = opts.get('serverlog')
2094 2094 if serverlogs:
2095 2095 for filename in serverlogs:
2096 2096 logfile = open(filename, 'r')
2097 2097 try:
2098 2098 line = logfile.readline()
2099 2099 while line:
2100 2100 parts = line.strip().split(';')
2101 2101 op = parts[1]
2102 2102 if op == 'cg':
2103 2103 pass
2104 2104 elif op == 'cgss':
2105 2105 doit(parts[2].split(' '), parts[3].split(' '))
2106 2106 elif op == 'unb':
2107 2107 doit(parts[3].split(' '), parts[2].split(' '))
2108 2108 line = logfile.readline()
2109 2109 finally:
2110 2110 logfile.close()
2111 2111
2112 2112 else:
2113 2113 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
2114 2114 opts.get('remote_head'))
2115 2115 localrevs = opts.get('local_head')
2116 2116 doit(localrevs, remoterevs)
2117 2117
2118 2118 @command('debugfileset',
2119 2119 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
2120 2120 _('[-r REV] FILESPEC'))
2121 2121 def debugfileset(ui, repo, expr, **opts):
2122 2122 '''parse and apply a fileset specification'''
2123 2123 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
2124 2124 if ui.verbose:
2125 2125 tree = fileset.parse(expr)[0]
2126 2126 ui.note(tree, "\n")
2127 2127
2128 2128 for f in ctx.getfileset(expr):
2129 2129 ui.write("%s\n" % f)
2130 2130
2131 2131 @command('debugfsinfo', [], _('[PATH]'), norepo=True)
2132 2132 def debugfsinfo(ui, path="."):
2133 2133 """show information detected about current filesystem"""
2134 2134 util.writefile('.debugfsinfo', '')
2135 2135 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
2136 2136 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
2137 2137 ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no'))
2138 2138 ui.write(('case-sensitive: %s\n') % (util.checkcase('.debugfsinfo')
2139 2139 and 'yes' or 'no'))
2140 2140 os.unlink('.debugfsinfo')
2141 2141
2142 2142 @command('debuggetbundle',
2143 2143 [('H', 'head', [], _('id of head node'), _('ID')),
2144 2144 ('C', 'common', [], _('id of common node'), _('ID')),
2145 2145 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
2146 2146 _('REPO FILE [-H|-C ID]...'),
2147 2147 norepo=True)
2148 2148 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
2149 2149 """retrieves a bundle from a repo
2150 2150
2151 2151 Every ID must be a full-length hex node id string. Saves the bundle to the
2152 2152 given file.
2153 2153 """
2154 2154 repo = hg.peer(ui, opts, repopath)
2155 2155 if not repo.capable('getbundle'):
2156 2156 raise util.Abort("getbundle() not supported by target repository")
2157 2157 args = {}
2158 2158 if common:
2159 2159 args['common'] = [bin(s) for s in common]
2160 2160 if head:
2161 2161 args['heads'] = [bin(s) for s in head]
2162 2162 # TODO: get desired bundlecaps from command line.
2163 2163 args['bundlecaps'] = None
2164 2164 bundle = repo.getbundle('debug', **args)
2165 2165
2166 2166 bundletype = opts.get('type', 'bzip2').lower()
2167 2167 btypes = {'none': 'HG10UN',
2168 2168 'bzip2': 'HG10BZ',
2169 2169 'gzip': 'HG10GZ',
2170 2170 'bundle2': 'HG2Y'}
2171 2171 bundletype = btypes.get(bundletype)
2172 2172 if bundletype not in changegroup.bundletypes:
2173 2173 raise util.Abort(_('unknown bundle type specified with --type'))
2174 2174 changegroup.writebundle(ui, bundle, bundlepath, bundletype)
2175 2175
2176 2176 @command('debugignore', [], '')
2177 2177 def debugignore(ui, repo, *values, **opts):
2178 2178 """display the combined ignore pattern"""
2179 2179 ignore = repo.dirstate._ignore
2180 2180 includepat = getattr(ignore, 'includepat', None)
2181 2181 if includepat is not None:
2182 2182 ui.write("%s\n" % includepat)
2183 2183 else:
2184 2184 raise util.Abort(_("no ignore patterns found"))
2185 2185
2186 2186 @command('debugindex',
2187 2187 [('c', 'changelog', False, _('open changelog')),
2188 2188 ('m', 'manifest', False, _('open manifest')),
2189 2189 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
2190 2190 _('[-f FORMAT] -c|-m|FILE'),
2191 2191 optionalrepo=True)
2192 2192 def debugindex(ui, repo, file_=None, **opts):
2193 2193 """dump the contents of an index file"""
2194 2194 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
2195 2195 format = opts.get('format', 0)
2196 2196 if format not in (0, 1):
2197 2197 raise util.Abort(_("unknown format %d") % format)
2198 2198
2199 2199 generaldelta = r.version & revlog.REVLOGGENERALDELTA
2200 2200 if generaldelta:
2201 2201 basehdr = ' delta'
2202 2202 else:
2203 2203 basehdr = ' base'
2204 2204
2205 2205 if ui.debugflag:
2206 2206 shortfn = hex
2207 2207 else:
2208 2208 shortfn = short
2209 2209
2210 2210 # There might not be anything in r, so have a sane default
2211 2211 idlen = 12
2212 2212 for i in r:
2213 2213 idlen = len(shortfn(r.node(i)))
2214 2214 break
2215 2215
2216 2216 if format == 0:
2217 2217 ui.write(" rev offset length " + basehdr + " linkrev"
2218 2218 " %s %s p2\n" % ("nodeid".ljust(idlen), "p1".ljust(idlen)))
2219 2219 elif format == 1:
2220 2220 ui.write(" rev flag offset length"
2221 2221 " size " + basehdr + " link p1 p2"
2222 2222 " %s\n" % "nodeid".rjust(idlen))
2223 2223
2224 2224 for i in r:
2225 2225 node = r.node(i)
2226 2226 if generaldelta:
2227 2227 base = r.deltaparent(i)
2228 2228 else:
2229 2229 base = r.chainbase(i)
2230 2230 if format == 0:
2231 2231 try:
2232 2232 pp = r.parents(node)
2233 2233 except Exception:
2234 2234 pp = [nullid, nullid]
2235 2235 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
2236 2236 i, r.start(i), r.length(i), base, r.linkrev(i),
2237 2237 shortfn(node), shortfn(pp[0]), shortfn(pp[1])))
2238 2238 elif format == 1:
2239 2239 pr = r.parentrevs(i)
2240 2240 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
2241 2241 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
2242 2242 base, r.linkrev(i), pr[0], pr[1], shortfn(node)))
2243 2243
2244 2244 @command('debugindexdot', [], _('FILE'), optionalrepo=True)
2245 2245 def debugindexdot(ui, repo, file_):
2246 2246 """dump an index DAG as a graphviz dot file"""
2247 2247 r = None
2248 2248 if repo:
2249 2249 filelog = repo.file(file_)
2250 2250 if len(filelog):
2251 2251 r = filelog
2252 2252 if not r:
2253 2253 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
2254 2254 ui.write(("digraph G {\n"))
2255 2255 for i in r:
2256 2256 node = r.node(i)
2257 2257 pp = r.parents(node)
2258 2258 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
2259 2259 if pp[1] != nullid:
2260 2260 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
2261 2261 ui.write("}\n")
2262 2262
2263 2263 @command('debuginstall', [], '', norepo=True)
2264 2264 def debuginstall(ui):
2265 2265 '''test Mercurial installation
2266 2266
2267 2267 Returns 0 on success.
2268 2268 '''
2269 2269
2270 2270 def writetemp(contents):
2271 2271 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
2272 2272 f = os.fdopen(fd, "wb")
2273 2273 f.write(contents)
2274 2274 f.close()
2275 2275 return name
2276 2276
2277 2277 problems = 0
2278 2278
2279 2279 # encoding
2280 2280 ui.status(_("checking encoding (%s)...\n") % encoding.encoding)
2281 2281 try:
2282 2282 encoding.fromlocal("test")
2283 2283 except util.Abort, inst:
2284 2284 ui.write(" %s\n" % inst)
2285 2285 ui.write(_(" (check that your locale is properly set)\n"))
2286 2286 problems += 1
2287 2287
2288 2288 # Python
2289 2289 ui.status(_("checking Python executable (%s)\n") % sys.executable)
2290 2290 ui.status(_("checking Python version (%s)\n")
2291 2291 % ("%s.%s.%s" % sys.version_info[:3]))
2292 2292 ui.status(_("checking Python lib (%s)...\n")
2293 2293 % os.path.dirname(os.__file__))
2294 2294
2295 2295 # compiled modules
2296 2296 ui.status(_("checking installed modules (%s)...\n")
2297 2297 % os.path.dirname(__file__))
2298 2298 try:
2299 2299 import bdiff, mpatch, base85, osutil
2300 2300 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
2301 2301 except Exception, inst:
2302 2302 ui.write(" %s\n" % inst)
2303 2303 ui.write(_(" One or more extensions could not be found"))
2304 2304 ui.write(_(" (check that you compiled the extensions)\n"))
2305 2305 problems += 1
2306 2306
2307 2307 # templates
2308 2308 import templater
2309 2309 p = templater.templatepaths()
2310 2310 ui.status(_("checking templates (%s)...\n") % ' '.join(p))
2311 2311 if p:
2312 2312 m = templater.templatepath("map-cmdline.default")
2313 2313 if m:
2314 2314 # template found, check if it is working
2315 2315 try:
2316 2316 templater.templater(m)
2317 2317 except Exception, inst:
2318 2318 ui.write(" %s\n" % inst)
2319 2319 p = None
2320 2320 else:
2321 2321 ui.write(_(" template 'default' not found\n"))
2322 2322 p = None
2323 2323 else:
2324 2324 ui.write(_(" no template directories found\n"))
2325 2325 if not p:
2326 2326 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
2327 2327 problems += 1
2328 2328
2329 2329 # editor
2330 2330 ui.status(_("checking commit editor...\n"))
2331 2331 editor = ui.geteditor()
2332 2332 cmdpath = util.findexe(shlex.split(editor)[0])
2333 2333 if not cmdpath:
2334 2334 if editor == 'vi':
2335 2335 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
2336 2336 ui.write(_(" (specify a commit editor in your configuration"
2337 2337 " file)\n"))
2338 2338 else:
2339 2339 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
2340 2340 ui.write(_(" (specify a commit editor in your configuration"
2341 2341 " file)\n"))
2342 2342 problems += 1
2343 2343
2344 2344 # check username
2345 2345 ui.status(_("checking username...\n"))
2346 2346 try:
2347 2347 ui.username()
2348 2348 except util.Abort, e:
2349 2349 ui.write(" %s\n" % e)
2350 2350 ui.write(_(" (specify a username in your configuration file)\n"))
2351 2351 problems += 1
2352 2352
2353 2353 if not problems:
2354 2354 ui.status(_("no problems detected\n"))
2355 2355 else:
2356 2356 ui.write(_("%s problems detected,"
2357 2357 " please check your install!\n") % problems)
2358 2358
2359 2359 return problems
2360 2360
2361 2361 @command('debugknown', [], _('REPO ID...'), norepo=True)
2362 2362 def debugknown(ui, repopath, *ids, **opts):
2363 2363 """test whether node ids are known to a repo
2364 2364
2365 2365 Every ID must be a full-length hex node id string. Returns a list of 0s
2366 2366 and 1s indicating unknown/known.
2367 2367 """
2368 2368 repo = hg.peer(ui, opts, repopath)
2369 2369 if not repo.capable('known'):
2370 2370 raise util.Abort("known() not supported by target repository")
2371 2371 flags = repo.known([bin(s) for s in ids])
2372 2372 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2373 2373
2374 2374 @command('debuglabelcomplete', [], _('LABEL...'))
2375 2375 def debuglabelcomplete(ui, repo, *args):
2376 2376 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
2377 2377 debugnamecomplete(ui, repo, *args)
2378 2378
2379 2379 @command('debugnamecomplete', [], _('NAME...'))
2380 2380 def debugnamecomplete(ui, repo, *args):
2381 2381 '''complete "names" - tags, open branch names, bookmark names'''
2382 2382
2383 2383 names = set()
2384 2384 # since we previously only listed open branches, we will handle that
2385 2385 # specially (after this for loop)
2386 2386 for name, ns in repo.names.iteritems():
2387 2387 if name != 'branches':
2388 2388 names.update(ns.listnames(repo))
2389 2389 names.update(tag for (tag, heads, tip, closed)
2390 2390 in repo.branchmap().iterbranches() if not closed)
2391 2391 completions = set()
2392 2392 if not args:
2393 2393 args = ['']
2394 2394 for a in args:
2395 2395 completions.update(n for n in names if n.startswith(a))
2396 2396 ui.write('\n'.join(sorted(completions)))
2397 2397 ui.write('\n')
2398 2398
2399 2399 @command('debuglocks',
2400 2400 [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
2401 2401 ('W', 'force-wlock', None,
2402 2402 _('free the working state lock (DANGEROUS)'))],
2403 2403 _('[OPTION]...'))
2404 2404 def debuglocks(ui, repo, **opts):
2405 2405 """show or modify state of locks
2406 2406
2407 2407 By default, this command will show which locks are held. This
2408 2408 includes the user and process holding the lock, the amount of time
2409 2409 the lock has been held, and the machine name where the process is
2410 2410 running if it's not local.
2411 2411
2412 2412 Locks protect the integrity of Mercurial's data, so should be
2413 2413 treated with care. System crashes or other interruptions may cause
2414 2414 locks to not be properly released, though Mercurial will usually
2415 2415 detect and remove such stale locks automatically.
2416 2416
2417 2417 However, detecting stale locks may not always be possible (for
2418 2418 instance, on a shared filesystem). Removing locks may also be
2419 2419 blocked by filesystem permissions.
2420 2420
2421 2421 Returns 0 if no locks are held.
2422 2422
2423 2423 """
2424 2424
2425 2425 if opts.get('force_lock'):
2426 2426 repo.svfs.unlink('lock')
2427 2427 if opts.get('force_wlock'):
2428 2428 repo.vfs.unlink('wlock')
2429 2429 if opts.get('force_lock') or opts.get('force_lock'):
2430 2430 return 0
2431 2431
2432 2432 now = time.time()
2433 2433 held = 0
2434 2434
2435 2435 def report(vfs, name, method):
2436 2436 # this causes stale locks to get reaped for more accurate reporting
2437 2437 try:
2438 2438 l = method(False)
2439 2439 except error.LockHeld:
2440 2440 l = None
2441 2441
2442 2442 if l:
2443 2443 l.release()
2444 2444 else:
2445 2445 try:
2446 2446 stat = repo.svfs.lstat(name)
2447 2447 age = now - stat.st_mtime
2448 2448 user = util.username(stat.st_uid)
2449 2449 locker = vfs.readlock(name)
2450 2450 if ":" in locker:
2451 2451 host, pid = locker.split(':')
2452 2452 if host == socket.gethostname():
2453 2453 locker = 'user %s, process %s' % (user, pid)
2454 2454 else:
2455 2455 locker = 'user %s, process %s, host %s' \
2456 2456 % (user, pid, host)
2457 2457 ui.write("%-6s %s (%ds)\n" % (name + ":", locker, age))
2458 2458 return 1
2459 2459 except OSError, e:
2460 2460 if e.errno != errno.ENOENT:
2461 2461 raise
2462 2462
2463 2463 ui.write("%-6s free\n" % (name + ":"))
2464 2464 return 0
2465 2465
2466 2466 held += report(repo.svfs, "lock", repo.lock)
2467 2467 held += report(repo.vfs, "wlock", repo.wlock)
2468 2468
2469 2469 return held
2470 2470
2471 2471 @command('debugobsolete',
2472 2472 [('', 'flags', 0, _('markers flag')),
2473 2473 ('', 'record-parents', False,
2474 2474 _('record parent information for the precursor')),
2475 2475 ('r', 'rev', [], _('display markers relevant to REV')),
2476 2476 ] + commitopts2,
2477 2477 _('[OBSOLETED [REPLACEMENT] [REPL... ]'))
2478 2478 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
2479 2479 """create arbitrary obsolete marker
2480 2480
2481 2481 With no arguments, displays the list of obsolescence markers."""
2482 2482
2483 2483 def parsenodeid(s):
2484 2484 try:
2485 2485 # We do not use revsingle/revrange functions here to accept
2486 2486 # arbitrary node identifiers, possibly not present in the
2487 2487 # local repository.
2488 2488 n = bin(s)
2489 2489 if len(n) != len(nullid):
2490 2490 raise TypeError()
2491 2491 return n
2492 2492 except TypeError:
2493 2493 raise util.Abort('changeset references must be full hexadecimal '
2494 2494 'node identifiers')
2495 2495
2496 2496 if precursor is not None:
2497 2497 if opts['rev']:
2498 2498 raise util.Abort('cannot select revision when creating marker')
2499 2499 metadata = {}
2500 2500 metadata['user'] = opts['user'] or ui.username()
2501 2501 succs = tuple(parsenodeid(succ) for succ in successors)
2502 2502 l = repo.lock()
2503 2503 try:
2504 2504 tr = repo.transaction('debugobsolete')
2505 2505 try:
2506 2506 try:
2507 2507 date = opts.get('date')
2508 2508 if date:
2509 2509 date = util.parsedate(date)
2510 2510 else:
2511 2511 date = None
2512 2512 prec = parsenodeid(precursor)
2513 2513 parents = None
2514 2514 if opts['record_parents']:
2515 2515 if prec not in repo.unfiltered():
2516 2516 raise util.Abort('cannot used --record-parents on '
2517 2517 'unknown changesets')
2518 2518 parents = repo.unfiltered()[prec].parents()
2519 2519 parents = tuple(p.node() for p in parents)
2520 2520 repo.obsstore.create(tr, prec, succs, opts['flags'],
2521 2521 parents=parents, date=date,
2522 2522 metadata=metadata)
2523 2523 tr.close()
2524 2524 except ValueError, exc:
2525 2525 raise util.Abort(_('bad obsmarker input: %s') % exc)
2526 2526 finally:
2527 2527 tr.release()
2528 2528 finally:
2529 2529 l.release()
2530 2530 else:
2531 2531 if opts['rev']:
2532 2532 revs = scmutil.revrange(repo, opts['rev'])
2533 2533 nodes = [repo[r].node() for r in revs]
2534 2534 markers = list(obsolete.getmarkers(repo, nodes=nodes))
2535 2535 markers.sort(key=lambda x: x._data)
2536 2536 else:
2537 2537 markers = obsolete.getmarkers(repo)
2538 2538
2539 2539 for m in markers:
2540 2540 cmdutil.showmarker(ui, m)
2541 2541
2542 2542 @command('debugpathcomplete',
2543 2543 [('f', 'full', None, _('complete an entire path')),
2544 2544 ('n', 'normal', None, _('show only normal files')),
2545 2545 ('a', 'added', None, _('show only added files')),
2546 2546 ('r', 'removed', None, _('show only removed files'))],
2547 2547 _('FILESPEC...'))
2548 2548 def debugpathcomplete(ui, repo, *specs, **opts):
2549 2549 '''complete part or all of a tracked path
2550 2550
2551 2551 This command supports shells that offer path name completion. It
2552 2552 currently completes only files already known to the dirstate.
2553 2553
2554 2554 Completion extends only to the next path segment unless
2555 2555 --full is specified, in which case entire paths are used.'''
2556 2556
2557 2557 def complete(path, acceptable):
2558 2558 dirstate = repo.dirstate
2559 2559 spec = os.path.normpath(os.path.join(os.getcwd(), path))
2560 2560 rootdir = repo.root + os.sep
2561 2561 if spec != repo.root and not spec.startswith(rootdir):
2562 2562 return [], []
2563 2563 if os.path.isdir(spec):
2564 2564 spec += '/'
2565 2565 spec = spec[len(rootdir):]
2566 2566 fixpaths = os.sep != '/'
2567 2567 if fixpaths:
2568 2568 spec = spec.replace(os.sep, '/')
2569 2569 speclen = len(spec)
2570 2570 fullpaths = opts['full']
2571 2571 files, dirs = set(), set()
2572 2572 adddir, addfile = dirs.add, files.add
2573 2573 for f, st in dirstate.iteritems():
2574 2574 if f.startswith(spec) and st[0] in acceptable:
2575 2575 if fixpaths:
2576 2576 f = f.replace('/', os.sep)
2577 2577 if fullpaths:
2578 2578 addfile(f)
2579 2579 continue
2580 2580 s = f.find(os.sep, speclen)
2581 2581 if s >= 0:
2582 2582 adddir(f[:s])
2583 2583 else:
2584 2584 addfile(f)
2585 2585 return files, dirs
2586 2586
2587 2587 acceptable = ''
2588 2588 if opts['normal']:
2589 2589 acceptable += 'nm'
2590 2590 if opts['added']:
2591 2591 acceptable += 'a'
2592 2592 if opts['removed']:
2593 2593 acceptable += 'r'
2594 2594 cwd = repo.getcwd()
2595 2595 if not specs:
2596 2596 specs = ['.']
2597 2597
2598 2598 files, dirs = set(), set()
2599 2599 for spec in specs:
2600 2600 f, d = complete(spec, acceptable or 'nmar')
2601 2601 files.update(f)
2602 2602 dirs.update(d)
2603 2603 files.update(dirs)
2604 2604 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
2605 2605 ui.write('\n')
2606 2606
2607 2607 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
2608 2608 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2609 2609 '''access the pushkey key/value protocol
2610 2610
2611 2611 With two args, list the keys in the given namespace.
2612 2612
2613 2613 With five args, set a key to new if it currently is set to old.
2614 2614 Reports success or failure.
2615 2615 '''
2616 2616
2617 2617 target = hg.peer(ui, {}, repopath)
2618 2618 if keyinfo:
2619 2619 key, old, new = keyinfo
2620 2620 r = target.pushkey(namespace, key, old, new)
2621 2621 ui.status(str(r) + '\n')
2622 2622 return not r
2623 2623 else:
2624 2624 for k, v in sorted(target.listkeys(namespace).iteritems()):
2625 2625 ui.write("%s\t%s\n" % (k.encode('string-escape'),
2626 2626 v.encode('string-escape')))
2627 2627
2628 2628 @command('debugpvec', [], _('A B'))
2629 2629 def debugpvec(ui, repo, a, b=None):
2630 2630 ca = scmutil.revsingle(repo, a)
2631 2631 cb = scmutil.revsingle(repo, b)
2632 2632 pa = pvec.ctxpvec(ca)
2633 2633 pb = pvec.ctxpvec(cb)
2634 2634 if pa == pb:
2635 2635 rel = "="
2636 2636 elif pa > pb:
2637 2637 rel = ">"
2638 2638 elif pa < pb:
2639 2639 rel = "<"
2640 2640 elif pa | pb:
2641 2641 rel = "|"
2642 2642 ui.write(_("a: %s\n") % pa)
2643 2643 ui.write(_("b: %s\n") % pb)
2644 2644 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2645 2645 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
2646 2646 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
2647 2647 pa.distance(pb), rel))
2648 2648
2649 2649 @command('debugrebuilddirstate|debugrebuildstate',
2650 2650 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
2651 2651 _('[-r REV]'))
2652 2652 def debugrebuilddirstate(ui, repo, rev):
2653 2653 """rebuild the dirstate as it would look like for the given revision
2654 2654
2655 2655 If no revision is specified the first current parent will be used.
2656 2656
2657 2657 The dirstate will be set to the files of the given revision.
2658 2658 The actual working directory content or existing dirstate
2659 2659 information such as adds or removes is not considered.
2660 2660
2661 2661 One use of this command is to make the next :hg:`status` invocation
2662 2662 check the actual file content.
2663 2663 """
2664 2664 ctx = scmutil.revsingle(repo, rev)
2665 2665 wlock = repo.wlock()
2666 2666 try:
2667 2667 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
2668 2668 finally:
2669 2669 wlock.release()
2670 2670
2671 2671 @command('debugrename',
2672 2672 [('r', 'rev', '', _('revision to debug'), _('REV'))],
2673 2673 _('[-r REV] FILE'))
2674 2674 def debugrename(ui, repo, file1, *pats, **opts):
2675 2675 """dump rename information"""
2676 2676
2677 2677 ctx = scmutil.revsingle(repo, opts.get('rev'))
2678 2678 m = scmutil.match(ctx, (file1,) + pats, opts)
2679 2679 for abs in ctx.walk(m):
2680 2680 fctx = ctx[abs]
2681 2681 o = fctx.filelog().renamed(fctx.filenode())
2682 2682 rel = m.rel(abs)
2683 2683 if o:
2684 2684 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2685 2685 else:
2686 2686 ui.write(_("%s not renamed\n") % rel)
2687 2687
2688 2688 @command('debugrevlog',
2689 2689 [('c', 'changelog', False, _('open changelog')),
2690 2690 ('m', 'manifest', False, _('open manifest')),
2691 2691 ('d', 'dump', False, _('dump index data'))],
2692 2692 _('-c|-m|FILE'),
2693 2693 optionalrepo=True)
2694 2694 def debugrevlog(ui, repo, file_=None, **opts):
2695 2695 """show data and statistics about a revlog"""
2696 2696 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
2697 2697
2698 2698 if opts.get("dump"):
2699 2699 numrevs = len(r)
2700 2700 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
2701 2701 " rawsize totalsize compression heads chainlen\n")
2702 2702 ts = 0
2703 2703 heads = set()
2704 2704
2705 2705 for rev in xrange(numrevs):
2706 2706 dbase = r.deltaparent(rev)
2707 2707 if dbase == -1:
2708 2708 dbase = rev
2709 2709 cbase = r.chainbase(rev)
2710 2710 clen = r.chainlen(rev)
2711 2711 p1, p2 = r.parentrevs(rev)
2712 2712 rs = r.rawsize(rev)
2713 2713 ts = ts + rs
2714 2714 heads -= set(r.parentrevs(rev))
2715 2715 heads.add(rev)
2716 2716 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
2717 2717 "%11d %5d %8d\n" %
2718 2718 (rev, p1, p2, r.start(rev), r.end(rev),
2719 2719 r.start(dbase), r.start(cbase),
2720 2720 r.start(p1), r.start(p2),
2721 2721 rs, ts, ts / r.end(rev), len(heads), clen))
2722 2722 return 0
2723 2723
2724 2724 v = r.version
2725 2725 format = v & 0xFFFF
2726 2726 flags = []
2727 2727 gdelta = False
2728 2728 if v & revlog.REVLOGNGINLINEDATA:
2729 2729 flags.append('inline')
2730 2730 if v & revlog.REVLOGGENERALDELTA:
2731 2731 gdelta = True
2732 2732 flags.append('generaldelta')
2733 2733 if not flags:
2734 2734 flags = ['(none)']
2735 2735
2736 2736 nummerges = 0
2737 2737 numfull = 0
2738 2738 numprev = 0
2739 2739 nump1 = 0
2740 2740 nump2 = 0
2741 2741 numother = 0
2742 2742 nump1prev = 0
2743 2743 nump2prev = 0
2744 2744 chainlengths = []
2745 2745
2746 2746 datasize = [None, 0, 0L]
2747 2747 fullsize = [None, 0, 0L]
2748 2748 deltasize = [None, 0, 0L]
2749 2749
2750 2750 def addsize(size, l):
2751 2751 if l[0] is None or size < l[0]:
2752 2752 l[0] = size
2753 2753 if size > l[1]:
2754 2754 l[1] = size
2755 2755 l[2] += size
2756 2756
2757 2757 numrevs = len(r)
2758 2758 for rev in xrange(numrevs):
2759 2759 p1, p2 = r.parentrevs(rev)
2760 2760 delta = r.deltaparent(rev)
2761 2761 if format > 0:
2762 2762 addsize(r.rawsize(rev), datasize)
2763 2763 if p2 != nullrev:
2764 2764 nummerges += 1
2765 2765 size = r.length(rev)
2766 2766 if delta == nullrev:
2767 2767 chainlengths.append(0)
2768 2768 numfull += 1
2769 2769 addsize(size, fullsize)
2770 2770 else:
2771 2771 chainlengths.append(chainlengths[delta] + 1)
2772 2772 addsize(size, deltasize)
2773 2773 if delta == rev - 1:
2774 2774 numprev += 1
2775 2775 if delta == p1:
2776 2776 nump1prev += 1
2777 2777 elif delta == p2:
2778 2778 nump2prev += 1
2779 2779 elif delta == p1:
2780 2780 nump1 += 1
2781 2781 elif delta == p2:
2782 2782 nump2 += 1
2783 2783 elif delta != nullrev:
2784 2784 numother += 1
2785 2785
2786 2786 # Adjust size min value for empty cases
2787 2787 for size in (datasize, fullsize, deltasize):
2788 2788 if size[0] is None:
2789 2789 size[0] = 0
2790 2790
2791 2791 numdeltas = numrevs - numfull
2792 2792 numoprev = numprev - nump1prev - nump2prev
2793 2793 totalrawsize = datasize[2]
2794 2794 datasize[2] /= numrevs
2795 2795 fulltotal = fullsize[2]
2796 2796 fullsize[2] /= numfull
2797 2797 deltatotal = deltasize[2]
2798 2798 if numrevs - numfull > 0:
2799 2799 deltasize[2] /= numrevs - numfull
2800 2800 totalsize = fulltotal + deltatotal
2801 2801 avgchainlen = sum(chainlengths) / numrevs
2802 2802 compratio = totalrawsize / totalsize
2803 2803
2804 2804 basedfmtstr = '%%%dd\n'
2805 2805 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2806 2806
2807 2807 def dfmtstr(max):
2808 2808 return basedfmtstr % len(str(max))
2809 2809 def pcfmtstr(max, padding=0):
2810 2810 return basepcfmtstr % (len(str(max)), ' ' * padding)
2811 2811
2812 2812 def pcfmt(value, total):
2813 2813 return (value, 100 * float(value) / total)
2814 2814
2815 2815 ui.write(('format : %d\n') % format)
2816 2816 ui.write(('flags : %s\n') % ', '.join(flags))
2817 2817
2818 2818 ui.write('\n')
2819 2819 fmt = pcfmtstr(totalsize)
2820 2820 fmt2 = dfmtstr(totalsize)
2821 2821 ui.write(('revisions : ') + fmt2 % numrevs)
2822 2822 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
2823 2823 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
2824 2824 ui.write(('revisions : ') + fmt2 % numrevs)
2825 2825 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
2826 2826 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
2827 2827 ui.write(('revision size : ') + fmt2 % totalsize)
2828 2828 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
2829 2829 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
2830 2830
2831 2831 ui.write('\n')
2832 2832 fmt = dfmtstr(max(avgchainlen, compratio))
2833 2833 ui.write(('avg chain length : ') + fmt % avgchainlen)
2834 2834 ui.write(('compression ratio : ') + fmt % compratio)
2835 2835
2836 2836 if format > 0:
2837 2837 ui.write('\n')
2838 2838 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
2839 2839 % tuple(datasize))
2840 2840 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
2841 2841 % tuple(fullsize))
2842 2842 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
2843 2843 % tuple(deltasize))
2844 2844
2845 2845 if numdeltas > 0:
2846 2846 ui.write('\n')
2847 2847 fmt = pcfmtstr(numdeltas)
2848 2848 fmt2 = pcfmtstr(numdeltas, 4)
2849 2849 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
2850 2850 if numprev > 0:
2851 2851 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
2852 2852 numprev))
2853 2853 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
2854 2854 numprev))
2855 2855 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
2856 2856 numprev))
2857 2857 if gdelta:
2858 2858 ui.write(('deltas against p1 : ')
2859 2859 + fmt % pcfmt(nump1, numdeltas))
2860 2860 ui.write(('deltas against p2 : ')
2861 2861 + fmt % pcfmt(nump2, numdeltas))
2862 2862 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
2863 2863 numdeltas))
2864 2864
2865 2865 @command('debugrevspec',
2866 2866 [('', 'optimize', None, _('print parsed tree after optimizing'))],
2867 2867 ('REVSPEC'))
2868 2868 def debugrevspec(ui, repo, expr, **opts):
2869 2869 """parse and apply a revision specification
2870 2870
2871 2871 Use --verbose to print the parsed tree before and after aliases
2872 2872 expansion.
2873 2873 """
2874 2874 if ui.verbose:
2875 2875 tree = revset.parse(expr)[0]
2876 2876 ui.note(revset.prettyformat(tree), "\n")
2877 2877 newtree = revset.findaliases(ui, tree)
2878 2878 if newtree != tree:
2879 2879 ui.note(revset.prettyformat(newtree), "\n")
2880 2880 tree = newtree
2881 2881 newtree = revset.foldconcat(tree)
2882 2882 if newtree != tree:
2883 2883 ui.note(revset.prettyformat(newtree), "\n")
2884 2884 if opts["optimize"]:
2885 2885 weight, optimizedtree = revset.optimize(newtree, True)
2886 2886 ui.note("* optimized:\n", revset.prettyformat(optimizedtree), "\n")
2887 2887 func = revset.match(ui, expr)
2888 2888 for c in func(repo):
2889 2889 ui.write("%s\n" % c)
2890 2890
2891 2891 @command('debugsetparents', [], _('REV1 [REV2]'))
2892 2892 def debugsetparents(ui, repo, rev1, rev2=None):
2893 2893 """manually set the parents of the current working directory
2894 2894
2895 2895 This is useful for writing repository conversion tools, but should
2896 2896 be used with care. For example, neither the working copy nor the dirstate
2897 2897 is updated, so file status may be incorrect after running this command.
2898 2898
2899 2899 Returns 0 on success.
2900 2900 """
2901 2901
2902 2902 r1 = scmutil.revsingle(repo, rev1).node()
2903 2903 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2904 2904
2905 2905 wlock = repo.wlock()
2906 2906 try:
2907 2907 repo.dirstate.beginparentchange()
2908 2908 repo.setparents(r1, r2)
2909 2909 repo.dirstate.endparentchange()
2910 2910 finally:
2911 2911 wlock.release()
2912 2912
2913 2913 @command('debugdirstate|debugstate',
2914 2914 [('', 'nodates', None, _('do not display the saved mtime')),
2915 2915 ('', 'datesort', None, _('sort by saved mtime'))],
2916 2916 _('[OPTION]...'))
2917 2917 def debugstate(ui, repo, nodates=None, datesort=None):
2918 2918 """show the contents of the current dirstate"""
2919 2919 timestr = ""
2920 2920 if datesort:
2921 2921 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2922 2922 else:
2923 2923 keyfunc = None # sort by filename
2924 2924 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2925 2925 if ent[3] == -1:
2926 2926 timestr = 'unset '
2927 2927 elif nodates:
2928 2928 timestr = 'set '
2929 2929 else:
2930 2930 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2931 2931 time.localtime(ent[3]))
2932 2932 if ent[1] & 020000:
2933 2933 mode = 'lnk'
2934 2934 else:
2935 2935 mode = '%3o' % (ent[1] & 0777 & ~util.umask)
2936 2936 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2937 2937 for f in repo.dirstate.copies():
2938 2938 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2939 2939
2940 2940 @command('debugsub',
2941 2941 [('r', 'rev', '',
2942 2942 _('revision to check'), _('REV'))],
2943 2943 _('[-r REV] [REV]'))
2944 2944 def debugsub(ui, repo, rev=None):
2945 2945 ctx = scmutil.revsingle(repo, rev, None)
2946 2946 for k, v in sorted(ctx.substate.items()):
2947 2947 ui.write(('path %s\n') % k)
2948 2948 ui.write((' source %s\n') % v[0])
2949 2949 ui.write((' revision %s\n') % v[1])
2950 2950
2951 2951 @command('debugsuccessorssets',
2952 2952 [],
2953 2953 _('[REV]'))
2954 2954 def debugsuccessorssets(ui, repo, *revs):
2955 2955 """show set of successors for revision
2956 2956
2957 2957 A successors set of changeset A is a consistent group of revisions that
2958 2958 succeed A. It contains non-obsolete changesets only.
2959 2959
2960 2960 In most cases a changeset A has a single successors set containing a single
2961 2961 successor (changeset A replaced by A').
2962 2962
2963 2963 A changeset that is made obsolete with no successors are called "pruned".
2964 2964 Such changesets have no successors sets at all.
2965 2965
2966 2966 A changeset that has been "split" will have a successors set containing
2967 2967 more than one successor.
2968 2968
2969 2969 A changeset that has been rewritten in multiple different ways is called
2970 2970 "divergent". Such changesets have multiple successor sets (each of which
2971 2971 may also be split, i.e. have multiple successors).
2972 2972
2973 2973 Results are displayed as follows::
2974 2974
2975 2975 <rev1>
2976 2976 <successors-1A>
2977 2977 <rev2>
2978 2978 <successors-2A>
2979 2979 <successors-2B1> <successors-2B2> <successors-2B3>
2980 2980
2981 2981 Here rev2 has two possible (i.e. divergent) successors sets. The first
2982 2982 holds one element, whereas the second holds three (i.e. the changeset has
2983 2983 been split).
2984 2984 """
2985 2985 # passed to successorssets caching computation from one call to another
2986 2986 cache = {}
2987 2987 ctx2str = str
2988 2988 node2str = short
2989 2989 if ui.debug():
2990 2990 def ctx2str(ctx):
2991 2991 return ctx.hex()
2992 2992 node2str = hex
2993 2993 for rev in scmutil.revrange(repo, revs):
2994 2994 ctx = repo[rev]
2995 2995 ui.write('%s\n'% ctx2str(ctx))
2996 2996 for succsset in obsolete.successorssets(repo, ctx.node(), cache):
2997 2997 if succsset:
2998 2998 ui.write(' ')
2999 2999 ui.write(node2str(succsset[0]))
3000 3000 for node in succsset[1:]:
3001 3001 ui.write(' ')
3002 3002 ui.write(node2str(node))
3003 3003 ui.write('\n')
3004 3004
3005 3005 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'), inferrepo=True)
3006 3006 def debugwalk(ui, repo, *pats, **opts):
3007 3007 """show how files match on given patterns"""
3008 3008 m = scmutil.match(repo[None], pats, opts)
3009 3009 items = list(repo.walk(m))
3010 3010 if not items:
3011 3011 return
3012 3012 f = lambda fn: fn
3013 3013 if ui.configbool('ui', 'slash') and os.sep != '/':
3014 3014 f = lambda fn: util.normpath(fn)
3015 3015 fmt = 'f %%-%ds %%-%ds %%s' % (
3016 3016 max([len(abs) for abs in items]),
3017 3017 max([len(m.rel(abs)) for abs in items]))
3018 3018 for abs in items:
3019 3019 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
3020 3020 ui.write("%s\n" % line.rstrip())
3021 3021
3022 3022 @command('debugwireargs',
3023 3023 [('', 'three', '', 'three'),
3024 3024 ('', 'four', '', 'four'),
3025 3025 ('', 'five', '', 'five'),
3026 3026 ] + remoteopts,
3027 3027 _('REPO [OPTIONS]... [ONE [TWO]]'),
3028 3028 norepo=True)
3029 3029 def debugwireargs(ui, repopath, *vals, **opts):
3030 3030 repo = hg.peer(ui, opts, repopath)
3031 3031 for opt in remoteopts:
3032 3032 del opts[opt[1]]
3033 3033 args = {}
3034 3034 for k, v in opts.iteritems():
3035 3035 if v:
3036 3036 args[k] = v
3037 3037 # run twice to check that we don't mess up the stream for the next command
3038 3038 res1 = repo.debugwireargs(*vals, **args)
3039 3039 res2 = repo.debugwireargs(*vals, **args)
3040 3040 ui.write("%s\n" % res1)
3041 3041 if res1 != res2:
3042 3042 ui.warn("%s\n" % res2)
3043 3043
3044 3044 @command('^diff',
3045 3045 [('r', 'rev', [], _('revision'), _('REV')),
3046 3046 ('c', 'change', '', _('change made by revision'), _('REV'))
3047 3047 ] + diffopts + diffopts2 + walkopts + subrepoopts,
3048 3048 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
3049 3049 inferrepo=True)
3050 3050 def diff(ui, repo, *pats, **opts):
3051 3051 """diff repository (or selected files)
3052 3052
3053 3053 Show differences between revisions for the specified files.
3054 3054
3055 3055 Differences between files are shown using the unified diff format.
3056 3056
3057 3057 .. note::
3058 3058
3059 3059 diff may generate unexpected results for merges, as it will
3060 3060 default to comparing against the working directory's first
3061 3061 parent changeset if no revisions are specified.
3062 3062
3063 3063 When two revision arguments are given, then changes are shown
3064 3064 between those revisions. If only one revision is specified then
3065 3065 that revision is compared to the working directory, and, when no
3066 3066 revisions are specified, the working directory files are compared
3067 3067 to its parent.
3068 3068
3069 3069 Alternatively you can specify -c/--change with a revision to see
3070 3070 the changes in that changeset relative to its first parent.
3071 3071
3072 3072 Without the -a/--text option, diff will avoid generating diffs of
3073 3073 files it detects as binary. With -a, diff will generate a diff
3074 3074 anyway, probably with undesirable results.
3075 3075
3076 3076 Use the -g/--git option to generate diffs in the git extended diff
3077 3077 format. For more information, read :hg:`help diffs`.
3078 3078
3079 3079 .. container:: verbose
3080 3080
3081 3081 Examples:
3082 3082
3083 3083 - compare a file in the current working directory to its parent::
3084 3084
3085 3085 hg diff foo.c
3086 3086
3087 3087 - compare two historical versions of a directory, with rename info::
3088 3088
3089 3089 hg diff --git -r 1.0:1.2 lib/
3090 3090
3091 3091 - get change stats relative to the last change on some date::
3092 3092
3093 3093 hg diff --stat -r "date('may 2')"
3094 3094
3095 3095 - diff all newly-added files that contain a keyword::
3096 3096
3097 3097 hg diff "set:added() and grep(GNU)"
3098 3098
3099 3099 - compare a revision and its parents::
3100 3100
3101 3101 hg diff -c 9353 # compare against first parent
3102 3102 hg diff -r 9353^:9353 # same using revset syntax
3103 3103 hg diff -r 9353^2:9353 # compare against the second parent
3104 3104
3105 3105 Returns 0 on success.
3106 3106 """
3107 3107
3108 3108 revs = opts.get('rev')
3109 3109 change = opts.get('change')
3110 3110 stat = opts.get('stat')
3111 3111 reverse = opts.get('reverse')
3112 3112
3113 3113 if revs and change:
3114 3114 msg = _('cannot specify --rev and --change at the same time')
3115 3115 raise util.Abort(msg)
3116 3116 elif change:
3117 3117 node2 = scmutil.revsingle(repo, change, None).node()
3118 3118 node1 = repo[node2].p1().node()
3119 3119 else:
3120 3120 node1, node2 = scmutil.revpair(repo, revs)
3121 3121
3122 3122 if reverse:
3123 3123 node1, node2 = node2, node1
3124 3124
3125 3125 diffopts = patch.diffallopts(ui, opts)
3126 3126 m = scmutil.match(repo[node2], pats, opts)
3127 3127 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
3128 3128 listsubrepos=opts.get('subrepos'))
3129 3129
3130 3130 @command('^export',
3131 3131 [('o', 'output', '',
3132 3132 _('print output to file with formatted name'), _('FORMAT')),
3133 3133 ('', 'switch-parent', None, _('diff against the second parent')),
3134 3134 ('r', 'rev', [], _('revisions to export'), _('REV')),
3135 3135 ] + diffopts,
3136 3136 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
3137 3137 def export(ui, repo, *changesets, **opts):
3138 3138 """dump the header and diffs for one or more changesets
3139 3139
3140 3140 Print the changeset header and diffs for one or more revisions.
3141 3141 If no revision is given, the parent of the working directory is used.
3142 3142
3143 3143 The information shown in the changeset header is: author, date,
3144 3144 branch name (if non-default), changeset hash, parent(s) and commit
3145 3145 comment.
3146 3146
3147 3147 .. note::
3148 3148
3149 3149 export may generate unexpected diff output for merge
3150 3150 changesets, as it will compare the merge changeset against its
3151 3151 first parent only.
3152 3152
3153 3153 Output may be to a file, in which case the name of the file is
3154 3154 given using a format string. The formatting rules are as follows:
3155 3155
3156 3156 :``%%``: literal "%" character
3157 3157 :``%H``: changeset hash (40 hexadecimal digits)
3158 3158 :``%N``: number of patches being generated
3159 3159 :``%R``: changeset revision number
3160 3160 :``%b``: basename of the exporting repository
3161 3161 :``%h``: short-form changeset hash (12 hexadecimal digits)
3162 3162 :``%m``: first line of the commit message (only alphanumeric characters)
3163 3163 :``%n``: zero-padded sequence number, starting at 1
3164 3164 :``%r``: zero-padded changeset revision number
3165 3165
3166 3166 Without the -a/--text option, export will avoid generating diffs
3167 3167 of files it detects as binary. With -a, export will generate a
3168 3168 diff anyway, probably with undesirable results.
3169 3169
3170 3170 Use the -g/--git option to generate diffs in the git extended diff
3171 3171 format. See :hg:`help diffs` for more information.
3172 3172
3173 3173 With the --switch-parent option, the diff will be against the
3174 3174 second parent. It can be useful to review a merge.
3175 3175
3176 3176 .. container:: verbose
3177 3177
3178 3178 Examples:
3179 3179
3180 3180 - use export and import to transplant a bugfix to the current
3181 3181 branch::
3182 3182
3183 3183 hg export -r 9353 | hg import -
3184 3184
3185 3185 - export all the changesets between two revisions to a file with
3186 3186 rename information::
3187 3187
3188 3188 hg export --git -r 123:150 > changes.txt
3189 3189
3190 3190 - split outgoing changes into a series of patches with
3191 3191 descriptive names::
3192 3192
3193 3193 hg export -r "outgoing()" -o "%n-%m.patch"
3194 3194
3195 3195 Returns 0 on success.
3196 3196 """
3197 3197 changesets += tuple(opts.get('rev', []))
3198 3198 if not changesets:
3199 3199 changesets = ['.']
3200 3200 revs = scmutil.revrange(repo, changesets)
3201 3201 if not revs:
3202 3202 raise util.Abort(_("export requires at least one changeset"))
3203 3203 if len(revs) > 1:
3204 3204 ui.note(_('exporting patches:\n'))
3205 3205 else:
3206 3206 ui.note(_('exporting patch:\n'))
3207 3207 cmdutil.export(repo, revs, template=opts.get('output'),
3208 3208 switch_parent=opts.get('switch_parent'),
3209 3209 opts=patch.diffallopts(ui, opts))
3210 3210
3211 3211 @command('files',
3212 3212 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3213 3213 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3214 3214 ] + walkopts + formatteropts,
3215 3215 _('[OPTION]... [PATTERN]...'))
3216 3216 def files(ui, repo, *pats, **opts):
3217 3217 """list tracked files
3218 3218
3219 3219 Print files under Mercurial control in the working directory or
3220 3220 specified revision whose names match the given patterns (excluding
3221 3221 removed files).
3222 3222
3223 3223 If no patterns are given to match, this command prints the names
3224 3224 of all files under Mercurial control in the working copy.
3225 3225
3226 3226 .. container:: verbose
3227 3227
3228 3228 Examples:
3229 3229
3230 3230 - list all files under the current directory::
3231 3231
3232 3232 hg files .
3233 3233
3234 3234 - shows sizes and flags for current revision::
3235 3235
3236 3236 hg files -vr .
3237 3237
3238 3238 - list all files named README::
3239 3239
3240 3240 hg files -I "**/README"
3241 3241
3242 3242 - list all binary files::
3243 3243
3244 3244 hg files "set:binary()"
3245 3245
3246 3246 - find files containing a regular expression::
3247 3247
3248 3248 hg files "set:grep('bob')"
3249 3249
3250 3250 - search tracked file contents with xargs and grep::
3251 3251
3252 3252 hg files -0 | xargs -0 grep foo
3253 3253
3254 3254 See :hg:`help patterns` and :hg:`help filesets` for more information
3255 3255 on specifying file patterns.
3256 3256
3257 3257 Returns 0 if a match is found, 1 otherwise.
3258 3258
3259 3259 """
3260 3260 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3261 rev = ctx.rev()
3262 ret = 1
3263 3261
3264 3262 end = '\n'
3265 3263 if opts.get('print0'):
3266 3264 end = '\0'
3267 3265 fm = ui.formatter('files', opts)
3268 3266 fmt = '%s' + end
3269 3267
3270 3268 m = scmutil.match(ctx, pats, opts)
3271 ds = ctx._repo.dirstate
3272 for f in ctx.matches(m):
3273 if rev is None and ds[f] == 'r':
3274 continue
3275 fm.startitem()
3276 if ui.verbose:
3277 fc = ctx[f]
3278 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags())
3279 fm.data(abspath=f)
3280 fm.write('path', fmt, m.rel(f))
3281 ret = 0
3269 ret = cmdutil.files(ui, ctx, m, fm, fmt)
3282 3270
3283 3271 fm.end()
3284 3272
3285 3273 return ret
3286 3274
3287 3275 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
3288 3276 def forget(ui, repo, *pats, **opts):
3289 3277 """forget the specified files on the next commit
3290 3278
3291 3279 Mark the specified files so they will no longer be tracked
3292 3280 after the next commit.
3293 3281
3294 3282 This only removes files from the current branch, not from the
3295 3283 entire project history, and it does not delete them from the
3296 3284 working directory.
3297 3285
3298 3286 To undo a forget before the next commit, see :hg:`add`.
3299 3287
3300 3288 .. container:: verbose
3301 3289
3302 3290 Examples:
3303 3291
3304 3292 - forget newly-added binary files::
3305 3293
3306 3294 hg forget "set:added() and binary()"
3307 3295
3308 3296 - forget files that would be excluded by .hgignore::
3309 3297
3310 3298 hg forget "set:hgignore()"
3311 3299
3312 3300 Returns 0 on success.
3313 3301 """
3314 3302
3315 3303 if not pats:
3316 3304 raise util.Abort(_('no files specified'))
3317 3305
3318 3306 m = scmutil.match(repo[None], pats, opts)
3319 3307 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
3320 3308 return rejected and 1 or 0
3321 3309
3322 3310 @command(
3323 3311 'graft',
3324 3312 [('r', 'rev', [], _('revisions to graft'), _('REV')),
3325 3313 ('c', 'continue', False, _('resume interrupted graft')),
3326 3314 ('e', 'edit', False, _('invoke editor on commit messages')),
3327 3315 ('', 'log', None, _('append graft info to log message')),
3328 3316 ('f', 'force', False, _('force graft')),
3329 3317 ('D', 'currentdate', False,
3330 3318 _('record the current date as commit date')),
3331 3319 ('U', 'currentuser', False,
3332 3320 _('record the current user as committer'), _('DATE'))]
3333 3321 + commitopts2 + mergetoolopts + dryrunopts,
3334 3322 _('[OPTION]... [-r] REV...'))
3335 3323 def graft(ui, repo, *revs, **opts):
3336 3324 '''copy changes from other branches onto the current branch
3337 3325
3338 3326 This command uses Mercurial's merge logic to copy individual
3339 3327 changes from other branches without merging branches in the
3340 3328 history graph. This is sometimes known as 'backporting' or
3341 3329 'cherry-picking'. By default, graft will copy user, date, and
3342 3330 description from the source changesets.
3343 3331
3344 3332 Changesets that are ancestors of the current revision, that have
3345 3333 already been grafted, or that are merges will be skipped.
3346 3334
3347 3335 If --log is specified, log messages will have a comment appended
3348 3336 of the form::
3349 3337
3350 3338 (grafted from CHANGESETHASH)
3351 3339
3352 3340 If --force is specified, revisions will be grafted even if they
3353 3341 are already ancestors of or have been grafted to the destination.
3354 3342 This is useful when the revisions have since been backed out.
3355 3343
3356 3344 If a graft merge results in conflicts, the graft process is
3357 3345 interrupted so that the current merge can be manually resolved.
3358 3346 Once all conflicts are addressed, the graft process can be
3359 3347 continued with the -c/--continue option.
3360 3348
3361 3349 .. note::
3362 3350
3363 3351 The -c/--continue option does not reapply earlier options, except
3364 3352 for --force.
3365 3353
3366 3354 .. container:: verbose
3367 3355
3368 3356 Examples:
3369 3357
3370 3358 - copy a single change to the stable branch and edit its description::
3371 3359
3372 3360 hg update stable
3373 3361 hg graft --edit 9393
3374 3362
3375 3363 - graft a range of changesets with one exception, updating dates::
3376 3364
3377 3365 hg graft -D "2085::2093 and not 2091"
3378 3366
3379 3367 - continue a graft after resolving conflicts::
3380 3368
3381 3369 hg graft -c
3382 3370
3383 3371 - show the source of a grafted changeset::
3384 3372
3385 3373 hg log --debug -r .
3386 3374
3387 3375 See :hg:`help revisions` and :hg:`help revsets` for more about
3388 3376 specifying revisions.
3389 3377
3390 3378 Returns 0 on successful completion.
3391 3379 '''
3392 3380
3393 3381 revs = list(revs)
3394 3382 revs.extend(opts['rev'])
3395 3383
3396 3384 if not opts.get('user') and opts.get('currentuser'):
3397 3385 opts['user'] = ui.username()
3398 3386 if not opts.get('date') and opts.get('currentdate'):
3399 3387 opts['date'] = "%d %d" % util.makedate()
3400 3388
3401 3389 editor = cmdutil.getcommiteditor(editform='graft', **opts)
3402 3390
3403 3391 cont = False
3404 3392 if opts['continue']:
3405 3393 cont = True
3406 3394 if revs:
3407 3395 raise util.Abort(_("can't specify --continue and revisions"))
3408 3396 # read in unfinished revisions
3409 3397 try:
3410 3398 nodes = repo.vfs.read('graftstate').splitlines()
3411 3399 revs = [repo[node].rev() for node in nodes]
3412 3400 except IOError, inst:
3413 3401 if inst.errno != errno.ENOENT:
3414 3402 raise
3415 3403 raise util.Abort(_("no graft state found, can't continue"))
3416 3404 else:
3417 3405 cmdutil.checkunfinished(repo)
3418 3406 cmdutil.bailifchanged(repo)
3419 3407 if not revs:
3420 3408 raise util.Abort(_('no revisions specified'))
3421 3409 revs = scmutil.revrange(repo, revs)
3422 3410
3423 3411 skipped = set()
3424 3412 # check for merges
3425 3413 for rev in repo.revs('%ld and merge()', revs):
3426 3414 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
3427 3415 skipped.add(rev)
3428 3416 revs = [r for r in revs if r not in skipped]
3429 3417 if not revs:
3430 3418 return -1
3431 3419
3432 3420 # Don't check in the --continue case, in effect retaining --force across
3433 3421 # --continues. That's because without --force, any revisions we decided to
3434 3422 # skip would have been filtered out here, so they wouldn't have made their
3435 3423 # way to the graftstate. With --force, any revisions we would have otherwise
3436 3424 # skipped would not have been filtered out, and if they hadn't been applied
3437 3425 # already, they'd have been in the graftstate.
3438 3426 if not (cont or opts.get('force')):
3439 3427 # check for ancestors of dest branch
3440 3428 crev = repo['.'].rev()
3441 3429 ancestors = repo.changelog.ancestors([crev], inclusive=True)
3442 3430 # Cannot use x.remove(y) on smart set, this has to be a list.
3443 3431 # XXX make this lazy in the future
3444 3432 revs = list(revs)
3445 3433 # don't mutate while iterating, create a copy
3446 3434 for rev in list(revs):
3447 3435 if rev in ancestors:
3448 3436 ui.warn(_('skipping ancestor revision %d:%s\n') %
3449 3437 (rev, repo[rev]))
3450 3438 # XXX remove on list is slow
3451 3439 revs.remove(rev)
3452 3440 if not revs:
3453 3441 return -1
3454 3442
3455 3443 # analyze revs for earlier grafts
3456 3444 ids = {}
3457 3445 for ctx in repo.set("%ld", revs):
3458 3446 ids[ctx.hex()] = ctx.rev()
3459 3447 n = ctx.extra().get('source')
3460 3448 if n:
3461 3449 ids[n] = ctx.rev()
3462 3450
3463 3451 # check ancestors for earlier grafts
3464 3452 ui.debug('scanning for duplicate grafts\n')
3465 3453
3466 3454 for rev in repo.changelog.findmissingrevs(revs, [crev]):
3467 3455 ctx = repo[rev]
3468 3456 n = ctx.extra().get('source')
3469 3457 if n in ids:
3470 3458 try:
3471 3459 r = repo[n].rev()
3472 3460 except error.RepoLookupError:
3473 3461 r = None
3474 3462 if r in revs:
3475 3463 ui.warn(_('skipping revision %d:%s '
3476 3464 '(already grafted to %d:%s)\n')
3477 3465 % (r, repo[r], rev, ctx))
3478 3466 revs.remove(r)
3479 3467 elif ids[n] in revs:
3480 3468 if r is None:
3481 3469 ui.warn(_('skipping already grafted revision %d:%s '
3482 3470 '(%d:%s also has unknown origin %s)\n')
3483 3471 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
3484 3472 else:
3485 3473 ui.warn(_('skipping already grafted revision %d:%s '
3486 3474 '(%d:%s also has origin %d:%s)\n')
3487 3475 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
3488 3476 revs.remove(ids[n])
3489 3477 elif ctx.hex() in ids:
3490 3478 r = ids[ctx.hex()]
3491 3479 ui.warn(_('skipping already grafted revision %d:%s '
3492 3480 '(was grafted from %d:%s)\n') %
3493 3481 (r, repo[r], rev, ctx))
3494 3482 revs.remove(r)
3495 3483 if not revs:
3496 3484 return -1
3497 3485
3498 3486 wlock = repo.wlock()
3499 3487 try:
3500 3488 for pos, ctx in enumerate(repo.set("%ld", revs)):
3501 3489 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
3502 3490 ctx.description().split('\n', 1)[0])
3503 3491 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3504 3492 if names:
3505 3493 desc += ' (%s)' % ' '.join(names)
3506 3494 ui.status(_('grafting %s\n') % desc)
3507 3495 if opts.get('dry_run'):
3508 3496 continue
3509 3497
3510 3498 source = ctx.extra().get('source')
3511 3499 if not source:
3512 3500 source = ctx.hex()
3513 3501 extra = {'source': source}
3514 3502 user = ctx.user()
3515 3503 if opts.get('user'):
3516 3504 user = opts['user']
3517 3505 date = ctx.date()
3518 3506 if opts.get('date'):
3519 3507 date = opts['date']
3520 3508 message = ctx.description()
3521 3509 if opts.get('log'):
3522 3510 message += '\n(grafted from %s)' % ctx.hex()
3523 3511
3524 3512 # we don't merge the first commit when continuing
3525 3513 if not cont:
3526 3514 # perform the graft merge with p1(rev) as 'ancestor'
3527 3515 try:
3528 3516 # ui.forcemerge is an internal variable, do not document
3529 3517 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
3530 3518 'graft')
3531 3519 stats = mergemod.graft(repo, ctx, ctx.p1(),
3532 3520 ['local', 'graft'])
3533 3521 finally:
3534 3522 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
3535 3523 # report any conflicts
3536 3524 if stats and stats[3] > 0:
3537 3525 # write out state for --continue
3538 3526 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
3539 3527 repo.vfs.write('graftstate', ''.join(nodelines))
3540 3528 raise util.Abort(
3541 3529 _("unresolved conflicts, can't continue"),
3542 3530 hint=_('use hg resolve and hg graft --continue'))
3543 3531 else:
3544 3532 cont = False
3545 3533
3546 3534 # commit
3547 3535 node = repo.commit(text=message, user=user,
3548 3536 date=date, extra=extra, editor=editor)
3549 3537 if node is None:
3550 3538 ui.warn(
3551 3539 _('note: graft of %d:%s created no changes to commit\n') %
3552 3540 (ctx.rev(), ctx))
3553 3541 finally:
3554 3542 wlock.release()
3555 3543
3556 3544 # remove state when we complete successfully
3557 3545 if not opts.get('dry_run'):
3558 3546 util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
3559 3547
3560 3548 return 0
3561 3549
3562 3550 @command('grep',
3563 3551 [('0', 'print0', None, _('end fields with NUL')),
3564 3552 ('', 'all', None, _('print all revisions that match')),
3565 3553 ('a', 'text', None, _('treat all files as text')),
3566 3554 ('f', 'follow', None,
3567 3555 _('follow changeset history,'
3568 3556 ' or file history across copies and renames')),
3569 3557 ('i', 'ignore-case', None, _('ignore case when matching')),
3570 3558 ('l', 'files-with-matches', None,
3571 3559 _('print only filenames and revisions that match')),
3572 3560 ('n', 'line-number', None, _('print matching line numbers')),
3573 3561 ('r', 'rev', [],
3574 3562 _('only search files changed within revision range'), _('REV')),
3575 3563 ('u', 'user', None, _('list the author (long with -v)')),
3576 3564 ('d', 'date', None, _('list the date (short with -q)')),
3577 3565 ] + walkopts,
3578 3566 _('[OPTION]... PATTERN [FILE]...'),
3579 3567 inferrepo=True)
3580 3568 def grep(ui, repo, pattern, *pats, **opts):
3581 3569 """search for a pattern in specified files and revisions
3582 3570
3583 3571 Search revisions of files for a regular expression.
3584 3572
3585 3573 This command behaves differently than Unix grep. It only accepts
3586 3574 Python/Perl regexps. It searches repository history, not the
3587 3575 working directory. It always prints the revision number in which a
3588 3576 match appears.
3589 3577
3590 3578 By default, grep only prints output for the first revision of a
3591 3579 file in which it finds a match. To get it to print every revision
3592 3580 that contains a change in match status ("-" for a match that
3593 3581 becomes a non-match, or "+" for a non-match that becomes a match),
3594 3582 use the --all flag.
3595 3583
3596 3584 Returns 0 if a match is found, 1 otherwise.
3597 3585 """
3598 3586 reflags = re.M
3599 3587 if opts.get('ignore_case'):
3600 3588 reflags |= re.I
3601 3589 try:
3602 3590 regexp = util.re.compile(pattern, reflags)
3603 3591 except re.error, inst:
3604 3592 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
3605 3593 return 1
3606 3594 sep, eol = ':', '\n'
3607 3595 if opts.get('print0'):
3608 3596 sep = eol = '\0'
3609 3597
3610 3598 getfile = util.lrucachefunc(repo.file)
3611 3599
3612 3600 def matchlines(body):
3613 3601 begin = 0
3614 3602 linenum = 0
3615 3603 while begin < len(body):
3616 3604 match = regexp.search(body, begin)
3617 3605 if not match:
3618 3606 break
3619 3607 mstart, mend = match.span()
3620 3608 linenum += body.count('\n', begin, mstart) + 1
3621 3609 lstart = body.rfind('\n', begin, mstart) + 1 or begin
3622 3610 begin = body.find('\n', mend) + 1 or len(body) + 1
3623 3611 lend = begin - 1
3624 3612 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
3625 3613
3626 3614 class linestate(object):
3627 3615 def __init__(self, line, linenum, colstart, colend):
3628 3616 self.line = line
3629 3617 self.linenum = linenum
3630 3618 self.colstart = colstart
3631 3619 self.colend = colend
3632 3620
3633 3621 def __hash__(self):
3634 3622 return hash((self.linenum, self.line))
3635 3623
3636 3624 def __eq__(self, other):
3637 3625 return self.line == other.line
3638 3626
3639 3627 def __iter__(self):
3640 3628 yield (self.line[:self.colstart], '')
3641 3629 yield (self.line[self.colstart:self.colend], 'grep.match')
3642 3630 rest = self.line[self.colend:]
3643 3631 while rest != '':
3644 3632 match = regexp.search(rest)
3645 3633 if not match:
3646 3634 yield (rest, '')
3647 3635 break
3648 3636 mstart, mend = match.span()
3649 3637 yield (rest[:mstart], '')
3650 3638 yield (rest[mstart:mend], 'grep.match')
3651 3639 rest = rest[mend:]
3652 3640
3653 3641 matches = {}
3654 3642 copies = {}
3655 3643 def grepbody(fn, rev, body):
3656 3644 matches[rev].setdefault(fn, [])
3657 3645 m = matches[rev][fn]
3658 3646 for lnum, cstart, cend, line in matchlines(body):
3659 3647 s = linestate(line, lnum, cstart, cend)
3660 3648 m.append(s)
3661 3649
3662 3650 def difflinestates(a, b):
3663 3651 sm = difflib.SequenceMatcher(None, a, b)
3664 3652 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3665 3653 if tag == 'insert':
3666 3654 for i in xrange(blo, bhi):
3667 3655 yield ('+', b[i])
3668 3656 elif tag == 'delete':
3669 3657 for i in xrange(alo, ahi):
3670 3658 yield ('-', a[i])
3671 3659 elif tag == 'replace':
3672 3660 for i in xrange(alo, ahi):
3673 3661 yield ('-', a[i])
3674 3662 for i in xrange(blo, bhi):
3675 3663 yield ('+', b[i])
3676 3664
3677 3665 def display(fn, ctx, pstates, states):
3678 3666 rev = ctx.rev()
3679 3667 datefunc = ui.quiet and util.shortdate or util.datestr
3680 3668 found = False
3681 3669 @util.cachefunc
3682 3670 def binary():
3683 3671 flog = getfile(fn)
3684 3672 return util.binary(flog.read(ctx.filenode(fn)))
3685 3673
3686 3674 if opts.get('all'):
3687 3675 iter = difflinestates(pstates, states)
3688 3676 else:
3689 3677 iter = [('', l) for l in states]
3690 3678 for change, l in iter:
3691 3679 cols = [(fn, 'grep.filename'), (str(rev), 'grep.rev')]
3692 3680
3693 3681 if opts.get('line_number'):
3694 3682 cols.append((str(l.linenum), 'grep.linenumber'))
3695 3683 if opts.get('all'):
3696 3684 cols.append((change, 'grep.change'))
3697 3685 if opts.get('user'):
3698 3686 cols.append((ui.shortuser(ctx.user()), 'grep.user'))
3699 3687 if opts.get('date'):
3700 3688 cols.append((datefunc(ctx.date()), 'grep.date'))
3701 3689 for col, label in cols[:-1]:
3702 3690 ui.write(col, label=label)
3703 3691 ui.write(sep, label='grep.sep')
3704 3692 ui.write(cols[-1][0], label=cols[-1][1])
3705 3693 if not opts.get('files_with_matches'):
3706 3694 ui.write(sep, label='grep.sep')
3707 3695 if not opts.get('text') and binary():
3708 3696 ui.write(" Binary file matches")
3709 3697 else:
3710 3698 for s, label in l:
3711 3699 ui.write(s, label=label)
3712 3700 ui.write(eol)
3713 3701 found = True
3714 3702 if opts.get('files_with_matches'):
3715 3703 break
3716 3704 return found
3717 3705
3718 3706 skip = {}
3719 3707 revfiles = {}
3720 3708 matchfn = scmutil.match(repo[None], pats, opts)
3721 3709 found = False
3722 3710 follow = opts.get('follow')
3723 3711
3724 3712 def prep(ctx, fns):
3725 3713 rev = ctx.rev()
3726 3714 pctx = ctx.p1()
3727 3715 parent = pctx.rev()
3728 3716 matches.setdefault(rev, {})
3729 3717 matches.setdefault(parent, {})
3730 3718 files = revfiles.setdefault(rev, [])
3731 3719 for fn in fns:
3732 3720 flog = getfile(fn)
3733 3721 try:
3734 3722 fnode = ctx.filenode(fn)
3735 3723 except error.LookupError:
3736 3724 continue
3737 3725
3738 3726 copied = flog.renamed(fnode)
3739 3727 copy = follow and copied and copied[0]
3740 3728 if copy:
3741 3729 copies.setdefault(rev, {})[fn] = copy
3742 3730 if fn in skip:
3743 3731 if copy:
3744 3732 skip[copy] = True
3745 3733 continue
3746 3734 files.append(fn)
3747 3735
3748 3736 if fn not in matches[rev]:
3749 3737 grepbody(fn, rev, flog.read(fnode))
3750 3738
3751 3739 pfn = copy or fn
3752 3740 if pfn not in matches[parent]:
3753 3741 try:
3754 3742 fnode = pctx.filenode(pfn)
3755 3743 grepbody(pfn, parent, flog.read(fnode))
3756 3744 except error.LookupError:
3757 3745 pass
3758 3746
3759 3747 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3760 3748 rev = ctx.rev()
3761 3749 parent = ctx.p1().rev()
3762 3750 for fn in sorted(revfiles.get(rev, [])):
3763 3751 states = matches[rev][fn]
3764 3752 copy = copies.get(rev, {}).get(fn)
3765 3753 if fn in skip:
3766 3754 if copy:
3767 3755 skip[copy] = True
3768 3756 continue
3769 3757 pstates = matches.get(parent, {}).get(copy or fn, [])
3770 3758 if pstates or states:
3771 3759 r = display(fn, ctx, pstates, states)
3772 3760 found = found or r
3773 3761 if r and not opts.get('all'):
3774 3762 skip[fn] = True
3775 3763 if copy:
3776 3764 skip[copy] = True
3777 3765 del matches[rev]
3778 3766 del revfiles[rev]
3779 3767
3780 3768 return not found
3781 3769
3782 3770 @command('heads',
3783 3771 [('r', 'rev', '',
3784 3772 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3785 3773 ('t', 'topo', False, _('show topological heads only')),
3786 3774 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3787 3775 ('c', 'closed', False, _('show normal and closed branch heads')),
3788 3776 ] + templateopts,
3789 3777 _('[-ct] [-r STARTREV] [REV]...'))
3790 3778 def heads(ui, repo, *branchrevs, **opts):
3791 3779 """show branch heads
3792 3780
3793 3781 With no arguments, show all open branch heads in the repository.
3794 3782 Branch heads are changesets that have no descendants on the
3795 3783 same branch. They are where development generally takes place and
3796 3784 are the usual targets for update and merge operations.
3797 3785
3798 3786 If one or more REVs are given, only open branch heads on the
3799 3787 branches associated with the specified changesets are shown. This
3800 3788 means that you can use :hg:`heads .` to see the heads on the
3801 3789 currently checked-out branch.
3802 3790
3803 3791 If -c/--closed is specified, also show branch heads marked closed
3804 3792 (see :hg:`commit --close-branch`).
3805 3793
3806 3794 If STARTREV is specified, only those heads that are descendants of
3807 3795 STARTREV will be displayed.
3808 3796
3809 3797 If -t/--topo is specified, named branch mechanics will be ignored and only
3810 3798 topological heads (changesets with no children) will be shown.
3811 3799
3812 3800 Returns 0 if matching heads are found, 1 if not.
3813 3801 """
3814 3802
3815 3803 start = None
3816 3804 if 'rev' in opts:
3817 3805 start = scmutil.revsingle(repo, opts['rev'], None).node()
3818 3806
3819 3807 if opts.get('topo'):
3820 3808 heads = [repo[h] for h in repo.heads(start)]
3821 3809 else:
3822 3810 heads = []
3823 3811 for branch in repo.branchmap():
3824 3812 heads += repo.branchheads(branch, start, opts.get('closed'))
3825 3813 heads = [repo[h] for h in heads]
3826 3814
3827 3815 if branchrevs:
3828 3816 branches = set(repo[br].branch() for br in branchrevs)
3829 3817 heads = [h for h in heads if h.branch() in branches]
3830 3818
3831 3819 if opts.get('active') and branchrevs:
3832 3820 dagheads = repo.heads(start)
3833 3821 heads = [h for h in heads if h.node() in dagheads]
3834 3822
3835 3823 if branchrevs:
3836 3824 haveheads = set(h.branch() for h in heads)
3837 3825 if branches - haveheads:
3838 3826 headless = ', '.join(b for b in branches - haveheads)
3839 3827 msg = _('no open branch heads found on branches %s')
3840 3828 if opts.get('rev'):
3841 3829 msg += _(' (started at %s)') % opts['rev']
3842 3830 ui.warn((msg + '\n') % headless)
3843 3831
3844 3832 if not heads:
3845 3833 return 1
3846 3834
3847 3835 heads = sorted(heads, key=lambda x: -x.rev())
3848 3836 displayer = cmdutil.show_changeset(ui, repo, opts)
3849 3837 for ctx in heads:
3850 3838 displayer.show(ctx)
3851 3839 displayer.close()
3852 3840
3853 3841 @command('help',
3854 3842 [('e', 'extension', None, _('show only help for extensions')),
3855 3843 ('c', 'command', None, _('show only help for commands')),
3856 3844 ('k', 'keyword', '', _('show topics matching keyword')),
3857 3845 ],
3858 3846 _('[-ec] [TOPIC]'),
3859 3847 norepo=True)
3860 3848 def help_(ui, name=None, **opts):
3861 3849 """show help for a given topic or a help overview
3862 3850
3863 3851 With no arguments, print a list of commands with short help messages.
3864 3852
3865 3853 Given a topic, extension, or command name, print help for that
3866 3854 topic.
3867 3855
3868 3856 Returns 0 if successful.
3869 3857 """
3870 3858
3871 3859 textwidth = min(ui.termwidth(), 80) - 2
3872 3860
3873 3861 keep = []
3874 3862 if ui.verbose:
3875 3863 keep.append('verbose')
3876 3864 if sys.platform.startswith('win'):
3877 3865 keep.append('windows')
3878 3866 elif sys.platform == 'OpenVMS':
3879 3867 keep.append('vms')
3880 3868 elif sys.platform == 'plan9':
3881 3869 keep.append('plan9')
3882 3870 else:
3883 3871 keep.append('unix')
3884 3872 keep.append(sys.platform.lower())
3885 3873
3886 3874 section = None
3887 3875 if name and '.' in name:
3888 3876 name, section = name.split('.', 1)
3889 3877
3890 3878 text = help.help_(ui, name, **opts)
3891 3879
3892 3880 formatted, pruned = minirst.format(text, textwidth, keep=keep,
3893 3881 section=section)
3894 3882 if section and not formatted:
3895 3883 raise util.Abort(_("help section not found"))
3896 3884
3897 3885 if 'verbose' in pruned:
3898 3886 keep.append('omitted')
3899 3887 else:
3900 3888 keep.append('notomitted')
3901 3889 formatted, pruned = minirst.format(text, textwidth, keep=keep,
3902 3890 section=section)
3903 3891 ui.write(formatted)
3904 3892
3905 3893
3906 3894 @command('identify|id',
3907 3895 [('r', 'rev', '',
3908 3896 _('identify the specified revision'), _('REV')),
3909 3897 ('n', 'num', None, _('show local revision number')),
3910 3898 ('i', 'id', None, _('show global revision id')),
3911 3899 ('b', 'branch', None, _('show branch')),
3912 3900 ('t', 'tags', None, _('show tags')),
3913 3901 ('B', 'bookmarks', None, _('show bookmarks')),
3914 3902 ] + remoteopts,
3915 3903 _('[-nibtB] [-r REV] [SOURCE]'),
3916 3904 optionalrepo=True)
3917 3905 def identify(ui, repo, source=None, rev=None,
3918 3906 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3919 3907 """identify the working copy or specified revision
3920 3908
3921 3909 Print a summary identifying the repository state at REV using one or
3922 3910 two parent hash identifiers, followed by a "+" if the working
3923 3911 directory has uncommitted changes, the branch name (if not default),
3924 3912 a list of tags, and a list of bookmarks.
3925 3913
3926 3914 When REV is not given, print a summary of the current state of the
3927 3915 repository.
3928 3916
3929 3917 Specifying a path to a repository root or Mercurial bundle will
3930 3918 cause lookup to operate on that repository/bundle.
3931 3919
3932 3920 .. container:: verbose
3933 3921
3934 3922 Examples:
3935 3923
3936 3924 - generate a build identifier for the working directory::
3937 3925
3938 3926 hg id --id > build-id.dat
3939 3927
3940 3928 - find the revision corresponding to a tag::
3941 3929
3942 3930 hg id -n -r 1.3
3943 3931
3944 3932 - check the most recent revision of a remote repository::
3945 3933
3946 3934 hg id -r tip http://selenic.com/hg/
3947 3935
3948 3936 Returns 0 if successful.
3949 3937 """
3950 3938
3951 3939 if not repo and not source:
3952 3940 raise util.Abort(_("there is no Mercurial repository here "
3953 3941 "(.hg not found)"))
3954 3942
3955 3943 hexfunc = ui.debugflag and hex or short
3956 3944 default = not (num or id or branch or tags or bookmarks)
3957 3945 output = []
3958 3946 revs = []
3959 3947
3960 3948 if source:
3961 3949 source, branches = hg.parseurl(ui.expandpath(source))
3962 3950 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3963 3951 repo = peer.local()
3964 3952 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3965 3953
3966 3954 if not repo:
3967 3955 if num or branch or tags:
3968 3956 raise util.Abort(
3969 3957 _("can't query remote revision number, branch, or tags"))
3970 3958 if not rev and revs:
3971 3959 rev = revs[0]
3972 3960 if not rev:
3973 3961 rev = "tip"
3974 3962
3975 3963 remoterev = peer.lookup(rev)
3976 3964 if default or id:
3977 3965 output = [hexfunc(remoterev)]
3978 3966
3979 3967 def getbms():
3980 3968 bms = []
3981 3969
3982 3970 if 'bookmarks' in peer.listkeys('namespaces'):
3983 3971 hexremoterev = hex(remoterev)
3984 3972 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3985 3973 if bmr == hexremoterev]
3986 3974
3987 3975 return sorted(bms)
3988 3976
3989 3977 if bookmarks:
3990 3978 output.extend(getbms())
3991 3979 elif default and not ui.quiet:
3992 3980 # multiple bookmarks for a single parent separated by '/'
3993 3981 bm = '/'.join(getbms())
3994 3982 if bm:
3995 3983 output.append(bm)
3996 3984 else:
3997 3985 if not rev:
3998 3986 ctx = repo[None]
3999 3987 parents = ctx.parents()
4000 3988 changed = ""
4001 3989 if default or id or num:
4002 3990 if (util.any(repo.status())
4003 3991 or util.any(ctx.sub(s).dirty() for s in ctx.substate)):
4004 3992 changed = '+'
4005 3993 if default or id:
4006 3994 output = ["%s%s" %
4007 3995 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
4008 3996 if num:
4009 3997 output.append("%s%s" %
4010 3998 ('+'.join([str(p.rev()) for p in parents]), changed))
4011 3999 else:
4012 4000 ctx = scmutil.revsingle(repo, rev)
4013 4001 if default or id:
4014 4002 output = [hexfunc(ctx.node())]
4015 4003 if num:
4016 4004 output.append(str(ctx.rev()))
4017 4005
4018 4006 if default and not ui.quiet:
4019 4007 b = ctx.branch()
4020 4008 if b != 'default':
4021 4009 output.append("(%s)" % b)
4022 4010
4023 4011 # multiple tags for a single parent separated by '/'
4024 4012 t = '/'.join(ctx.tags())
4025 4013 if t:
4026 4014 output.append(t)
4027 4015
4028 4016 # multiple bookmarks for a single parent separated by '/'
4029 4017 bm = '/'.join(ctx.bookmarks())
4030 4018 if bm:
4031 4019 output.append(bm)
4032 4020 else:
4033 4021 if branch:
4034 4022 output.append(ctx.branch())
4035 4023
4036 4024 if tags:
4037 4025 output.extend(ctx.tags())
4038 4026
4039 4027 if bookmarks:
4040 4028 output.extend(ctx.bookmarks())
4041 4029
4042 4030 ui.write("%s\n" % ' '.join(output))
4043 4031
4044 4032 @command('import|patch',
4045 4033 [('p', 'strip', 1,
4046 4034 _('directory strip option for patch. This has the same '
4047 4035 'meaning as the corresponding patch option'), _('NUM')),
4048 4036 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
4049 4037 ('e', 'edit', False, _('invoke editor on commit messages')),
4050 4038 ('f', 'force', None,
4051 4039 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
4052 4040 ('', 'no-commit', None,
4053 4041 _("don't commit, just update the working directory")),
4054 4042 ('', 'bypass', None,
4055 4043 _("apply patch without touching the working directory")),
4056 4044 ('', 'partial', None,
4057 4045 _('commit even if some hunks fail')),
4058 4046 ('', 'exact', None,
4059 4047 _('apply patch to the nodes from which it was generated')),
4060 4048 ('', 'prefix', '',
4061 4049 _('apply patch to directory relative to the root'), _('DIR')),
4062 4050 ('', 'import-branch', None,
4063 4051 _('use any branch information in patch (implied by --exact)'))] +
4064 4052 commitopts + commitopts2 + similarityopts,
4065 4053 _('[OPTION]... PATCH...'))
4066 4054 def import_(ui, repo, patch1=None, *patches, **opts):
4067 4055 """import an ordered set of patches
4068 4056
4069 4057 Import a list of patches and commit them individually (unless
4070 4058 --no-commit is specified).
4071 4059
4072 4060 Because import first applies changes to the working directory,
4073 4061 import will abort if there are outstanding changes.
4074 4062
4075 4063 You can import a patch straight from a mail message. Even patches
4076 4064 as attachments work (to use the body part, it must have type
4077 4065 text/plain or text/x-patch). From and Subject headers of email
4078 4066 message are used as default committer and commit message. All
4079 4067 text/plain body parts before first diff are added to commit
4080 4068 message.
4081 4069
4082 4070 If the imported patch was generated by :hg:`export`, user and
4083 4071 description from patch override values from message headers and
4084 4072 body. Values given on command line with -m/--message and -u/--user
4085 4073 override these.
4086 4074
4087 4075 If --exact is specified, import will set the working directory to
4088 4076 the parent of each patch before applying it, and will abort if the
4089 4077 resulting changeset has a different ID than the one recorded in
4090 4078 the patch. This may happen due to character set problems or other
4091 4079 deficiencies in the text patch format.
4092 4080
4093 4081 Use --bypass to apply and commit patches directly to the
4094 4082 repository, not touching the working directory. Without --exact,
4095 4083 patches will be applied on top of the working directory parent
4096 4084 revision.
4097 4085
4098 4086 With -s/--similarity, hg will attempt to discover renames and
4099 4087 copies in the patch in the same way as :hg:`addremove`.
4100 4088
4101 4089 Use --partial to ensure a changeset will be created from the patch
4102 4090 even if some hunks fail to apply. Hunks that fail to apply will be
4103 4091 written to a <target-file>.rej file. Conflicts can then be resolved
4104 4092 by hand before :hg:`commit --amend` is run to update the created
4105 4093 changeset. This flag exists to let people import patches that
4106 4094 partially apply without losing the associated metadata (author,
4107 4095 date, description, ...). Note that when none of the hunk applies
4108 4096 cleanly, :hg:`import --partial` will create an empty changeset,
4109 4097 importing only the patch metadata.
4110 4098
4111 4099 To read a patch from standard input, use "-" as the patch name. If
4112 4100 a URL is specified, the patch will be downloaded from it.
4113 4101 See :hg:`help dates` for a list of formats valid for -d/--date.
4114 4102
4115 4103 .. container:: verbose
4116 4104
4117 4105 Examples:
4118 4106
4119 4107 - import a traditional patch from a website and detect renames::
4120 4108
4121 4109 hg import -s 80 http://example.com/bugfix.patch
4122 4110
4123 4111 - import a changeset from an hgweb server::
4124 4112
4125 4113 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
4126 4114
4127 4115 - import all the patches in an Unix-style mbox::
4128 4116
4129 4117 hg import incoming-patches.mbox
4130 4118
4131 4119 - attempt to exactly restore an exported changeset (not always
4132 4120 possible)::
4133 4121
4134 4122 hg import --exact proposed-fix.patch
4135 4123
4136 4124 Returns 0 on success, 1 on partial success (see --partial).
4137 4125 """
4138 4126
4139 4127 if not patch1:
4140 4128 raise util.Abort(_('need at least one patch to import'))
4141 4129
4142 4130 patches = (patch1,) + patches
4143 4131
4144 4132 date = opts.get('date')
4145 4133 if date:
4146 4134 opts['date'] = util.parsedate(date)
4147 4135
4148 4136 update = not opts.get('bypass')
4149 4137 if not update and opts.get('no_commit'):
4150 4138 raise util.Abort(_('cannot use --no-commit with --bypass'))
4151 4139 try:
4152 4140 sim = float(opts.get('similarity') or 0)
4153 4141 except ValueError:
4154 4142 raise util.Abort(_('similarity must be a number'))
4155 4143 if sim < 0 or sim > 100:
4156 4144 raise util.Abort(_('similarity must be between 0 and 100'))
4157 4145 if sim and not update:
4158 4146 raise util.Abort(_('cannot use --similarity with --bypass'))
4159 4147 if opts.get('exact') and opts.get('edit'):
4160 4148 raise util.Abort(_('cannot use --exact with --edit'))
4161 4149 if opts.get('exact') and opts.get('prefix'):
4162 4150 raise util.Abort(_('cannot use --exact with --prefix'))
4163 4151
4164 4152 if update:
4165 4153 cmdutil.checkunfinished(repo)
4166 4154 if (opts.get('exact') or not opts.get('force')) and update:
4167 4155 cmdutil.bailifchanged(repo)
4168 4156
4169 4157 base = opts["base"]
4170 4158 wlock = lock = tr = None
4171 4159 msgs = []
4172 4160 ret = 0
4173 4161
4174 4162
4175 4163 try:
4176 4164 try:
4177 4165 wlock = repo.wlock()
4178 4166 repo.dirstate.beginparentchange()
4179 4167 if not opts.get('no_commit'):
4180 4168 lock = repo.lock()
4181 4169 tr = repo.transaction('import')
4182 4170 parents = repo.parents()
4183 4171 for patchurl in patches:
4184 4172 if patchurl == '-':
4185 4173 ui.status(_('applying patch from stdin\n'))
4186 4174 patchfile = ui.fin
4187 4175 patchurl = 'stdin' # for error message
4188 4176 else:
4189 4177 patchurl = os.path.join(base, patchurl)
4190 4178 ui.status(_('applying %s\n') % patchurl)
4191 4179 patchfile = hg.openpath(ui, patchurl)
4192 4180
4193 4181 haspatch = False
4194 4182 for hunk in patch.split(patchfile):
4195 4183 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
4196 4184 parents, opts,
4197 4185 msgs, hg.clean)
4198 4186 if msg:
4199 4187 haspatch = True
4200 4188 ui.note(msg + '\n')
4201 4189 if update or opts.get('exact'):
4202 4190 parents = repo.parents()
4203 4191 else:
4204 4192 parents = [repo[node]]
4205 4193 if rej:
4206 4194 ui.write_err(_("patch applied partially\n"))
4207 4195 ui.write_err(_("(fix the .rej files and run "
4208 4196 "`hg commit --amend`)\n"))
4209 4197 ret = 1
4210 4198 break
4211 4199
4212 4200 if not haspatch:
4213 4201 raise util.Abort(_('%s: no diffs found') % patchurl)
4214 4202
4215 4203 if tr:
4216 4204 tr.close()
4217 4205 if msgs:
4218 4206 repo.savecommitmessage('\n* * *\n'.join(msgs))
4219 4207 repo.dirstate.endparentchange()
4220 4208 return ret
4221 4209 except: # re-raises
4222 4210 # wlock.release() indirectly calls dirstate.write(): since
4223 4211 # we're crashing, we do not want to change the working dir
4224 4212 # parent after all, so make sure it writes nothing
4225 4213 repo.dirstate.invalidate()
4226 4214 raise
4227 4215 finally:
4228 4216 if tr:
4229 4217 tr.release()
4230 4218 release(lock, wlock)
4231 4219
4232 4220 @command('incoming|in',
4233 4221 [('f', 'force', None,
4234 4222 _('run even if remote repository is unrelated')),
4235 4223 ('n', 'newest-first', None, _('show newest record first')),
4236 4224 ('', 'bundle', '',
4237 4225 _('file to store the bundles into'), _('FILE')),
4238 4226 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4239 4227 ('B', 'bookmarks', False, _("compare bookmarks")),
4240 4228 ('b', 'branch', [],
4241 4229 _('a specific branch you would like to pull'), _('BRANCH')),
4242 4230 ] + logopts + remoteopts + subrepoopts,
4243 4231 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
4244 4232 def incoming(ui, repo, source="default", **opts):
4245 4233 """show new changesets found in source
4246 4234
4247 4235 Show new changesets found in the specified path/URL or the default
4248 4236 pull location. These are the changesets that would have been pulled
4249 4237 if a pull at the time you issued this command.
4250 4238
4251 4239 See pull for valid source format details.
4252 4240
4253 4241 .. container:: verbose
4254 4242
4255 4243 For remote repository, using --bundle avoids downloading the
4256 4244 changesets twice if the incoming is followed by a pull.
4257 4245
4258 4246 Examples:
4259 4247
4260 4248 - show incoming changes with patches and full description::
4261 4249
4262 4250 hg incoming -vp
4263 4251
4264 4252 - show incoming changes excluding merges, store a bundle::
4265 4253
4266 4254 hg in -vpM --bundle incoming.hg
4267 4255 hg pull incoming.hg
4268 4256
4269 4257 - briefly list changes inside a bundle::
4270 4258
4271 4259 hg in changes.hg -T "{desc|firstline}\\n"
4272 4260
4273 4261 Returns 0 if there are incoming changes, 1 otherwise.
4274 4262 """
4275 4263 if opts.get('graph'):
4276 4264 cmdutil.checkunsupportedgraphflags([], opts)
4277 4265 def display(other, chlist, displayer):
4278 4266 revdag = cmdutil.graphrevs(other, chlist, opts)
4279 4267 showparents = [ctx.node() for ctx in repo[None].parents()]
4280 4268 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4281 4269 graphmod.asciiedges)
4282 4270
4283 4271 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4284 4272 return 0
4285 4273
4286 4274 if opts.get('bundle') and opts.get('subrepos'):
4287 4275 raise util.Abort(_('cannot combine --bundle and --subrepos'))
4288 4276
4289 4277 if opts.get('bookmarks'):
4290 4278 source, branches = hg.parseurl(ui.expandpath(source),
4291 4279 opts.get('branch'))
4292 4280 other = hg.peer(repo, opts, source)
4293 4281 if 'bookmarks' not in other.listkeys('namespaces'):
4294 4282 ui.warn(_("remote doesn't support bookmarks\n"))
4295 4283 return 0
4296 4284 ui.status(_('comparing with %s\n') % util.hidepassword(source))
4297 4285 return bookmarks.diff(ui, repo, other)
4298 4286
4299 4287 repo._subtoppath = ui.expandpath(source)
4300 4288 try:
4301 4289 return hg.incoming(ui, repo, source, opts)
4302 4290 finally:
4303 4291 del repo._subtoppath
4304 4292
4305 4293
4306 4294 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
4307 4295 norepo=True)
4308 4296 def init(ui, dest=".", **opts):
4309 4297 """create a new repository in the given directory
4310 4298
4311 4299 Initialize a new repository in the given directory. If the given
4312 4300 directory does not exist, it will be created.
4313 4301
4314 4302 If no directory is given, the current directory is used.
4315 4303
4316 4304 It is possible to specify an ``ssh://`` URL as the destination.
4317 4305 See :hg:`help urls` for more information.
4318 4306
4319 4307 Returns 0 on success.
4320 4308 """
4321 4309 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4322 4310
4323 4311 @command('locate',
4324 4312 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
4325 4313 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4326 4314 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
4327 4315 ] + walkopts,
4328 4316 _('[OPTION]... [PATTERN]...'))
4329 4317 def locate(ui, repo, *pats, **opts):
4330 4318 """locate files matching specific patterns (DEPRECATED)
4331 4319
4332 4320 Print files under Mercurial control in the working directory whose
4333 4321 names match the given patterns.
4334 4322
4335 4323 By default, this command searches all directories in the working
4336 4324 directory. To search just the current directory and its
4337 4325 subdirectories, use "--include .".
4338 4326
4339 4327 If no patterns are given to match, this command prints the names
4340 4328 of all files under Mercurial control in the working directory.
4341 4329
4342 4330 If you want to feed the output of this command into the "xargs"
4343 4331 command, use the -0 option to both this command and "xargs". This
4344 4332 will avoid the problem of "xargs" treating single filenames that
4345 4333 contain whitespace as multiple filenames.
4346 4334
4347 4335 See :hg:`help files` for a more versatile command.
4348 4336
4349 4337 Returns 0 if a match is found, 1 otherwise.
4350 4338 """
4351 4339 end = opts.get('print0') and '\0' or '\n'
4352 4340 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
4353 4341
4354 4342 ret = 1
4355 4343 ctx = repo[rev]
4356 4344 m = scmutil.match(ctx, pats, opts, default='relglob')
4357 4345 m.bad = lambda x, y: False
4358 4346
4359 4347 for abs in ctx.matches(m):
4360 4348 if opts.get('fullpath'):
4361 4349 ui.write(repo.wjoin(abs), end)
4362 4350 else:
4363 4351 ui.write(((pats and m.rel(abs)) or abs), end)
4364 4352 ret = 0
4365 4353
4366 4354 return ret
4367 4355
4368 4356 @command('^log|history',
4369 4357 [('f', 'follow', None,
4370 4358 _('follow changeset history, or file history across copies and renames')),
4371 4359 ('', 'follow-first', None,
4372 4360 _('only follow the first parent of merge changesets (DEPRECATED)')),
4373 4361 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
4374 4362 ('C', 'copies', None, _('show copied files')),
4375 4363 ('k', 'keyword', [],
4376 4364 _('do case-insensitive search for a given text'), _('TEXT')),
4377 4365 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
4378 4366 ('', 'removed', None, _('include revisions where files were removed')),
4379 4367 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
4380 4368 ('u', 'user', [], _('revisions committed by user'), _('USER')),
4381 4369 ('', 'only-branch', [],
4382 4370 _('show only changesets within the given named branch (DEPRECATED)'),
4383 4371 _('BRANCH')),
4384 4372 ('b', 'branch', [],
4385 4373 _('show changesets within the given named branch'), _('BRANCH')),
4386 4374 ('P', 'prune', [],
4387 4375 _('do not display revision or any of its ancestors'), _('REV')),
4388 4376 ] + logopts + walkopts,
4389 4377 _('[OPTION]... [FILE]'),
4390 4378 inferrepo=True)
4391 4379 def log(ui, repo, *pats, **opts):
4392 4380 """show revision history of entire repository or files
4393 4381
4394 4382 Print the revision history of the specified files or the entire
4395 4383 project.
4396 4384
4397 4385 If no revision range is specified, the default is ``tip:0`` unless
4398 4386 --follow is set, in which case the working directory parent is
4399 4387 used as the starting revision.
4400 4388
4401 4389 File history is shown without following rename or copy history of
4402 4390 files. Use -f/--follow with a filename to follow history across
4403 4391 renames and copies. --follow without a filename will only show
4404 4392 ancestors or descendants of the starting revision.
4405 4393
4406 4394 By default this command prints revision number and changeset id,
4407 4395 tags, non-trivial parents, user, date and time, and a summary for
4408 4396 each commit. When the -v/--verbose switch is used, the list of
4409 4397 changed files and full commit message are shown.
4410 4398
4411 4399 With --graph the revisions are shown as an ASCII art DAG with the most
4412 4400 recent changeset at the top.
4413 4401 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
4414 4402 and '+' represents a fork where the changeset from the lines below is a
4415 4403 parent of the 'o' merge on the same line.
4416 4404
4417 4405 .. note::
4418 4406
4419 4407 log -p/--patch may generate unexpected diff output for merge
4420 4408 changesets, as it will only compare the merge changeset against
4421 4409 its first parent. Also, only files different from BOTH parents
4422 4410 will appear in files:.
4423 4411
4424 4412 .. note::
4425 4413
4426 4414 for performance reasons, log FILE may omit duplicate changes
4427 4415 made on branches and will not show removals or mode changes. To
4428 4416 see all such changes, use the --removed switch.
4429 4417
4430 4418 .. container:: verbose
4431 4419
4432 4420 Some examples:
4433 4421
4434 4422 - changesets with full descriptions and file lists::
4435 4423
4436 4424 hg log -v
4437 4425
4438 4426 - changesets ancestral to the working directory::
4439 4427
4440 4428 hg log -f
4441 4429
4442 4430 - last 10 commits on the current branch::
4443 4431
4444 4432 hg log -l 10 -b .
4445 4433
4446 4434 - changesets showing all modifications of a file, including removals::
4447 4435
4448 4436 hg log --removed file.c
4449 4437
4450 4438 - all changesets that touch a directory, with diffs, excluding merges::
4451 4439
4452 4440 hg log -Mp lib/
4453 4441
4454 4442 - all revision numbers that match a keyword::
4455 4443
4456 4444 hg log -k bug --template "{rev}\\n"
4457 4445
4458 4446 - list available log templates::
4459 4447
4460 4448 hg log -T list
4461 4449
4462 4450 - check if a given changeset is included in a tagged release::
4463 4451
4464 4452 hg log -r "a21ccf and ancestor(1.9)"
4465 4453
4466 4454 - find all changesets by some user in a date range::
4467 4455
4468 4456 hg log -k alice -d "may 2008 to jul 2008"
4469 4457
4470 4458 - summary of all changesets after the last tag::
4471 4459
4472 4460 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4473 4461
4474 4462 See :hg:`help dates` for a list of formats valid for -d/--date.
4475 4463
4476 4464 See :hg:`help revisions` and :hg:`help revsets` for more about
4477 4465 specifying revisions.
4478 4466
4479 4467 See :hg:`help templates` for more about pre-packaged styles and
4480 4468 specifying custom templates.
4481 4469
4482 4470 Returns 0 on success.
4483 4471
4484 4472 """
4485 4473 if opts.get('follow') and opts.get('rev'):
4486 4474 opts['rev'] = [revset.formatspec('reverse(::%lr)', opts.get('rev'))]
4487 4475 del opts['follow']
4488 4476
4489 4477 if opts.get('graph'):
4490 4478 return cmdutil.graphlog(ui, repo, *pats, **opts)
4491 4479
4492 4480 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
4493 4481 limit = cmdutil.loglimit(opts)
4494 4482 count = 0
4495 4483
4496 4484 getrenamed = None
4497 4485 if opts.get('copies'):
4498 4486 endrev = None
4499 4487 if opts.get('rev'):
4500 4488 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
4501 4489 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
4502 4490
4503 4491 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4504 4492 for rev in revs:
4505 4493 if count == limit:
4506 4494 break
4507 4495 ctx = repo[rev]
4508 4496 copies = None
4509 4497 if getrenamed is not None and rev:
4510 4498 copies = []
4511 4499 for fn in ctx.files():
4512 4500 rename = getrenamed(fn, rev)
4513 4501 if rename:
4514 4502 copies.append((fn, rename[0]))
4515 4503 revmatchfn = filematcher and filematcher(ctx.rev()) or None
4516 4504 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
4517 4505 if displayer.flush(rev):
4518 4506 count += 1
4519 4507
4520 4508 displayer.close()
4521 4509
4522 4510 @command('manifest',
4523 4511 [('r', 'rev', '', _('revision to display'), _('REV')),
4524 4512 ('', 'all', False, _("list files from all revisions"))]
4525 4513 + formatteropts,
4526 4514 _('[-r REV]'))
4527 4515 def manifest(ui, repo, node=None, rev=None, **opts):
4528 4516 """output the current or given revision of the project manifest
4529 4517
4530 4518 Print a list of version controlled files for the given revision.
4531 4519 If no revision is given, the first parent of the working directory
4532 4520 is used, or the null revision if no revision is checked out.
4533 4521
4534 4522 With -v, print file permissions, symlink and executable bits.
4535 4523 With --debug, print file revision hashes.
4536 4524
4537 4525 If option --all is specified, the list of all files from all revisions
4538 4526 is printed. This includes deleted and renamed files.
4539 4527
4540 4528 Returns 0 on success.
4541 4529 """
4542 4530
4543 4531 fm = ui.formatter('manifest', opts)
4544 4532
4545 4533 if opts.get('all'):
4546 4534 if rev or node:
4547 4535 raise util.Abort(_("can't specify a revision with --all"))
4548 4536
4549 4537 res = []
4550 4538 prefix = "data/"
4551 4539 suffix = ".i"
4552 4540 plen = len(prefix)
4553 4541 slen = len(suffix)
4554 4542 lock = repo.lock()
4555 4543 try:
4556 4544 for fn, b, size in repo.store.datafiles():
4557 4545 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
4558 4546 res.append(fn[plen:-slen])
4559 4547 finally:
4560 4548 lock.release()
4561 4549 for f in res:
4562 4550 fm.startitem()
4563 4551 fm.write("path", '%s\n', f)
4564 4552 fm.end()
4565 4553 return
4566 4554
4567 4555 if rev and node:
4568 4556 raise util.Abort(_("please specify just one revision"))
4569 4557
4570 4558 if not node:
4571 4559 node = rev
4572 4560
4573 4561 char = {'l': '@', 'x': '*', '': ''}
4574 4562 mode = {'l': '644', 'x': '755', '': '644'}
4575 4563 ctx = scmutil.revsingle(repo, node)
4576 4564 mf = ctx.manifest()
4577 4565 for f in ctx:
4578 4566 fm.startitem()
4579 4567 fl = ctx[f].flags()
4580 4568 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
4581 4569 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
4582 4570 fm.write('path', '%s\n', f)
4583 4571 fm.end()
4584 4572
4585 4573 @command('^merge',
4586 4574 [('f', 'force', None,
4587 4575 _('force a merge including outstanding changes (DEPRECATED)')),
4588 4576 ('r', 'rev', '', _('revision to merge'), _('REV')),
4589 4577 ('P', 'preview', None,
4590 4578 _('review revisions to merge (no merge is performed)'))
4591 4579 ] + mergetoolopts,
4592 4580 _('[-P] [-f] [[-r] REV]'))
4593 4581 def merge(ui, repo, node=None, **opts):
4594 4582 """merge another revision into working directory
4595 4583
4596 4584 The current working directory is updated with all changes made in
4597 4585 the requested revision since the last common predecessor revision.
4598 4586
4599 4587 Files that changed between either parent are marked as changed for
4600 4588 the next commit and a commit must be performed before any further
4601 4589 updates to the repository are allowed. The next commit will have
4602 4590 two parents.
4603 4591
4604 4592 ``--tool`` can be used to specify the merge tool used for file
4605 4593 merges. It overrides the HGMERGE environment variable and your
4606 4594 configuration files. See :hg:`help merge-tools` for options.
4607 4595
4608 4596 If no revision is specified, the working directory's parent is a
4609 4597 head revision, and the current branch contains exactly one other
4610 4598 head, the other head is merged with by default. Otherwise, an
4611 4599 explicit revision with which to merge with must be provided.
4612 4600
4613 4601 :hg:`resolve` must be used to resolve unresolved files.
4614 4602
4615 4603 To undo an uncommitted merge, use :hg:`update --clean .` which
4616 4604 will check out a clean copy of the original merge parent, losing
4617 4605 all changes.
4618 4606
4619 4607 Returns 0 on success, 1 if there are unresolved files.
4620 4608 """
4621 4609
4622 4610 if opts.get('rev') and node:
4623 4611 raise util.Abort(_("please specify just one revision"))
4624 4612 if not node:
4625 4613 node = opts.get('rev')
4626 4614
4627 4615 if node:
4628 4616 node = scmutil.revsingle(repo, node).node()
4629 4617
4630 4618 if not node and repo._bookmarkcurrent:
4631 4619 bmheads = repo.bookmarkheads(repo._bookmarkcurrent)
4632 4620 curhead = repo[repo._bookmarkcurrent].node()
4633 4621 if len(bmheads) == 2:
4634 4622 if curhead == bmheads[0]:
4635 4623 node = bmheads[1]
4636 4624 else:
4637 4625 node = bmheads[0]
4638 4626 elif len(bmheads) > 2:
4639 4627 raise util.Abort(_("multiple matching bookmarks to merge - "
4640 4628 "please merge with an explicit rev or bookmark"),
4641 4629 hint=_("run 'hg heads' to see all heads"))
4642 4630 elif len(bmheads) <= 1:
4643 4631 raise util.Abort(_("no matching bookmark to merge - "
4644 4632 "please merge with an explicit rev or bookmark"),
4645 4633 hint=_("run 'hg heads' to see all heads"))
4646 4634
4647 4635 if not node and not repo._bookmarkcurrent:
4648 4636 branch = repo[None].branch()
4649 4637 bheads = repo.branchheads(branch)
4650 4638 nbhs = [bh for bh in bheads if not repo[bh].bookmarks()]
4651 4639
4652 4640 if len(nbhs) > 2:
4653 4641 raise util.Abort(_("branch '%s' has %d heads - "
4654 4642 "please merge with an explicit rev")
4655 4643 % (branch, len(bheads)),
4656 4644 hint=_("run 'hg heads .' to see heads"))
4657 4645
4658 4646 parent = repo.dirstate.p1()
4659 4647 if len(nbhs) <= 1:
4660 4648 if len(bheads) > 1:
4661 4649 raise util.Abort(_("heads are bookmarked - "
4662 4650 "please merge with an explicit rev"),
4663 4651 hint=_("run 'hg heads' to see all heads"))
4664 4652 if len(repo.heads()) > 1:
4665 4653 raise util.Abort(_("branch '%s' has one head - "
4666 4654 "please merge with an explicit rev")
4667 4655 % branch,
4668 4656 hint=_("run 'hg heads' to see all heads"))
4669 4657 msg, hint = _('nothing to merge'), None
4670 4658 if parent != repo.lookup(branch):
4671 4659 hint = _("use 'hg update' instead")
4672 4660 raise util.Abort(msg, hint=hint)
4673 4661
4674 4662 if parent not in bheads:
4675 4663 raise util.Abort(_('working directory not at a head revision'),
4676 4664 hint=_("use 'hg update' or merge with an "
4677 4665 "explicit revision"))
4678 4666 if parent == nbhs[0]:
4679 4667 node = nbhs[-1]
4680 4668 else:
4681 4669 node = nbhs[0]
4682 4670
4683 4671 if opts.get('preview'):
4684 4672 # find nodes that are ancestors of p2 but not of p1
4685 4673 p1 = repo.lookup('.')
4686 4674 p2 = repo.lookup(node)
4687 4675 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4688 4676
4689 4677 displayer = cmdutil.show_changeset(ui, repo, opts)
4690 4678 for node in nodes:
4691 4679 displayer.show(repo[node])
4692 4680 displayer.close()
4693 4681 return 0
4694 4682
4695 4683 try:
4696 4684 # ui.forcemerge is an internal variable, do not document
4697 4685 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
4698 4686 return hg.merge(repo, node, force=opts.get('force'))
4699 4687 finally:
4700 4688 ui.setconfig('ui', 'forcemerge', '', 'merge')
4701 4689
4702 4690 @command('outgoing|out',
4703 4691 [('f', 'force', None, _('run even when the destination is unrelated')),
4704 4692 ('r', 'rev', [],
4705 4693 _('a changeset intended to be included in the destination'), _('REV')),
4706 4694 ('n', 'newest-first', None, _('show newest record first')),
4707 4695 ('B', 'bookmarks', False, _('compare bookmarks')),
4708 4696 ('b', 'branch', [], _('a specific branch you would like to push'),
4709 4697 _('BRANCH')),
4710 4698 ] + logopts + remoteopts + subrepoopts,
4711 4699 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4712 4700 def outgoing(ui, repo, dest=None, **opts):
4713 4701 """show changesets not found in the destination
4714 4702
4715 4703 Show changesets not found in the specified destination repository
4716 4704 or the default push location. These are the changesets that would
4717 4705 be pushed if a push was requested.
4718 4706
4719 4707 See pull for details of valid destination formats.
4720 4708
4721 4709 Returns 0 if there are outgoing changes, 1 otherwise.
4722 4710 """
4723 4711 if opts.get('graph'):
4724 4712 cmdutil.checkunsupportedgraphflags([], opts)
4725 4713 o, other = hg._outgoing(ui, repo, dest, opts)
4726 4714 if not o:
4727 4715 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4728 4716 return
4729 4717
4730 4718 revdag = cmdutil.graphrevs(repo, o, opts)
4731 4719 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4732 4720 showparents = [ctx.node() for ctx in repo[None].parents()]
4733 4721 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4734 4722 graphmod.asciiedges)
4735 4723 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4736 4724 return 0
4737 4725
4738 4726 if opts.get('bookmarks'):
4739 4727 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4740 4728 dest, branches = hg.parseurl(dest, opts.get('branch'))
4741 4729 other = hg.peer(repo, opts, dest)
4742 4730 if 'bookmarks' not in other.listkeys('namespaces'):
4743 4731 ui.warn(_("remote doesn't support bookmarks\n"))
4744 4732 return 0
4745 4733 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4746 4734 return bookmarks.diff(ui, other, repo)
4747 4735
4748 4736 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4749 4737 try:
4750 4738 return hg.outgoing(ui, repo, dest, opts)
4751 4739 finally:
4752 4740 del repo._subtoppath
4753 4741
4754 4742 @command('parents',
4755 4743 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4756 4744 ] + templateopts,
4757 4745 _('[-r REV] [FILE]'),
4758 4746 inferrepo=True)
4759 4747 def parents(ui, repo, file_=None, **opts):
4760 4748 """show the parents of the working directory or revision (DEPRECATED)
4761 4749
4762 4750 Print the working directory's parent revisions. If a revision is
4763 4751 given via -r/--rev, the parent of that revision will be printed.
4764 4752 If a file argument is given, the revision in which the file was
4765 4753 last changed (before the working directory revision or the
4766 4754 argument to --rev if given) is printed.
4767 4755
4768 4756 See :hg:`summary` and :hg:`help revsets` for related information.
4769 4757
4770 4758 Returns 0 on success.
4771 4759 """
4772 4760
4773 4761 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4774 4762
4775 4763 if file_:
4776 4764 m = scmutil.match(ctx, (file_,), opts)
4777 4765 if m.anypats() or len(m.files()) != 1:
4778 4766 raise util.Abort(_('can only specify an explicit filename'))
4779 4767 file_ = m.files()[0]
4780 4768 filenodes = []
4781 4769 for cp in ctx.parents():
4782 4770 if not cp:
4783 4771 continue
4784 4772 try:
4785 4773 filenodes.append(cp.filenode(file_))
4786 4774 except error.LookupError:
4787 4775 pass
4788 4776 if not filenodes:
4789 4777 raise util.Abort(_("'%s' not found in manifest!") % file_)
4790 4778 p = []
4791 4779 for fn in filenodes:
4792 4780 fctx = repo.filectx(file_, fileid=fn)
4793 4781 p.append(fctx.node())
4794 4782 else:
4795 4783 p = [cp.node() for cp in ctx.parents()]
4796 4784
4797 4785 displayer = cmdutil.show_changeset(ui, repo, opts)
4798 4786 for n in p:
4799 4787 if n != nullid:
4800 4788 displayer.show(repo[n])
4801 4789 displayer.close()
4802 4790
4803 4791 @command('paths', [], _('[NAME]'), optionalrepo=True)
4804 4792 def paths(ui, repo, search=None):
4805 4793 """show aliases for remote repositories
4806 4794
4807 4795 Show definition of symbolic path name NAME. If no name is given,
4808 4796 show definition of all available names.
4809 4797
4810 4798 Option -q/--quiet suppresses all output when searching for NAME
4811 4799 and shows only the path names when listing all definitions.
4812 4800
4813 4801 Path names are defined in the [paths] section of your
4814 4802 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4815 4803 repository, ``.hg/hgrc`` is used, too.
4816 4804
4817 4805 The path names ``default`` and ``default-push`` have a special
4818 4806 meaning. When performing a push or pull operation, they are used
4819 4807 as fallbacks if no location is specified on the command-line.
4820 4808 When ``default-push`` is set, it will be used for push and
4821 4809 ``default`` will be used for pull; otherwise ``default`` is used
4822 4810 as the fallback for both. When cloning a repository, the clone
4823 4811 source is written as ``default`` in ``.hg/hgrc``. Note that
4824 4812 ``default`` and ``default-push`` apply to all inbound (e.g.
4825 4813 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
4826 4814 :hg:`bundle`) operations.
4827 4815
4828 4816 See :hg:`help urls` for more information.
4829 4817
4830 4818 Returns 0 on success.
4831 4819 """
4832 4820 if search:
4833 4821 for name, path in sorted(ui.paths.iteritems()):
4834 4822 if name == search:
4835 4823 ui.status("%s\n" % util.hidepassword(path.loc))
4836 4824 return
4837 4825 if not ui.quiet:
4838 4826 ui.warn(_("not found!\n"))
4839 4827 return 1
4840 4828 else:
4841 4829 for name, path in sorted(ui.paths.iteritems()):
4842 4830 if ui.quiet:
4843 4831 ui.write("%s\n" % name)
4844 4832 else:
4845 4833 ui.write("%s = %s\n" % (name,
4846 4834 util.hidepassword(path.loc)))
4847 4835
4848 4836 @command('phase',
4849 4837 [('p', 'public', False, _('set changeset phase to public')),
4850 4838 ('d', 'draft', False, _('set changeset phase to draft')),
4851 4839 ('s', 'secret', False, _('set changeset phase to secret')),
4852 4840 ('f', 'force', False, _('allow to move boundary backward')),
4853 4841 ('r', 'rev', [], _('target revision'), _('REV')),
4854 4842 ],
4855 4843 _('[-p|-d|-s] [-f] [-r] REV...'))
4856 4844 def phase(ui, repo, *revs, **opts):
4857 4845 """set or show the current phase name
4858 4846
4859 4847 With no argument, show the phase name of specified revisions.
4860 4848
4861 4849 With one of -p/--public, -d/--draft or -s/--secret, change the
4862 4850 phase value of the specified revisions.
4863 4851
4864 4852 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
4865 4853 lower phase to an higher phase. Phases are ordered as follows::
4866 4854
4867 4855 public < draft < secret
4868 4856
4869 4857 Returns 0 on success, 1 if no phases were changed or some could not
4870 4858 be changed.
4871 4859 """
4872 4860 # search for a unique phase argument
4873 4861 targetphase = None
4874 4862 for idx, name in enumerate(phases.phasenames):
4875 4863 if opts[name]:
4876 4864 if targetphase is not None:
4877 4865 raise util.Abort(_('only one phase can be specified'))
4878 4866 targetphase = idx
4879 4867
4880 4868 # look for specified revision
4881 4869 revs = list(revs)
4882 4870 revs.extend(opts['rev'])
4883 4871 if not revs:
4884 4872 raise util.Abort(_('no revisions specified'))
4885 4873
4886 4874 revs = scmutil.revrange(repo, revs)
4887 4875
4888 4876 lock = None
4889 4877 ret = 0
4890 4878 if targetphase is None:
4891 4879 # display
4892 4880 for r in revs:
4893 4881 ctx = repo[r]
4894 4882 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4895 4883 else:
4896 4884 tr = None
4897 4885 lock = repo.lock()
4898 4886 try:
4899 4887 tr = repo.transaction("phase")
4900 4888 # set phase
4901 4889 if not revs:
4902 4890 raise util.Abort(_('empty revision set'))
4903 4891 nodes = [repo[r].node() for r in revs]
4904 4892 # moving revision from public to draft may hide them
4905 4893 # We have to check result on an unfiltered repository
4906 4894 unfi = repo.unfiltered()
4907 4895 getphase = unfi._phasecache.phase
4908 4896 olddata = [getphase(unfi, r) for r in unfi]
4909 4897 phases.advanceboundary(repo, tr, targetphase, nodes)
4910 4898 if opts['force']:
4911 4899 phases.retractboundary(repo, tr, targetphase, nodes)
4912 4900 tr.close()
4913 4901 finally:
4914 4902 if tr is not None:
4915 4903 tr.release()
4916 4904 lock.release()
4917 4905 getphase = unfi._phasecache.phase
4918 4906 newdata = [getphase(unfi, r) for r in unfi]
4919 4907 changes = sum(newdata[r] != olddata[r] for r in unfi)
4920 4908 cl = unfi.changelog
4921 4909 rejected = [n for n in nodes
4922 4910 if newdata[cl.rev(n)] < targetphase]
4923 4911 if rejected:
4924 4912 ui.warn(_('cannot move %i changesets to a higher '
4925 4913 'phase, use --force\n') % len(rejected))
4926 4914 ret = 1
4927 4915 if changes:
4928 4916 msg = _('phase changed for %i changesets\n') % changes
4929 4917 if ret:
4930 4918 ui.status(msg)
4931 4919 else:
4932 4920 ui.note(msg)
4933 4921 else:
4934 4922 ui.warn(_('no phases changed\n'))
4935 4923 ret = 1
4936 4924 return ret
4937 4925
4938 4926 def postincoming(ui, repo, modheads, optupdate, checkout):
4939 4927 if modheads == 0:
4940 4928 return
4941 4929 if optupdate:
4942 4930 checkout, movemarkfrom = bookmarks.calculateupdate(ui, repo, checkout)
4943 4931 try:
4944 4932 ret = hg.update(repo, checkout)
4945 4933 except util.Abort, inst:
4946 4934 ui.warn(_("not updating: %s\n") % str(inst))
4947 4935 if inst.hint:
4948 4936 ui.warn(_("(%s)\n") % inst.hint)
4949 4937 return 0
4950 4938 if not ret and not checkout:
4951 4939 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
4952 4940 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
4953 4941 return ret
4954 4942 if modheads > 1:
4955 4943 currentbranchheads = len(repo.branchheads())
4956 4944 if currentbranchheads == modheads:
4957 4945 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4958 4946 elif currentbranchheads > 1:
4959 4947 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4960 4948 "merge)\n"))
4961 4949 else:
4962 4950 ui.status(_("(run 'hg heads' to see heads)\n"))
4963 4951 else:
4964 4952 ui.status(_("(run 'hg update' to get a working copy)\n"))
4965 4953
4966 4954 @command('^pull',
4967 4955 [('u', 'update', None,
4968 4956 _('update to new branch head if changesets were pulled')),
4969 4957 ('f', 'force', None, _('run even when remote repository is unrelated')),
4970 4958 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4971 4959 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4972 4960 ('b', 'branch', [], _('a specific branch you would like to pull'),
4973 4961 _('BRANCH')),
4974 4962 ] + remoteopts,
4975 4963 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
4976 4964 def pull(ui, repo, source="default", **opts):
4977 4965 """pull changes from the specified source
4978 4966
4979 4967 Pull changes from a remote repository to a local one.
4980 4968
4981 4969 This finds all changes from the repository at the specified path
4982 4970 or URL and adds them to a local repository (the current one unless
4983 4971 -R is specified). By default, this does not update the copy of the
4984 4972 project in the working directory.
4985 4973
4986 4974 Use :hg:`incoming` if you want to see what would have been added
4987 4975 by a pull at the time you issued this command. If you then decide
4988 4976 to add those changes to the repository, you should use :hg:`pull
4989 4977 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4990 4978
4991 4979 If SOURCE is omitted, the 'default' path will be used.
4992 4980 See :hg:`help urls` for more information.
4993 4981
4994 4982 Returns 0 on success, 1 if an update had unresolved files.
4995 4983 """
4996 4984 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4997 4985 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4998 4986 other = hg.peer(repo, opts, source)
4999 4987 try:
5000 4988 revs, checkout = hg.addbranchrevs(repo, other, branches,
5001 4989 opts.get('rev'))
5002 4990
5003 4991 remotebookmarks = other.listkeys('bookmarks')
5004 4992
5005 4993 if opts.get('bookmark'):
5006 4994 if not revs:
5007 4995 revs = []
5008 4996 for b in opts['bookmark']:
5009 4997 if b not in remotebookmarks:
5010 4998 raise util.Abort(_('remote bookmark %s not found!') % b)
5011 4999 revs.append(remotebookmarks[b])
5012 5000
5013 5001 if revs:
5014 5002 try:
5015 5003 revs = [other.lookup(rev) for rev in revs]
5016 5004 except error.CapabilityError:
5017 5005 err = _("other repository doesn't support revision lookup, "
5018 5006 "so a rev cannot be specified.")
5019 5007 raise util.Abort(err)
5020 5008
5021 5009 modheads = exchange.pull(repo, other, heads=revs,
5022 5010 force=opts.get('force'),
5023 5011 bookmarks=opts.get('bookmark', ())).cgresult
5024 5012 if checkout:
5025 5013 checkout = str(repo.changelog.rev(other.lookup(checkout)))
5026 5014 repo._subtoppath = source
5027 5015 try:
5028 5016 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
5029 5017
5030 5018 finally:
5031 5019 del repo._subtoppath
5032 5020
5033 5021 finally:
5034 5022 other.close()
5035 5023 return ret
5036 5024
5037 5025 @command('^push',
5038 5026 [('f', 'force', None, _('force push')),
5039 5027 ('r', 'rev', [],
5040 5028 _('a changeset intended to be included in the destination'),
5041 5029 _('REV')),
5042 5030 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
5043 5031 ('b', 'branch', [],
5044 5032 _('a specific branch you would like to push'), _('BRANCH')),
5045 5033 ('', 'new-branch', False, _('allow pushing a new branch')),
5046 5034 ] + remoteopts,
5047 5035 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
5048 5036 def push(ui, repo, dest=None, **opts):
5049 5037 """push changes to the specified destination
5050 5038
5051 5039 Push changesets from the local repository to the specified
5052 5040 destination.
5053 5041
5054 5042 This operation is symmetrical to pull: it is identical to a pull
5055 5043 in the destination repository from the current one.
5056 5044
5057 5045 By default, push will not allow creation of new heads at the
5058 5046 destination, since multiple heads would make it unclear which head
5059 5047 to use. In this situation, it is recommended to pull and merge
5060 5048 before pushing.
5061 5049
5062 5050 Use --new-branch if you want to allow push to create a new named
5063 5051 branch that is not present at the destination. This allows you to
5064 5052 only create a new branch without forcing other changes.
5065 5053
5066 5054 .. note::
5067 5055
5068 5056 Extra care should be taken with the -f/--force option,
5069 5057 which will push all new heads on all branches, an action which will
5070 5058 almost always cause confusion for collaborators.
5071 5059
5072 5060 If -r/--rev is used, the specified revision and all its ancestors
5073 5061 will be pushed to the remote repository.
5074 5062
5075 5063 If -B/--bookmark is used, the specified bookmarked revision, its
5076 5064 ancestors, and the bookmark will be pushed to the remote
5077 5065 repository.
5078 5066
5079 5067 Please see :hg:`help urls` for important details about ``ssh://``
5080 5068 URLs. If DESTINATION is omitted, a default path will be used.
5081 5069
5082 5070 Returns 0 if push was successful, 1 if nothing to push.
5083 5071 """
5084 5072
5085 5073 if opts.get('bookmark'):
5086 5074 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
5087 5075 for b in opts['bookmark']:
5088 5076 # translate -B options to -r so changesets get pushed
5089 5077 if b in repo._bookmarks:
5090 5078 opts.setdefault('rev', []).append(b)
5091 5079 else:
5092 5080 # if we try to push a deleted bookmark, translate it to null
5093 5081 # this lets simultaneous -r, -b options continue working
5094 5082 opts.setdefault('rev', []).append("null")
5095 5083
5096 5084 dest = ui.expandpath(dest or 'default-push', dest or 'default')
5097 5085 dest, branches = hg.parseurl(dest, opts.get('branch'))
5098 5086 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
5099 5087 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
5100 5088 try:
5101 5089 other = hg.peer(repo, opts, dest)
5102 5090 except error.RepoError:
5103 5091 if dest == "default-push":
5104 5092 raise util.Abort(_("default repository not configured!"),
5105 5093 hint=_('see the "path" section in "hg help config"'))
5106 5094 else:
5107 5095 raise
5108 5096
5109 5097 if revs:
5110 5098 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
5111 5099
5112 5100 repo._subtoppath = dest
5113 5101 try:
5114 5102 # push subrepos depth-first for coherent ordering
5115 5103 c = repo['']
5116 5104 subs = c.substate # only repos that are committed
5117 5105 for s in sorted(subs):
5118 5106 result = c.sub(s).push(opts)
5119 5107 if result == 0:
5120 5108 return not result
5121 5109 finally:
5122 5110 del repo._subtoppath
5123 5111 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
5124 5112 newbranch=opts.get('new_branch'),
5125 5113 bookmarks=opts.get('bookmark', ()))
5126 5114
5127 5115 result = not pushop.cgresult
5128 5116
5129 5117 if pushop.bkresult is not None:
5130 5118 if pushop.bkresult == 2:
5131 5119 result = 2
5132 5120 elif not result and pushop.bkresult:
5133 5121 result = 2
5134 5122
5135 5123 return result
5136 5124
5137 5125 @command('recover', [])
5138 5126 def recover(ui, repo):
5139 5127 """roll back an interrupted transaction
5140 5128
5141 5129 Recover from an interrupted commit or pull.
5142 5130
5143 5131 This command tries to fix the repository status after an
5144 5132 interrupted operation. It should only be necessary when Mercurial
5145 5133 suggests it.
5146 5134
5147 5135 Returns 0 if successful, 1 if nothing to recover or verify fails.
5148 5136 """
5149 5137 if repo.recover():
5150 5138 return hg.verify(repo)
5151 5139 return 1
5152 5140
5153 5141 @command('^remove|rm',
5154 5142 [('A', 'after', None, _('record delete for missing files')),
5155 5143 ('f', 'force', None,
5156 5144 _('remove (and delete) file even if added or modified')),
5157 5145 ] + subrepoopts + walkopts,
5158 5146 _('[OPTION]... FILE...'),
5159 5147 inferrepo=True)
5160 5148 def remove(ui, repo, *pats, **opts):
5161 5149 """remove the specified files on the next commit
5162 5150
5163 5151 Schedule the indicated files for removal from the current branch.
5164 5152
5165 5153 This command schedules the files to be removed at the next commit.
5166 5154 To undo a remove before that, see :hg:`revert`. To undo added
5167 5155 files, see :hg:`forget`.
5168 5156
5169 5157 .. container:: verbose
5170 5158
5171 5159 -A/--after can be used to remove only files that have already
5172 5160 been deleted, -f/--force can be used to force deletion, and -Af
5173 5161 can be used to remove files from the next revision without
5174 5162 deleting them from the working directory.
5175 5163
5176 5164 The following table details the behavior of remove for different
5177 5165 file states (columns) and option combinations (rows). The file
5178 5166 states are Added [A], Clean [C], Modified [M] and Missing [!]
5179 5167 (as reported by :hg:`status`). The actions are Warn, Remove
5180 5168 (from branch) and Delete (from disk):
5181 5169
5182 5170 ========= == == == ==
5183 5171 opt/state A C M !
5184 5172 ========= == == == ==
5185 5173 none W RD W R
5186 5174 -f R RD RD R
5187 5175 -A W W W R
5188 5176 -Af R R R R
5189 5177 ========= == == == ==
5190 5178
5191 5179 Note that remove never deletes files in Added [A] state from the
5192 5180 working directory, not even if option --force is specified.
5193 5181
5194 5182 Returns 0 on success, 1 if any warnings encountered.
5195 5183 """
5196 5184
5197 5185 after, force = opts.get('after'), opts.get('force')
5198 5186 if not pats and not after:
5199 5187 raise util.Abort(_('no files specified'))
5200 5188
5201 5189 m = scmutil.match(repo[None], pats, opts)
5202 5190 subrepos = opts.get('subrepos')
5203 5191 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
5204 5192
5205 5193 @command('rename|move|mv',
5206 5194 [('A', 'after', None, _('record a rename that has already occurred')),
5207 5195 ('f', 'force', None, _('forcibly copy over an existing managed file')),
5208 5196 ] + walkopts + dryrunopts,
5209 5197 _('[OPTION]... SOURCE... DEST'))
5210 5198 def rename(ui, repo, *pats, **opts):
5211 5199 """rename files; equivalent of copy + remove
5212 5200
5213 5201 Mark dest as copies of sources; mark sources for deletion. If dest
5214 5202 is a directory, copies are put in that directory. If dest is a
5215 5203 file, there can only be one source.
5216 5204
5217 5205 By default, this command copies the contents of files as they
5218 5206 exist in the working directory. If invoked with -A/--after, the
5219 5207 operation is recorded, but no copying is performed.
5220 5208
5221 5209 This command takes effect at the next commit. To undo a rename
5222 5210 before that, see :hg:`revert`.
5223 5211
5224 5212 Returns 0 on success, 1 if errors are encountered.
5225 5213 """
5226 5214 wlock = repo.wlock(False)
5227 5215 try:
5228 5216 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5229 5217 finally:
5230 5218 wlock.release()
5231 5219
5232 5220 @command('resolve',
5233 5221 [('a', 'all', None, _('select all unresolved files')),
5234 5222 ('l', 'list', None, _('list state of files needing merge')),
5235 5223 ('m', 'mark', None, _('mark files as resolved')),
5236 5224 ('u', 'unmark', None, _('mark files as unresolved')),
5237 5225 ('n', 'no-status', None, _('hide status prefix'))]
5238 5226 + mergetoolopts + walkopts + formatteropts,
5239 5227 _('[OPTION]... [FILE]...'),
5240 5228 inferrepo=True)
5241 5229 def resolve(ui, repo, *pats, **opts):
5242 5230 """redo merges or set/view the merge status of files
5243 5231
5244 5232 Merges with unresolved conflicts are often the result of
5245 5233 non-interactive merging using the ``internal:merge`` configuration
5246 5234 setting, or a command-line merge tool like ``diff3``. The resolve
5247 5235 command is used to manage the files involved in a merge, after
5248 5236 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5249 5237 working directory must have two parents). See :hg:`help
5250 5238 merge-tools` for information on configuring merge tools.
5251 5239
5252 5240 The resolve command can be used in the following ways:
5253 5241
5254 5242 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
5255 5243 files, discarding any previous merge attempts. Re-merging is not
5256 5244 performed for files already marked as resolved. Use ``--all/-a``
5257 5245 to select all unresolved files. ``--tool`` can be used to specify
5258 5246 the merge tool used for the given files. It overrides the HGMERGE
5259 5247 environment variable and your configuration files. Previous file
5260 5248 contents are saved with a ``.orig`` suffix.
5261 5249
5262 5250 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5263 5251 (e.g. after having manually fixed-up the files). The default is
5264 5252 to mark all unresolved files.
5265 5253
5266 5254 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5267 5255 default is to mark all resolved files.
5268 5256
5269 5257 - :hg:`resolve -l`: list files which had or still have conflicts.
5270 5258 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5271 5259
5272 5260 Note that Mercurial will not let you commit files with unresolved
5273 5261 merge conflicts. You must use :hg:`resolve -m ...` before you can
5274 5262 commit after a conflicting merge.
5275 5263
5276 5264 Returns 0 on success, 1 if any files fail a resolve attempt.
5277 5265 """
5278 5266
5279 5267 all, mark, unmark, show, nostatus = \
5280 5268 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
5281 5269
5282 5270 if (show and (mark or unmark)) or (mark and unmark):
5283 5271 raise util.Abort(_("too many options specified"))
5284 5272 if pats and all:
5285 5273 raise util.Abort(_("can't specify --all and patterns"))
5286 5274 if not (all or pats or show or mark or unmark):
5287 5275 raise util.Abort(_('no files or directories specified'),
5288 5276 hint=('use --all to remerge all files'))
5289 5277
5290 5278 if show:
5291 5279 fm = ui.formatter('resolve', opts)
5292 5280 ms = mergemod.mergestate(repo)
5293 5281 m = scmutil.match(repo[None], pats, opts)
5294 5282 for f in ms:
5295 5283 if not m(f):
5296 5284 continue
5297 5285 l = 'resolve.' + {'u': 'unresolved', 'r': 'resolved'}[ms[f]]
5298 5286 fm.startitem()
5299 5287 fm.condwrite(not nostatus, 'status', '%s ', ms[f].upper(), label=l)
5300 5288 fm.write('path', '%s\n', f, label=l)
5301 5289 fm.end()
5302 5290 return 0
5303 5291
5304 5292 wlock = repo.wlock()
5305 5293 try:
5306 5294 ms = mergemod.mergestate(repo)
5307 5295
5308 5296 if not (ms.active() or repo.dirstate.p2() != nullid):
5309 5297 raise util.Abort(
5310 5298 _('resolve command not applicable when not merging'))
5311 5299
5312 5300 m = scmutil.match(repo[None], pats, opts)
5313 5301 ret = 0
5314 5302 didwork = False
5315 5303
5316 5304 for f in ms:
5317 5305 if not m(f):
5318 5306 continue
5319 5307
5320 5308 didwork = True
5321 5309
5322 5310 if mark:
5323 5311 ms.mark(f, "r")
5324 5312 elif unmark:
5325 5313 ms.mark(f, "u")
5326 5314 else:
5327 5315 wctx = repo[None]
5328 5316
5329 5317 # backup pre-resolve (merge uses .orig for its own purposes)
5330 5318 a = repo.wjoin(f)
5331 5319 util.copyfile(a, a + ".resolve")
5332 5320
5333 5321 try:
5334 5322 # resolve file
5335 5323 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
5336 5324 'resolve')
5337 5325 if ms.resolve(f, wctx):
5338 5326 ret = 1
5339 5327 finally:
5340 5328 ui.setconfig('ui', 'forcemerge', '', 'resolve')
5341 5329 ms.commit()
5342 5330
5343 5331 # replace filemerge's .orig file with our resolve file
5344 5332 util.rename(a + ".resolve", a + ".orig")
5345 5333
5346 5334 ms.commit()
5347 5335
5348 5336 if not didwork and pats:
5349 5337 ui.warn(_("arguments do not match paths that need resolving\n"))
5350 5338
5351 5339 finally:
5352 5340 wlock.release()
5353 5341
5354 5342 # Nudge users into finishing an unfinished operation
5355 5343 if not list(ms.unresolved()):
5356 5344 ui.status(_('(no more unresolved files)\n'))
5357 5345
5358 5346 return ret
5359 5347
5360 5348 @command('revert',
5361 5349 [('a', 'all', None, _('revert all changes when no arguments given')),
5362 5350 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5363 5351 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5364 5352 ('C', 'no-backup', None, _('do not save backup copies of files')),
5365 5353 ] + walkopts + dryrunopts,
5366 5354 _('[OPTION]... [-r REV] [NAME]...'))
5367 5355 def revert(ui, repo, *pats, **opts):
5368 5356 """restore files to their checkout state
5369 5357
5370 5358 .. note::
5371 5359
5372 5360 To check out earlier revisions, you should use :hg:`update REV`.
5373 5361 To cancel an uncommitted merge (and lose your changes),
5374 5362 use :hg:`update --clean .`.
5375 5363
5376 5364 With no revision specified, revert the specified files or directories
5377 5365 to the contents they had in the parent of the working directory.
5378 5366 This restores the contents of files to an unmodified
5379 5367 state and unschedules adds, removes, copies, and renames. If the
5380 5368 working directory has two parents, you must explicitly specify a
5381 5369 revision.
5382 5370
5383 5371 Using the -r/--rev or -d/--date options, revert the given files or
5384 5372 directories to their states as of a specific revision. Because
5385 5373 revert does not change the working directory parents, this will
5386 5374 cause these files to appear modified. This can be helpful to "back
5387 5375 out" some or all of an earlier change. See :hg:`backout` for a
5388 5376 related method.
5389 5377
5390 5378 Modified files are saved with a .orig suffix before reverting.
5391 5379 To disable these backups, use --no-backup.
5392 5380
5393 5381 See :hg:`help dates` for a list of formats valid for -d/--date.
5394 5382
5395 5383 Returns 0 on success.
5396 5384 """
5397 5385
5398 5386 if opts.get("date"):
5399 5387 if opts.get("rev"):
5400 5388 raise util.Abort(_("you can't specify a revision and a date"))
5401 5389 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5402 5390
5403 5391 parent, p2 = repo.dirstate.parents()
5404 5392 if not opts.get('rev') and p2 != nullid:
5405 5393 # revert after merge is a trap for new users (issue2915)
5406 5394 raise util.Abort(_('uncommitted merge with no revision specified'),
5407 5395 hint=_('use "hg update" or see "hg help revert"'))
5408 5396
5409 5397 ctx = scmutil.revsingle(repo, opts.get('rev'))
5410 5398
5411 5399 if not pats and not opts.get('all'):
5412 5400 msg = _("no files or directories specified")
5413 5401 if p2 != nullid:
5414 5402 hint = _("uncommitted merge, use --all to discard all changes,"
5415 5403 " or 'hg update -C .' to abort the merge")
5416 5404 raise util.Abort(msg, hint=hint)
5417 5405 dirty = util.any(repo.status())
5418 5406 node = ctx.node()
5419 5407 if node != parent:
5420 5408 if dirty:
5421 5409 hint = _("uncommitted changes, use --all to discard all"
5422 5410 " changes, or 'hg update %s' to update") % ctx.rev()
5423 5411 else:
5424 5412 hint = _("use --all to revert all files,"
5425 5413 " or 'hg update %s' to update") % ctx.rev()
5426 5414 elif dirty:
5427 5415 hint = _("uncommitted changes, use --all to discard all changes")
5428 5416 else:
5429 5417 hint = _("use --all to revert all files")
5430 5418 raise util.Abort(msg, hint=hint)
5431 5419
5432 5420 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
5433 5421
5434 5422 @command('rollback', dryrunopts +
5435 5423 [('f', 'force', False, _('ignore safety measures'))])
5436 5424 def rollback(ui, repo, **opts):
5437 5425 """roll back the last transaction (DANGEROUS) (DEPRECATED)
5438 5426
5439 5427 Please use :hg:`commit --amend` instead of rollback to correct
5440 5428 mistakes in the last commit.
5441 5429
5442 5430 This command should be used with care. There is only one level of
5443 5431 rollback, and there is no way to undo a rollback. It will also
5444 5432 restore the dirstate at the time of the last transaction, losing
5445 5433 any dirstate changes since that time. This command does not alter
5446 5434 the working directory.
5447 5435
5448 5436 Transactions are used to encapsulate the effects of all commands
5449 5437 that create new changesets or propagate existing changesets into a
5450 5438 repository.
5451 5439
5452 5440 .. container:: verbose
5453 5441
5454 5442 For example, the following commands are transactional, and their
5455 5443 effects can be rolled back:
5456 5444
5457 5445 - commit
5458 5446 - import
5459 5447 - pull
5460 5448 - push (with this repository as the destination)
5461 5449 - unbundle
5462 5450
5463 5451 To avoid permanent data loss, rollback will refuse to rollback a
5464 5452 commit transaction if it isn't checked out. Use --force to
5465 5453 override this protection.
5466 5454
5467 5455 This command is not intended for use on public repositories. Once
5468 5456 changes are visible for pull by other users, rolling a transaction
5469 5457 back locally is ineffective (someone else may already have pulled
5470 5458 the changes). Furthermore, a race is possible with readers of the
5471 5459 repository; for example an in-progress pull from the repository
5472 5460 may fail if a rollback is performed.
5473 5461
5474 5462 Returns 0 on success, 1 if no rollback data is available.
5475 5463 """
5476 5464 return repo.rollback(dryrun=opts.get('dry_run'),
5477 5465 force=opts.get('force'))
5478 5466
5479 5467 @command('root', [])
5480 5468 def root(ui, repo):
5481 5469 """print the root (top) of the current working directory
5482 5470
5483 5471 Print the root directory of the current repository.
5484 5472
5485 5473 Returns 0 on success.
5486 5474 """
5487 5475 ui.write(repo.root + "\n")
5488 5476
5489 5477 @command('^serve',
5490 5478 [('A', 'accesslog', '', _('name of access log file to write to'),
5491 5479 _('FILE')),
5492 5480 ('d', 'daemon', None, _('run server in background')),
5493 5481 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('FILE')),
5494 5482 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5495 5483 # use string type, then we can check if something was passed
5496 5484 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5497 5485 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5498 5486 _('ADDR')),
5499 5487 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5500 5488 _('PREFIX')),
5501 5489 ('n', 'name', '',
5502 5490 _('name to show in web pages (default: working directory)'), _('NAME')),
5503 5491 ('', 'web-conf', '',
5504 5492 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
5505 5493 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5506 5494 _('FILE')),
5507 5495 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5508 5496 ('', 'stdio', None, _('for remote clients')),
5509 5497 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
5510 5498 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5511 5499 ('', 'style', '', _('template style to use'), _('STYLE')),
5512 5500 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5513 5501 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
5514 5502 _('[OPTION]...'),
5515 5503 optionalrepo=True)
5516 5504 def serve(ui, repo, **opts):
5517 5505 """start stand-alone webserver
5518 5506
5519 5507 Start a local HTTP repository browser and pull server. You can use
5520 5508 this for ad-hoc sharing and browsing of repositories. It is
5521 5509 recommended to use a real web server to serve a repository for
5522 5510 longer periods of time.
5523 5511
5524 5512 Please note that the server does not implement access control.
5525 5513 This means that, by default, anybody can read from the server and
5526 5514 nobody can write to it by default. Set the ``web.allow_push``
5527 5515 option to ``*`` to allow everybody to push to the server. You
5528 5516 should use a real web server if you need to authenticate users.
5529 5517
5530 5518 By default, the server logs accesses to stdout and errors to
5531 5519 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5532 5520 files.
5533 5521
5534 5522 To have the server choose a free port number to listen on, specify
5535 5523 a port number of 0; in this case, the server will print the port
5536 5524 number it uses.
5537 5525
5538 5526 Returns 0 on success.
5539 5527 """
5540 5528
5541 5529 if opts["stdio"] and opts["cmdserver"]:
5542 5530 raise util.Abort(_("cannot use --stdio with --cmdserver"))
5543 5531
5544 5532 if opts["stdio"]:
5545 5533 if repo is None:
5546 5534 raise error.RepoError(_("there is no Mercurial repository here"
5547 5535 " (.hg not found)"))
5548 5536 s = sshserver.sshserver(ui, repo)
5549 5537 s.serve_forever()
5550 5538
5551 5539 if opts["cmdserver"]:
5552 5540 service = commandserver.createservice(ui, repo, opts)
5553 5541 return cmdutil.service(opts, initfn=service.init, runfn=service.run)
5554 5542
5555 5543 # this way we can check if something was given in the command-line
5556 5544 if opts.get('port'):
5557 5545 opts['port'] = util.getport(opts.get('port'))
5558 5546
5559 5547 baseui = repo and repo.baseui or ui
5560 5548 optlist = ("name templates style address port prefix ipv6"
5561 5549 " accesslog errorlog certificate encoding")
5562 5550 for o in optlist.split():
5563 5551 val = opts.get(o, '')
5564 5552 if val in (None, ''): # should check against default options instead
5565 5553 continue
5566 5554 baseui.setconfig("web", o, val, 'serve')
5567 5555 if repo and repo.ui != baseui:
5568 5556 repo.ui.setconfig("web", o, val, 'serve')
5569 5557
5570 5558 o = opts.get('web_conf') or opts.get('webdir_conf')
5571 5559 if not o:
5572 5560 if not repo:
5573 5561 raise error.RepoError(_("there is no Mercurial repository"
5574 5562 " here (.hg not found)"))
5575 5563 o = repo
5576 5564
5577 5565 app = hgweb.hgweb(o, baseui=baseui)
5578 5566 service = httpservice(ui, app, opts)
5579 5567 cmdutil.service(opts, initfn=service.init, runfn=service.run)
5580 5568
5581 5569 class httpservice(object):
5582 5570 def __init__(self, ui, app, opts):
5583 5571 self.ui = ui
5584 5572 self.app = app
5585 5573 self.opts = opts
5586 5574
5587 5575 def init(self):
5588 5576 util.setsignalhandler()
5589 5577 self.httpd = hgweb_server.create_server(self.ui, self.app)
5590 5578
5591 5579 if self.opts['port'] and not self.ui.verbose:
5592 5580 return
5593 5581
5594 5582 if self.httpd.prefix:
5595 5583 prefix = self.httpd.prefix.strip('/') + '/'
5596 5584 else:
5597 5585 prefix = ''
5598 5586
5599 5587 port = ':%d' % self.httpd.port
5600 5588 if port == ':80':
5601 5589 port = ''
5602 5590
5603 5591 bindaddr = self.httpd.addr
5604 5592 if bindaddr == '0.0.0.0':
5605 5593 bindaddr = '*'
5606 5594 elif ':' in bindaddr: # IPv6
5607 5595 bindaddr = '[%s]' % bindaddr
5608 5596
5609 5597 fqaddr = self.httpd.fqaddr
5610 5598 if ':' in fqaddr:
5611 5599 fqaddr = '[%s]' % fqaddr
5612 5600 if self.opts['port']:
5613 5601 write = self.ui.status
5614 5602 else:
5615 5603 write = self.ui.write
5616 5604 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
5617 5605 (fqaddr, port, prefix, bindaddr, self.httpd.port))
5618 5606 self.ui.flush() # avoid buffering of status message
5619 5607
5620 5608 def run(self):
5621 5609 self.httpd.serve_forever()
5622 5610
5623 5611
5624 5612 @command('^status|st',
5625 5613 [('A', 'all', None, _('show status of all files')),
5626 5614 ('m', 'modified', None, _('show only modified files')),
5627 5615 ('a', 'added', None, _('show only added files')),
5628 5616 ('r', 'removed', None, _('show only removed files')),
5629 5617 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5630 5618 ('c', 'clean', None, _('show only files without changes')),
5631 5619 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5632 5620 ('i', 'ignored', None, _('show only ignored files')),
5633 5621 ('n', 'no-status', None, _('hide status prefix')),
5634 5622 ('C', 'copies', None, _('show source of copied files')),
5635 5623 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5636 5624 ('', 'rev', [], _('show difference from revision'), _('REV')),
5637 5625 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5638 5626 ] + walkopts + subrepoopts + formatteropts,
5639 5627 _('[OPTION]... [FILE]...'),
5640 5628 inferrepo=True)
5641 5629 def status(ui, repo, *pats, **opts):
5642 5630 """show changed files in the working directory
5643 5631
5644 5632 Show status of files in the repository. If names are given, only
5645 5633 files that match are shown. Files that are clean or ignored or
5646 5634 the source of a copy/move operation, are not listed unless
5647 5635 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5648 5636 Unless options described with "show only ..." are given, the
5649 5637 options -mardu are used.
5650 5638
5651 5639 Option -q/--quiet hides untracked (unknown and ignored) files
5652 5640 unless explicitly requested with -u/--unknown or -i/--ignored.
5653 5641
5654 5642 .. note::
5655 5643
5656 5644 status may appear to disagree with diff if permissions have
5657 5645 changed or a merge has occurred. The standard diff format does
5658 5646 not report permission changes and diff only reports changes
5659 5647 relative to one merge parent.
5660 5648
5661 5649 If one revision is given, it is used as the base revision.
5662 5650 If two revisions are given, the differences between them are
5663 5651 shown. The --change option can also be used as a shortcut to list
5664 5652 the changed files of a revision from its first parent.
5665 5653
5666 5654 The codes used to show the status of files are::
5667 5655
5668 5656 M = modified
5669 5657 A = added
5670 5658 R = removed
5671 5659 C = clean
5672 5660 ! = missing (deleted by non-hg command, but still tracked)
5673 5661 ? = not tracked
5674 5662 I = ignored
5675 5663 = origin of the previous file (with --copies)
5676 5664
5677 5665 .. container:: verbose
5678 5666
5679 5667 Examples:
5680 5668
5681 5669 - show changes in the working directory relative to a
5682 5670 changeset::
5683 5671
5684 5672 hg status --rev 9353
5685 5673
5686 5674 - show all changes including copies in an existing changeset::
5687 5675
5688 5676 hg status --copies --change 9353
5689 5677
5690 5678 - get a NUL separated list of added files, suitable for xargs::
5691 5679
5692 5680 hg status -an0
5693 5681
5694 5682 Returns 0 on success.
5695 5683 """
5696 5684
5697 5685 revs = opts.get('rev')
5698 5686 change = opts.get('change')
5699 5687
5700 5688 if revs and change:
5701 5689 msg = _('cannot specify --rev and --change at the same time')
5702 5690 raise util.Abort(msg)
5703 5691 elif change:
5704 5692 node2 = scmutil.revsingle(repo, change, None).node()
5705 5693 node1 = repo[node2].p1().node()
5706 5694 else:
5707 5695 node1, node2 = scmutil.revpair(repo, revs)
5708 5696
5709 5697 cwd = (pats and repo.getcwd()) or ''
5710 5698 end = opts.get('print0') and '\0' or '\n'
5711 5699 copy = {}
5712 5700 states = 'modified added removed deleted unknown ignored clean'.split()
5713 5701 show = [k for k in states if opts.get(k)]
5714 5702 if opts.get('all'):
5715 5703 show += ui.quiet and (states[:4] + ['clean']) or states
5716 5704 if not show:
5717 5705 show = ui.quiet and states[:4] or states[:5]
5718 5706
5719 5707 stat = repo.status(node1, node2, scmutil.match(repo[node2], pats, opts),
5720 5708 'ignored' in show, 'clean' in show, 'unknown' in show,
5721 5709 opts.get('subrepos'))
5722 5710 changestates = zip(states, 'MAR!?IC', stat)
5723 5711
5724 5712 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
5725 5713 copy = copies.pathcopies(repo[node1], repo[node2])
5726 5714
5727 5715 fm = ui.formatter('status', opts)
5728 5716 fmt = '%s' + end
5729 5717 showchar = not opts.get('no_status')
5730 5718
5731 5719 for state, char, files in changestates:
5732 5720 if state in show:
5733 5721 label = 'status.' + state
5734 5722 for f in files:
5735 5723 fm.startitem()
5736 5724 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5737 5725 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
5738 5726 if f in copy:
5739 5727 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
5740 5728 label='status.copied')
5741 5729 fm.end()
5742 5730
5743 5731 @command('^summary|sum',
5744 5732 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5745 5733 def summary(ui, repo, **opts):
5746 5734 """summarize working directory state
5747 5735
5748 5736 This generates a brief summary of the working directory state,
5749 5737 including parents, branch, commit status, and available updates.
5750 5738
5751 5739 With the --remote option, this will check the default paths for
5752 5740 incoming and outgoing changes. This can be time-consuming.
5753 5741
5754 5742 Returns 0 on success.
5755 5743 """
5756 5744
5757 5745 ctx = repo[None]
5758 5746 parents = ctx.parents()
5759 5747 pnode = parents[0].node()
5760 5748 marks = []
5761 5749
5762 5750 for p in parents:
5763 5751 # label with log.changeset (instead of log.parent) since this
5764 5752 # shows a working directory parent *changeset*:
5765 5753 # i18n: column positioning for "hg summary"
5766 5754 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5767 5755 label='log.changeset changeset.%s' % p.phasestr())
5768 5756 ui.write(' '.join(p.tags()), label='log.tag')
5769 5757 if p.bookmarks():
5770 5758 marks.extend(p.bookmarks())
5771 5759 if p.rev() == -1:
5772 5760 if not len(repo):
5773 5761 ui.write(_(' (empty repository)'))
5774 5762 else:
5775 5763 ui.write(_(' (no revision checked out)'))
5776 5764 ui.write('\n')
5777 5765 if p.description():
5778 5766 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5779 5767 label='log.summary')
5780 5768
5781 5769 branch = ctx.branch()
5782 5770 bheads = repo.branchheads(branch)
5783 5771 # i18n: column positioning for "hg summary"
5784 5772 m = _('branch: %s\n') % branch
5785 5773 if branch != 'default':
5786 5774 ui.write(m, label='log.branch')
5787 5775 else:
5788 5776 ui.status(m, label='log.branch')
5789 5777
5790 5778 if marks:
5791 5779 current = repo._bookmarkcurrent
5792 5780 # i18n: column positioning for "hg summary"
5793 5781 ui.write(_('bookmarks:'), label='log.bookmark')
5794 5782 if current is not None:
5795 5783 if current in marks:
5796 5784 ui.write(' *' + current, label='bookmarks.current')
5797 5785 marks.remove(current)
5798 5786 else:
5799 5787 ui.write(' [%s]' % current, label='bookmarks.current')
5800 5788 for m in marks:
5801 5789 ui.write(' ' + m, label='log.bookmark')
5802 5790 ui.write('\n', label='log.bookmark')
5803 5791
5804 5792 status = repo.status(unknown=True)
5805 5793
5806 5794 c = repo.dirstate.copies()
5807 5795 copied, renamed = [], []
5808 5796 for d, s in c.iteritems():
5809 5797 if s in status.removed:
5810 5798 status.removed.remove(s)
5811 5799 renamed.append(d)
5812 5800 else:
5813 5801 copied.append(d)
5814 5802 if d in status.added:
5815 5803 status.added.remove(d)
5816 5804
5817 5805 ms = mergemod.mergestate(repo)
5818 5806 unresolved = [f for f in ms if ms[f] == 'u']
5819 5807
5820 5808 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5821 5809
5822 5810 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5823 5811 (ui.label(_('%d added'), 'status.added'), status.added),
5824 5812 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5825 5813 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5826 5814 (ui.label(_('%d copied'), 'status.copied'), copied),
5827 5815 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5828 5816 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5829 5817 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5830 5818 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5831 5819 t = []
5832 5820 for l, s in labels:
5833 5821 if s:
5834 5822 t.append(l % len(s))
5835 5823
5836 5824 t = ', '.join(t)
5837 5825 cleanworkdir = False
5838 5826
5839 5827 if repo.vfs.exists('updatestate'):
5840 5828 t += _(' (interrupted update)')
5841 5829 elif len(parents) > 1:
5842 5830 t += _(' (merge)')
5843 5831 elif branch != parents[0].branch():
5844 5832 t += _(' (new branch)')
5845 5833 elif (parents[0].closesbranch() and
5846 5834 pnode in repo.branchheads(branch, closed=True)):
5847 5835 t += _(' (head closed)')
5848 5836 elif not (status.modified or status.added or status.removed or renamed or
5849 5837 copied or subs):
5850 5838 t += _(' (clean)')
5851 5839 cleanworkdir = True
5852 5840 elif pnode not in bheads:
5853 5841 t += _(' (new branch head)')
5854 5842
5855 5843 if cleanworkdir:
5856 5844 # i18n: column positioning for "hg summary"
5857 5845 ui.status(_('commit: %s\n') % t.strip())
5858 5846 else:
5859 5847 # i18n: column positioning for "hg summary"
5860 5848 ui.write(_('commit: %s\n') % t.strip())
5861 5849
5862 5850 # all ancestors of branch heads - all ancestors of parent = new csets
5863 5851 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5864 5852 bheads))
5865 5853
5866 5854 if new == 0:
5867 5855 # i18n: column positioning for "hg summary"
5868 5856 ui.status(_('update: (current)\n'))
5869 5857 elif pnode not in bheads:
5870 5858 # i18n: column positioning for "hg summary"
5871 5859 ui.write(_('update: %d new changesets (update)\n') % new)
5872 5860 else:
5873 5861 # i18n: column positioning for "hg summary"
5874 5862 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5875 5863 (new, len(bheads)))
5876 5864
5877 5865 cmdutil.summaryhooks(ui, repo)
5878 5866
5879 5867 if opts.get('remote'):
5880 5868 needsincoming, needsoutgoing = True, True
5881 5869 else:
5882 5870 needsincoming, needsoutgoing = False, False
5883 5871 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5884 5872 if i:
5885 5873 needsincoming = True
5886 5874 if o:
5887 5875 needsoutgoing = True
5888 5876 if not needsincoming and not needsoutgoing:
5889 5877 return
5890 5878
5891 5879 def getincoming():
5892 5880 source, branches = hg.parseurl(ui.expandpath('default'))
5893 5881 sbranch = branches[0]
5894 5882 try:
5895 5883 other = hg.peer(repo, {}, source)
5896 5884 except error.RepoError:
5897 5885 if opts.get('remote'):
5898 5886 raise
5899 5887 return source, sbranch, None, None, None
5900 5888 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5901 5889 if revs:
5902 5890 revs = [other.lookup(rev) for rev in revs]
5903 5891 ui.debug('comparing with %s\n' % util.hidepassword(source))
5904 5892 repo.ui.pushbuffer()
5905 5893 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5906 5894 repo.ui.popbuffer()
5907 5895 return source, sbranch, other, commoninc, commoninc[1]
5908 5896
5909 5897 if needsincoming:
5910 5898 source, sbranch, sother, commoninc, incoming = getincoming()
5911 5899 else:
5912 5900 source = sbranch = sother = commoninc = incoming = None
5913 5901
5914 5902 def getoutgoing():
5915 5903 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5916 5904 dbranch = branches[0]
5917 5905 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5918 5906 if source != dest:
5919 5907 try:
5920 5908 dother = hg.peer(repo, {}, dest)
5921 5909 except error.RepoError:
5922 5910 if opts.get('remote'):
5923 5911 raise
5924 5912 return dest, dbranch, None, None
5925 5913 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5926 5914 elif sother is None:
5927 5915 # there is no explicit destination peer, but source one is invalid
5928 5916 return dest, dbranch, None, None
5929 5917 else:
5930 5918 dother = sother
5931 5919 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5932 5920 common = None
5933 5921 else:
5934 5922 common = commoninc
5935 5923 if revs:
5936 5924 revs = [repo.lookup(rev) for rev in revs]
5937 5925 repo.ui.pushbuffer()
5938 5926 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5939 5927 commoninc=common)
5940 5928 repo.ui.popbuffer()
5941 5929 return dest, dbranch, dother, outgoing
5942 5930
5943 5931 if needsoutgoing:
5944 5932 dest, dbranch, dother, outgoing = getoutgoing()
5945 5933 else:
5946 5934 dest = dbranch = dother = outgoing = None
5947 5935
5948 5936 if opts.get('remote'):
5949 5937 t = []
5950 5938 if incoming:
5951 5939 t.append(_('1 or more incoming'))
5952 5940 o = outgoing.missing
5953 5941 if o:
5954 5942 t.append(_('%d outgoing') % len(o))
5955 5943 other = dother or sother
5956 5944 if 'bookmarks' in other.listkeys('namespaces'):
5957 5945 lmarks = repo.listkeys('bookmarks')
5958 5946 rmarks = other.listkeys('bookmarks')
5959 5947 diff = set(rmarks) - set(lmarks)
5960 5948 if len(diff) > 0:
5961 5949 t.append(_('%d incoming bookmarks') % len(diff))
5962 5950 diff = set(lmarks) - set(rmarks)
5963 5951 if len(diff) > 0:
5964 5952 t.append(_('%d outgoing bookmarks') % len(diff))
5965 5953
5966 5954 if t:
5967 5955 # i18n: column positioning for "hg summary"
5968 5956 ui.write(_('remote: %s\n') % (', '.join(t)))
5969 5957 else:
5970 5958 # i18n: column positioning for "hg summary"
5971 5959 ui.status(_('remote: (synced)\n'))
5972 5960
5973 5961 cmdutil.summaryremotehooks(ui, repo, opts,
5974 5962 ((source, sbranch, sother, commoninc),
5975 5963 (dest, dbranch, dother, outgoing)))
5976 5964
5977 5965 @command('tag',
5978 5966 [('f', 'force', None, _('force tag')),
5979 5967 ('l', 'local', None, _('make the tag local')),
5980 5968 ('r', 'rev', '', _('revision to tag'), _('REV')),
5981 5969 ('', 'remove', None, _('remove a tag')),
5982 5970 # -l/--local is already there, commitopts cannot be used
5983 5971 ('e', 'edit', None, _('invoke editor on commit messages')),
5984 5972 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5985 5973 ] + commitopts2,
5986 5974 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5987 5975 def tag(ui, repo, name1, *names, **opts):
5988 5976 """add one or more tags for the current or given revision
5989 5977
5990 5978 Name a particular revision using <name>.
5991 5979
5992 5980 Tags are used to name particular revisions of the repository and are
5993 5981 very useful to compare different revisions, to go back to significant
5994 5982 earlier versions or to mark branch points as releases, etc. Changing
5995 5983 an existing tag is normally disallowed; use -f/--force to override.
5996 5984
5997 5985 If no revision is given, the parent of the working directory is
5998 5986 used.
5999 5987
6000 5988 To facilitate version control, distribution, and merging of tags,
6001 5989 they are stored as a file named ".hgtags" which is managed similarly
6002 5990 to other project files and can be hand-edited if necessary. This
6003 5991 also means that tagging creates a new commit. The file
6004 5992 ".hg/localtags" is used for local tags (not shared among
6005 5993 repositories).
6006 5994
6007 5995 Tag commits are usually made at the head of a branch. If the parent
6008 5996 of the working directory is not a branch head, :hg:`tag` aborts; use
6009 5997 -f/--force to force the tag commit to be based on a non-head
6010 5998 changeset.
6011 5999
6012 6000 See :hg:`help dates` for a list of formats valid for -d/--date.
6013 6001
6014 6002 Since tag names have priority over branch names during revision
6015 6003 lookup, using an existing branch name as a tag name is discouraged.
6016 6004
6017 6005 Returns 0 on success.
6018 6006 """
6019 6007 wlock = lock = None
6020 6008 try:
6021 6009 wlock = repo.wlock()
6022 6010 lock = repo.lock()
6023 6011 rev_ = "."
6024 6012 names = [t.strip() for t in (name1,) + names]
6025 6013 if len(names) != len(set(names)):
6026 6014 raise util.Abort(_('tag names must be unique'))
6027 6015 for n in names:
6028 6016 scmutil.checknewlabel(repo, n, 'tag')
6029 6017 if not n:
6030 6018 raise util.Abort(_('tag names cannot consist entirely of '
6031 6019 'whitespace'))
6032 6020 if opts.get('rev') and opts.get('remove'):
6033 6021 raise util.Abort(_("--rev and --remove are incompatible"))
6034 6022 if opts.get('rev'):
6035 6023 rev_ = opts['rev']
6036 6024 message = opts.get('message')
6037 6025 if opts.get('remove'):
6038 6026 expectedtype = opts.get('local') and 'local' or 'global'
6039 6027 for n in names:
6040 6028 if not repo.tagtype(n):
6041 6029 raise util.Abort(_("tag '%s' does not exist") % n)
6042 6030 if repo.tagtype(n) != expectedtype:
6043 6031 if expectedtype == 'global':
6044 6032 raise util.Abort(_("tag '%s' is not a global tag") % n)
6045 6033 else:
6046 6034 raise util.Abort(_("tag '%s' is not a local tag") % n)
6047 6035 rev_ = nullid
6048 6036 if not message:
6049 6037 # we don't translate commit messages
6050 6038 message = 'Removed tag %s' % ', '.join(names)
6051 6039 elif not opts.get('force'):
6052 6040 for n in names:
6053 6041 if n in repo.tags():
6054 6042 raise util.Abort(_("tag '%s' already exists "
6055 6043 "(use -f to force)") % n)
6056 6044 if not opts.get('local'):
6057 6045 p1, p2 = repo.dirstate.parents()
6058 6046 if p2 != nullid:
6059 6047 raise util.Abort(_('uncommitted merge'))
6060 6048 bheads = repo.branchheads()
6061 6049 if not opts.get('force') and bheads and p1 not in bheads:
6062 6050 raise util.Abort(_('not at a branch head (use -f to force)'))
6063 6051 r = scmutil.revsingle(repo, rev_).node()
6064 6052
6065 6053 if not message:
6066 6054 # we don't translate commit messages
6067 6055 message = ('Added tag %s for changeset %s' %
6068 6056 (', '.join(names), short(r)))
6069 6057
6070 6058 date = opts.get('date')
6071 6059 if date:
6072 6060 date = util.parsedate(date)
6073 6061
6074 6062 if opts.get('remove'):
6075 6063 editform = 'tag.remove'
6076 6064 else:
6077 6065 editform = 'tag.add'
6078 6066 editor = cmdutil.getcommiteditor(editform=editform, **opts)
6079 6067
6080 6068 # don't allow tagging the null rev
6081 6069 if (not opts.get('remove') and
6082 6070 scmutil.revsingle(repo, rev_).rev() == nullrev):
6083 6071 raise util.Abort(_("cannot tag null revision"))
6084 6072
6085 6073 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date,
6086 6074 editor=editor)
6087 6075 finally:
6088 6076 release(lock, wlock)
6089 6077
6090 6078 @command('tags', formatteropts, '')
6091 6079 def tags(ui, repo, **opts):
6092 6080 """list repository tags
6093 6081
6094 6082 This lists both regular and local tags. When the -v/--verbose
6095 6083 switch is used, a third column "local" is printed for local tags.
6096 6084
6097 6085 Returns 0 on success.
6098 6086 """
6099 6087
6100 6088 fm = ui.formatter('tags', opts)
6101 6089 hexfunc = fm.hexfunc
6102 6090 tagtype = ""
6103 6091
6104 6092 for t, n in reversed(repo.tagslist()):
6105 6093 hn = hexfunc(n)
6106 6094 label = 'tags.normal'
6107 6095 tagtype = ''
6108 6096 if repo.tagtype(t) == 'local':
6109 6097 label = 'tags.local'
6110 6098 tagtype = 'local'
6111 6099
6112 6100 fm.startitem()
6113 6101 fm.write('tag', '%s', t, label=label)
6114 6102 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
6115 6103 fm.condwrite(not ui.quiet, 'rev node', fmt,
6116 6104 repo.changelog.rev(n), hn, label=label)
6117 6105 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
6118 6106 tagtype, label=label)
6119 6107 fm.plain('\n')
6120 6108 fm.end()
6121 6109
6122 6110 @command('tip',
6123 6111 [('p', 'patch', None, _('show patch')),
6124 6112 ('g', 'git', None, _('use git extended diff format')),
6125 6113 ] + templateopts,
6126 6114 _('[-p] [-g]'))
6127 6115 def tip(ui, repo, **opts):
6128 6116 """show the tip revision (DEPRECATED)
6129 6117
6130 6118 The tip revision (usually just called the tip) is the changeset
6131 6119 most recently added to the repository (and therefore the most
6132 6120 recently changed head).
6133 6121
6134 6122 If you have just made a commit, that commit will be the tip. If
6135 6123 you have just pulled changes from another repository, the tip of
6136 6124 that repository becomes the current tip. The "tip" tag is special
6137 6125 and cannot be renamed or assigned to a different changeset.
6138 6126
6139 6127 This command is deprecated, please use :hg:`heads` instead.
6140 6128
6141 6129 Returns 0 on success.
6142 6130 """
6143 6131 displayer = cmdutil.show_changeset(ui, repo, opts)
6144 6132 displayer.show(repo['tip'])
6145 6133 displayer.close()
6146 6134
6147 6135 @command('unbundle',
6148 6136 [('u', 'update', None,
6149 6137 _('update to new branch head if changesets were unbundled'))],
6150 6138 _('[-u] FILE...'))
6151 6139 def unbundle(ui, repo, fname1, *fnames, **opts):
6152 6140 """apply one or more changegroup files
6153 6141
6154 6142 Apply one or more compressed changegroup files generated by the
6155 6143 bundle command.
6156 6144
6157 6145 Returns 0 on success, 1 if an update has unresolved files.
6158 6146 """
6159 6147 fnames = (fname1,) + fnames
6160 6148
6161 6149 lock = repo.lock()
6162 6150 try:
6163 6151 for fname in fnames:
6164 6152 f = hg.openpath(ui, fname)
6165 6153 gen = exchange.readbundle(ui, f, fname)
6166 6154 if isinstance(gen, bundle2.unbundle20):
6167 6155 tr = repo.transaction('unbundle')
6168 6156 try:
6169 6157 op = bundle2.processbundle(repo, gen, lambda: tr)
6170 6158 tr.close()
6171 6159 finally:
6172 6160 if tr:
6173 6161 tr.release()
6174 6162 changes = [r.get('result', 0)
6175 6163 for r in op.records['changegroup']]
6176 6164 modheads = changegroup.combineresults(changes)
6177 6165 else:
6178 6166 modheads = changegroup.addchangegroup(repo, gen, 'unbundle',
6179 6167 'bundle:' + fname)
6180 6168 finally:
6181 6169 lock.release()
6182 6170
6183 6171 return postincoming(ui, repo, modheads, opts.get('update'), None)
6184 6172
6185 6173 @command('^update|up|checkout|co',
6186 6174 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
6187 6175 ('c', 'check', None,
6188 6176 _('update across branches if no uncommitted changes')),
6189 6177 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6190 6178 ('r', 'rev', '', _('revision'), _('REV'))
6191 6179 ] + mergetoolopts,
6192 6180 _('[-c] [-C] [-d DATE] [[-r] REV]'))
6193 6181 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
6194 6182 tool=None):
6195 6183 """update working directory (or switch revisions)
6196 6184
6197 6185 Update the repository's working directory to the specified
6198 6186 changeset. If no changeset is specified, update to the tip of the
6199 6187 current named branch and move the current bookmark (see :hg:`help
6200 6188 bookmarks`).
6201 6189
6202 6190 Update sets the working directory's parent revision to the specified
6203 6191 changeset (see :hg:`help parents`).
6204 6192
6205 6193 If the changeset is not a descendant or ancestor of the working
6206 6194 directory's parent, the update is aborted. With the -c/--check
6207 6195 option, the working directory is checked for uncommitted changes; if
6208 6196 none are found, the working directory is updated to the specified
6209 6197 changeset.
6210 6198
6211 6199 .. container:: verbose
6212 6200
6213 6201 The following rules apply when the working directory contains
6214 6202 uncommitted changes:
6215 6203
6216 6204 1. If neither -c/--check nor -C/--clean is specified, and if
6217 6205 the requested changeset is an ancestor or descendant of
6218 6206 the working directory's parent, the uncommitted changes
6219 6207 are merged into the requested changeset and the merged
6220 6208 result is left uncommitted. If the requested changeset is
6221 6209 not an ancestor or descendant (that is, it is on another
6222 6210 branch), the update is aborted and the uncommitted changes
6223 6211 are preserved.
6224 6212
6225 6213 2. With the -c/--check option, the update is aborted and the
6226 6214 uncommitted changes are preserved.
6227 6215
6228 6216 3. With the -C/--clean option, uncommitted changes are discarded and
6229 6217 the working directory is updated to the requested changeset.
6230 6218
6231 6219 To cancel an uncommitted merge (and lose your changes), use
6232 6220 :hg:`update --clean .`.
6233 6221
6234 6222 Use null as the changeset to remove the working directory (like
6235 6223 :hg:`clone -U`).
6236 6224
6237 6225 If you want to revert just one file to an older revision, use
6238 6226 :hg:`revert [-r REV] NAME`.
6239 6227
6240 6228 See :hg:`help dates` for a list of formats valid for -d/--date.
6241 6229
6242 6230 Returns 0 on success, 1 if there are unresolved files.
6243 6231 """
6244 6232 if rev and node:
6245 6233 raise util.Abort(_("please specify just one revision"))
6246 6234
6247 6235 if rev is None or rev == '':
6248 6236 rev = node
6249 6237
6250 6238 cmdutil.clearunfinished(repo)
6251 6239
6252 6240 # with no argument, we also move the current bookmark, if any
6253 6241 rev, movemarkfrom = bookmarks.calculateupdate(ui, repo, rev)
6254 6242
6255 6243 # if we defined a bookmark, we have to remember the original bookmark name
6256 6244 brev = rev
6257 6245 rev = scmutil.revsingle(repo, rev, rev).rev()
6258 6246
6259 6247 if check and clean:
6260 6248 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
6261 6249
6262 6250 if date:
6263 6251 if rev is not None:
6264 6252 raise util.Abort(_("you can't specify a revision and a date"))
6265 6253 rev = cmdutil.finddate(ui, repo, date)
6266 6254
6267 6255 if check:
6268 6256 c = repo[None]
6269 6257 if c.dirty(merge=False, branch=False, missing=True):
6270 6258 raise util.Abort(_("uncommitted changes"))
6271 6259 if rev is None:
6272 6260 rev = repo[repo[None].branch()].rev()
6273 6261
6274 6262 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
6275 6263
6276 6264 if clean:
6277 6265 ret = hg.clean(repo, rev)
6278 6266 else:
6279 6267 ret = hg.update(repo, rev)
6280 6268
6281 6269 if not ret and movemarkfrom:
6282 6270 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
6283 6271 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
6284 6272 elif brev in repo._bookmarks:
6285 6273 bookmarks.setcurrent(repo, brev)
6286 6274 ui.status(_("(activating bookmark %s)\n") % brev)
6287 6275 elif brev:
6288 6276 if repo._bookmarkcurrent:
6289 6277 ui.status(_("(leaving bookmark %s)\n") %
6290 6278 repo._bookmarkcurrent)
6291 6279 bookmarks.unsetcurrent(repo)
6292 6280
6293 6281 return ret
6294 6282
6295 6283 @command('verify', [])
6296 6284 def verify(ui, repo):
6297 6285 """verify the integrity of the repository
6298 6286
6299 6287 Verify the integrity of the current repository.
6300 6288
6301 6289 This will perform an extensive check of the repository's
6302 6290 integrity, validating the hashes and checksums of each entry in
6303 6291 the changelog, manifest, and tracked files, as well as the
6304 6292 integrity of their crosslinks and indices.
6305 6293
6306 6294 Please see http://mercurial.selenic.com/wiki/RepositoryCorruption
6307 6295 for more information about recovery from corruption of the
6308 6296 repository.
6309 6297
6310 6298 Returns 0 on success, 1 if errors are encountered.
6311 6299 """
6312 6300 return hg.verify(repo)
6313 6301
6314 6302 @command('version', [], norepo=True)
6315 6303 def version_(ui):
6316 6304 """output version and copyright information"""
6317 6305 ui.write(_("Mercurial Distributed SCM (version %s)\n")
6318 6306 % util.version())
6319 6307 ui.status(_(
6320 6308 "(see http://mercurial.selenic.com for more information)\n"
6321 6309 "\nCopyright (C) 2005-2015 Matt Mackall and others\n"
6322 6310 "This is free software; see the source for copying conditions. "
6323 6311 "There is NO\nwarranty; "
6324 6312 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
6325 6313 ))
6326 6314
6327 6315 ui.note(_("\nEnabled extensions:\n\n"))
6328 6316 if ui.verbose:
6329 6317 # format names and versions into columns
6330 6318 names = []
6331 6319 vers = []
6332 6320 for name, module in extensions.extensions():
6333 6321 names.append(name)
6334 6322 vers.append(extensions.moduleversion(module))
6335 6323 if names:
6336 6324 maxnamelen = max(len(n) for n in names)
6337 6325 for i, name in enumerate(names):
6338 6326 ui.write(" %-*s %s\n" % (maxnamelen, name, vers[i]))
General Comments 0
You need to be logged in to leave comments. Login now