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