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