##// END OF EJS Templates
copy: handle rename internally...
Matt Mackall -
r5610:2493a478 default
parent child Browse files
Show More
@@ -1,1143 +1,1145 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
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from node import *
9 9 from i18n import _
10 10 import os, sys, bisect, stat
11 11 import mdiff, bdiff, util, templater, patch, errno
12 12
13 13 revrangesep = ':'
14 14
15 15 class UnknownCommand(Exception):
16 16 """Exception raised if command is not in the command table."""
17 17 class AmbiguousCommand(Exception):
18 18 """Exception raised if command shortcut matches more than one command."""
19 19
20 20 def findpossible(ui, cmd, table):
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 = e.lstrip("^").split("|")
30 30 found = None
31 31 if cmd in aliases:
32 32 found = cmd
33 33 elif not ui.config("ui", "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(ui, cmd, table):
50 50 """Return (aliases, command table entry) for command string."""
51 51 choice = findpossible(ui, cmd, table)
52 52
53 53 if choice.has_key(cmd):
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 AmbiguousCommand(cmd, clist)
60 60
61 61 if choice:
62 62 return choice.values()[0]
63 63
64 64 raise UnknownCommand(cmd)
65 65
66 66 def bail_if_changed(repo):
67 67 modified, added, removed, deleted = repo.status()[:4]
68 68 if modified or added or removed or deleted:
69 69 raise util.Abort(_("outstanding uncommitted changes"))
70 70
71 71 def logmessage(opts):
72 72 """ get the log message according to -m and -l option """
73 73 message = opts['message']
74 74 logfile = opts['logfile']
75 75
76 76 if message and logfile:
77 77 raise util.Abort(_('options --message and --logfile are mutually '
78 78 'exclusive'))
79 79 if not message and logfile:
80 80 try:
81 81 if logfile == '-':
82 82 message = sys.stdin.read()
83 83 else:
84 84 message = open(logfile).read()
85 85 except IOError, inst:
86 86 raise util.Abort(_("can't read commit message '%s': %s") %
87 87 (logfile, inst.strerror))
88 88 return message
89 89
90 90 def setremoteconfig(ui, opts):
91 91 "copy remote options to ui tree"
92 92 if opts.get('ssh'):
93 93 ui.setconfig("ui", "ssh", opts['ssh'])
94 94 if opts.get('remotecmd'):
95 95 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
96 96
97 97 def revpair(repo, revs):
98 98 '''return pair of nodes, given list of revisions. second item can
99 99 be None, meaning use working dir.'''
100 100
101 101 def revfix(repo, val, defval):
102 102 if not val and val != 0 and defval is not None:
103 103 val = defval
104 104 return repo.lookup(val)
105 105
106 106 if not revs:
107 107 return repo.dirstate.parents()[0], None
108 108 end = None
109 109 if len(revs) == 1:
110 110 if revrangesep in revs[0]:
111 111 start, end = revs[0].split(revrangesep, 1)
112 112 start = revfix(repo, start, 0)
113 113 end = revfix(repo, end, repo.changelog.count() - 1)
114 114 else:
115 115 start = revfix(repo, revs[0], None)
116 116 elif len(revs) == 2:
117 117 if revrangesep in revs[0] or revrangesep in revs[1]:
118 118 raise util.Abort(_('too many revisions specified'))
119 119 start = revfix(repo, revs[0], None)
120 120 end = revfix(repo, revs[1], None)
121 121 else:
122 122 raise util.Abort(_('too many revisions specified'))
123 123 return start, end
124 124
125 125 def revrange(repo, revs):
126 126 """Yield revision as strings from a list of revision specifications."""
127 127
128 128 def revfix(repo, val, defval):
129 129 if not val and val != 0 and defval is not None:
130 130 return defval
131 131 return repo.changelog.rev(repo.lookup(val))
132 132
133 133 seen, l = {}, []
134 134 for spec in revs:
135 135 if revrangesep in spec:
136 136 start, end = spec.split(revrangesep, 1)
137 137 start = revfix(repo, start, 0)
138 138 end = revfix(repo, end, repo.changelog.count() - 1)
139 139 step = start > end and -1 or 1
140 140 for rev in xrange(start, end+step, step):
141 141 if rev in seen:
142 142 continue
143 143 seen[rev] = 1
144 144 l.append(rev)
145 145 else:
146 146 rev = revfix(repo, spec, None)
147 147 if rev in seen:
148 148 continue
149 149 seen[rev] = 1
150 150 l.append(rev)
151 151
152 152 return l
153 153
154 154 def make_filename(repo, pat, node,
155 155 total=None, seqno=None, revwidth=None, pathname=None):
156 156 node_expander = {
157 157 'H': lambda: hex(node),
158 158 'R': lambda: str(repo.changelog.rev(node)),
159 159 'h': lambda: short(node),
160 160 }
161 161 expander = {
162 162 '%': lambda: '%',
163 163 'b': lambda: os.path.basename(repo.root),
164 164 }
165 165
166 166 try:
167 167 if node:
168 168 expander.update(node_expander)
169 169 if node:
170 170 expander['r'] = (lambda:
171 171 str(repo.changelog.rev(node)).zfill(revwidth or 0))
172 172 if total is not None:
173 173 expander['N'] = lambda: str(total)
174 174 if seqno is not None:
175 175 expander['n'] = lambda: str(seqno)
176 176 if total is not None and seqno is not None:
177 177 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
178 178 if pathname is not None:
179 179 expander['s'] = lambda: os.path.basename(pathname)
180 180 expander['d'] = lambda: os.path.dirname(pathname) or '.'
181 181 expander['p'] = lambda: pathname
182 182
183 183 newname = []
184 184 patlen = len(pat)
185 185 i = 0
186 186 while i < patlen:
187 187 c = pat[i]
188 188 if c == '%':
189 189 i += 1
190 190 c = pat[i]
191 191 c = expander[c]()
192 192 newname.append(c)
193 193 i += 1
194 194 return ''.join(newname)
195 195 except KeyError, inst:
196 196 raise util.Abort(_("invalid format spec '%%%s' in output file name") %
197 197 inst.args[0])
198 198
199 199 def make_file(repo, pat, node=None,
200 200 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
201 201 if not pat or pat == '-':
202 202 return 'w' in mode and sys.stdout or sys.stdin
203 203 if hasattr(pat, 'write') and 'w' in mode:
204 204 return pat
205 205 if hasattr(pat, 'read') and 'r' in mode:
206 206 return pat
207 207 return open(make_filename(repo, pat, node, total, seqno, revwidth,
208 208 pathname),
209 209 mode)
210 210
211 211 def matchpats(repo, pats=[], opts={}, globbed=False, default=None):
212 212 cwd = repo.getcwd()
213 213 return util.cmdmatcher(repo.root, cwd, pats or [], opts.get('include'),
214 214 opts.get('exclude'), globbed=globbed,
215 215 default=default)
216 216
217 217 def walk(repo, pats=[], opts={}, node=None, badmatch=None, globbed=False,
218 218 default=None):
219 219 files, matchfn, anypats = matchpats(repo, pats, opts, globbed=globbed,
220 220 default=default)
221 221 exact = dict.fromkeys(files)
222 222 cwd = repo.getcwd()
223 223 for src, fn in repo.walk(node=node, files=files, match=matchfn,
224 224 badmatch=badmatch):
225 225 yield src, fn, repo.pathto(fn, cwd), fn in exact
226 226
227 227 def findrenames(repo, added=None, removed=None, threshold=0.5):
228 228 '''find renamed files -- yields (before, after, score) tuples'''
229 229 if added is None or removed is None:
230 230 added, removed = repo.status()[1:3]
231 231 ctx = repo.changectx()
232 232 for a in added:
233 233 aa = repo.wread(a)
234 234 bestname, bestscore = None, threshold
235 235 for r in removed:
236 236 rr = ctx.filectx(r).data()
237 237
238 238 # bdiff.blocks() returns blocks of matching lines
239 239 # count the number of bytes in each
240 240 equal = 0
241 241 alines = mdiff.splitnewlines(aa)
242 242 matches = bdiff.blocks(aa, rr)
243 243 for x1,x2,y1,y2 in matches:
244 244 for line in alines[x1:x2]:
245 245 equal += len(line)
246 246
247 247 lengths = len(aa) + len(rr)
248 248 if lengths:
249 249 myscore = equal*2.0 / lengths
250 250 if myscore >= bestscore:
251 251 bestname, bestscore = r, myscore
252 252 if bestname:
253 253 yield bestname, a, bestscore
254 254
255 255 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
256 256 if dry_run is None:
257 257 dry_run = opts.get('dry_run')
258 258 if similarity is None:
259 259 similarity = float(opts.get('similarity') or 0)
260 260 add, remove = [], []
261 261 mapping = {}
262 262 for src, abs, rel, exact in walk(repo, pats, opts):
263 263 target = repo.wjoin(abs)
264 264 if src == 'f' and abs not in repo.dirstate:
265 265 add.append(abs)
266 266 mapping[abs] = rel, exact
267 267 if repo.ui.verbose or not exact:
268 268 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
269 269 if repo.dirstate[abs] != 'r' and (not util.lexists(target)
270 270 or (os.path.isdir(target) and not os.path.islink(target))):
271 271 remove.append(abs)
272 272 mapping[abs] = rel, exact
273 273 if repo.ui.verbose or not exact:
274 274 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
275 275 if not dry_run:
276 276 repo.remove(remove)
277 277 repo.add(add)
278 278 if similarity > 0:
279 279 for old, new, score in findrenames(repo, add, remove, similarity):
280 280 oldrel, oldexact = mapping[old]
281 281 newrel, newexact = mapping[new]
282 282 if repo.ui.verbose or not oldexact or not newexact:
283 283 repo.ui.status(_('recording removal of %s as rename to %s '
284 284 '(%d%% similar)\n') %
285 285 (oldrel, newrel, score * 100))
286 286 if not dry_run:
287 287 repo.copy(old, new)
288 288
289 def copy(ui, repo, pats, opts):
289 def copy(ui, repo, pats, opts, rename=False):
290 290 # called with the repo lock held
291 291 #
292 292 # hgsep => pathname that uses "/" to separate directories
293 293 # ossep => pathname that uses os.sep to separate directories
294 294 cwd = repo.getcwd()
295 copied = []
296 295 targets = {}
297 296 after = opts.get("after")
298 297 dryrun = opts.get("dry_run")
299 298
300 299 def walkpat(pat):
301 300 srcs = []
302 301 for tag, abs, rel, exact in walk(repo, [pat], opts, globbed=True):
303 302 state = repo.dirstate[abs]
304 303 if state in '?r':
305 304 if exact and state == '?':
306 305 ui.warn(_('%s: not copying - file is not managed\n') % rel)
307 306 if exact and state == 'r':
308 307 ui.warn(_('%s: not copying - file has been marked for'
309 308 ' remove\n') % rel)
310 309 continue
311 310 # abs: hgsep
312 311 # rel: ossep
313 312 srcs.append((abs, rel, exact))
314 313 return srcs
315 314
316 315 # abssrc: hgsep
317 316 # relsrc: ossep
318 317 # otarget: ossep
319 318 def copyfile(abssrc, relsrc, otarget, exact):
320 319 abstarget = util.canonpath(repo.root, cwd, otarget)
321 320 reltarget = repo.pathto(abstarget, cwd)
322 321 target = repo.wjoin(abstarget)
323 322 src = repo.wjoin(abssrc)
324 323 state = repo.dirstate[abstarget]
325 324
326 325 # check for collisions
327 326 prevsrc = targets.get(abstarget)
328 327 if prevsrc is not None:
329 328 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
330 329 (reltarget, repo.pathto(abssrc, cwd),
331 330 repo.pathto(prevsrc, cwd)))
332 331 return
333 332
334 333 # check for overwrites
335 334 exists = os.path.exists(target)
336 335 if (not after and exists or after and state in 'mn'):
337 336 if not opts['force']:
338 337 ui.warn(_('%s: not overwriting - file exists\n') %
339 338 reltarget)
340 339 return
341 340
342 341 if after:
343 342 if not exists:
344 343 return
345 344 elif not dryrun:
346 345 try:
347 346 if exists:
348 347 os.unlink(target)
349 348 targetdir = os.path.dirname(target) or '.'
350 349 if not os.path.isdir(targetdir):
351 350 os.makedirs(targetdir)
352 351 util.copyfile(src, target)
353 352 except IOError, inst:
354 353 if inst.errno == errno.ENOENT:
355 354 ui.warn(_('%s: deleted in working copy\n') % relsrc)
356 355 else:
357 356 ui.warn(_('%s: cannot copy - %s\n') %
358 357 (relsrc, inst.strerror))
359 358 return True # report a failure
360 359
361 360 if ui.verbose or not exact:
362 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
361 action = rename and "moving" or "copying"
362 ui.status(_('%s %s to %s\n') % (action, relsrc, reltarget))
363 363
364 364 targets[abstarget] = abssrc
365 365
366 366 # fix up dirstate
367 367 origsrc = repo.dirstate.copied(abssrc) or abssrc
368 368 if abstarget == origsrc: # copying back a copy?
369 369 if state not in 'mn' and not dryrun:
370 370 repo.dirstate.normallookup(abstarget)
371 371 else:
372 372 if repo.dirstate[origsrc] == 'a':
373 373 if not ui.quiet:
374 374 ui.warn(_("%s has not been committed yet, so no copy "
375 375 "data will be stored for %s.\n")
376 376 % (repo.pathto(origsrc, cwd), reltarget))
377 377 if abstarget not in repo.dirstate and not dryrun:
378 378 repo.add([abstarget])
379 379 elif not dryrun:
380 380 repo.copy(origsrc, abstarget)
381 copied.append((abssrc, relsrc, exact))
381
382 if rename and not dryrun:
383 repo.remove([abssrc], True)
382 384
383 385 # pat: ossep
384 386 # dest ossep
385 387 # srcs: list of (hgsep, hgsep, ossep, bool)
386 388 # return: function that takes hgsep and returns ossep
387 389 def targetpathfn(pat, dest, srcs):
388 390 if os.path.isdir(pat):
389 391 abspfx = util.canonpath(repo.root, cwd, pat)
390 392 abspfx = util.localpath(abspfx)
391 393 if destdirexists:
392 394 striplen = len(os.path.split(abspfx)[0])
393 395 else:
394 396 striplen = len(abspfx)
395 397 if striplen:
396 398 striplen += len(os.sep)
397 399 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
398 400 elif destdirexists:
399 401 res = lambda p: os.path.join(dest,
400 402 os.path.basename(util.localpath(p)))
401 403 else:
402 404 res = lambda p: dest
403 405 return res
404 406
405 407 # pat: ossep
406 408 # dest ossep
407 409 # srcs: list of (hgsep, hgsep, ossep, bool)
408 410 # return: function that takes hgsep and returns ossep
409 411 def targetpathafterfn(pat, dest, srcs):
410 412 if util.patkind(pat, None)[0]:
411 413 # a mercurial pattern
412 414 res = lambda p: os.path.join(dest,
413 415 os.path.basename(util.localpath(p)))
414 416 else:
415 417 abspfx = util.canonpath(repo.root, cwd, pat)
416 418 if len(abspfx) < len(srcs[0][0]):
417 419 # A directory. Either the target path contains the last
418 420 # component of the source path or it does not.
419 421 def evalpath(striplen):
420 422 score = 0
421 423 for s in srcs:
422 424 t = os.path.join(dest, util.localpath(s[0])[striplen:])
423 425 if os.path.exists(t):
424 426 score += 1
425 427 return score
426 428
427 429 abspfx = util.localpath(abspfx)
428 430 striplen = len(abspfx)
429 431 if striplen:
430 432 striplen += len(os.sep)
431 433 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
432 434 score = evalpath(striplen)
433 435 striplen1 = len(os.path.split(abspfx)[0])
434 436 if striplen1:
435 437 striplen1 += len(os.sep)
436 438 if evalpath(striplen1) > score:
437 439 striplen = striplen1
438 440 res = lambda p: os.path.join(dest,
439 441 util.localpath(p)[striplen:])
440 442 else:
441 443 # a file
442 444 if destdirexists:
443 445 res = lambda p: os.path.join(dest,
444 446 os.path.basename(util.localpath(p)))
445 447 else:
446 448 res = lambda p: dest
447 449 return res
448 450
449 451
450 452 pats = util.expand_glob(pats)
451 453 if not pats:
452 454 raise util.Abort(_('no source or destination specified'))
453 455 if len(pats) == 1:
454 456 raise util.Abort(_('no destination specified'))
455 457 dest = pats.pop()
456 458 destdirexists = os.path.isdir(dest)
457 459 if not destdirexists:
458 460 if len(pats) > 1 or util.patkind(pats[0], None)[0]:
459 461 raise util.Abort(_('with multiple sources, destination must be an '
460 462 'existing directory'))
461 463 if dest.endswith(os.sep) or os.altsep and dest.endswith(os.altsep):
462 464 raise util.Abort(_('destination %s is not a directory') % dest)
463 465
464 466 tfn = targetpathfn
465 467 if after:
466 468 tfn = targetpathafterfn
467 469 copylist = []
468 470 for pat in pats:
469 471 srcs = walkpat(pat)
470 472 if not srcs:
471 473 continue
472 474 copylist.append((tfn(pat, dest, srcs), srcs))
473 475 if not copylist:
474 476 raise util.Abort(_('no files to copy'))
475 477
476 478 errors = 0
477 479 for targetpath, srcs in copylist:
478 480 for abssrc, relsrc, exact in srcs:
479 481 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
480 482 errors += 1
481 483
482 484 if errors:
483 485 ui.warn(_('(consider using --after)\n'))
484 486
485 return errors, copied
487 return errors
486 488
487 489 def service(opts, parentfn=None, initfn=None, runfn=None):
488 490 '''Run a command as a service.'''
489 491
490 492 if opts['daemon'] and not opts['daemon_pipefds']:
491 493 rfd, wfd = os.pipe()
492 494 args = sys.argv[:]
493 495 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
494 496 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
495 497 args[0], args)
496 498 os.close(wfd)
497 499 os.read(rfd, 1)
498 500 if parentfn:
499 501 return parentfn(pid)
500 502 else:
501 503 os._exit(0)
502 504
503 505 if initfn:
504 506 initfn()
505 507
506 508 if opts['pid_file']:
507 509 fp = open(opts['pid_file'], 'w')
508 510 fp.write(str(os.getpid()) + '\n')
509 511 fp.close()
510 512
511 513 if opts['daemon_pipefds']:
512 514 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
513 515 os.close(rfd)
514 516 try:
515 517 os.setsid()
516 518 except AttributeError:
517 519 pass
518 520 os.write(wfd, 'y')
519 521 os.close(wfd)
520 522 sys.stdout.flush()
521 523 sys.stderr.flush()
522 524 fd = os.open(util.nulldev, os.O_RDWR)
523 525 if fd != 0: os.dup2(fd, 0)
524 526 if fd != 1: os.dup2(fd, 1)
525 527 if fd != 2: os.dup2(fd, 2)
526 528 if fd not in (0, 1, 2): os.close(fd)
527 529
528 530 if runfn:
529 531 return runfn()
530 532
531 533 class changeset_printer(object):
532 534 '''show changeset information when templating not requested.'''
533 535
534 536 def __init__(self, ui, repo, patch, buffered):
535 537 self.ui = ui
536 538 self.repo = repo
537 539 self.buffered = buffered
538 540 self.patch = patch
539 541 self.header = {}
540 542 self.hunk = {}
541 543 self.lastheader = None
542 544
543 545 def flush(self, rev):
544 546 if rev in self.header:
545 547 h = self.header[rev]
546 548 if h != self.lastheader:
547 549 self.lastheader = h
548 550 self.ui.write(h)
549 551 del self.header[rev]
550 552 if rev in self.hunk:
551 553 self.ui.write(self.hunk[rev])
552 554 del self.hunk[rev]
553 555 return 1
554 556 return 0
555 557
556 558 def show(self, rev=0, changenode=None, copies=(), **props):
557 559 if self.buffered:
558 560 self.ui.pushbuffer()
559 561 self._show(rev, changenode, copies, props)
560 562 self.hunk[rev] = self.ui.popbuffer()
561 563 else:
562 564 self._show(rev, changenode, copies, props)
563 565
564 566 def _show(self, rev, changenode, copies, props):
565 567 '''show a single changeset or file revision'''
566 568 log = self.repo.changelog
567 569 if changenode is None:
568 570 changenode = log.node(rev)
569 571 elif not rev:
570 572 rev = log.rev(changenode)
571 573
572 574 if self.ui.quiet:
573 575 self.ui.write("%d:%s\n" % (rev, short(changenode)))
574 576 return
575 577
576 578 changes = log.read(changenode)
577 579 date = util.datestr(changes[2])
578 580 extra = changes[5]
579 581 branch = extra.get("branch")
580 582
581 583 hexfunc = self.ui.debugflag and hex or short
582 584
583 585 parents = [(p, hexfunc(log.node(p)))
584 586 for p in self._meaningful_parentrevs(log, rev)]
585 587
586 588 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
587 589
588 590 # don't show the default branch name
589 591 if branch != 'default':
590 592 branch = util.tolocal(branch)
591 593 self.ui.write(_("branch: %s\n") % branch)
592 594 for tag in self.repo.nodetags(changenode):
593 595 self.ui.write(_("tag: %s\n") % tag)
594 596 for parent in parents:
595 597 self.ui.write(_("parent: %d:%s\n") % parent)
596 598
597 599 if self.ui.debugflag:
598 600 self.ui.write(_("manifest: %d:%s\n") %
599 601 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
600 602 self.ui.write(_("user: %s\n") % changes[1])
601 603 self.ui.write(_("date: %s\n") % date)
602 604
603 605 if self.ui.debugflag:
604 606 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
605 607 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
606 608 files):
607 609 if value:
608 610 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
609 611 elif changes[3] and self.ui.verbose:
610 612 self.ui.write(_("files: %s\n") % " ".join(changes[3]))
611 613 if copies and self.ui.verbose:
612 614 copies = ['%s (%s)' % c for c in copies]
613 615 self.ui.write(_("copies: %s\n") % ' '.join(copies))
614 616
615 617 if extra and self.ui.debugflag:
616 618 extraitems = extra.items()
617 619 extraitems.sort()
618 620 for key, value in extraitems:
619 621 self.ui.write(_("extra: %s=%s\n")
620 622 % (key, value.encode('string_escape')))
621 623
622 624 description = changes[4].strip()
623 625 if description:
624 626 if self.ui.verbose:
625 627 self.ui.write(_("description:\n"))
626 628 self.ui.write(description)
627 629 self.ui.write("\n\n")
628 630 else:
629 631 self.ui.write(_("summary: %s\n") %
630 632 description.splitlines()[0])
631 633 self.ui.write("\n")
632 634
633 635 self.showpatch(changenode)
634 636
635 637 def showpatch(self, node):
636 638 if self.patch:
637 639 prev = self.repo.changelog.parents(node)[0]
638 640 patch.diff(self.repo, prev, node, match=self.patch, fp=self.ui,
639 641 opts=patch.diffopts(self.ui))
640 642 self.ui.write("\n")
641 643
642 644 def _meaningful_parentrevs(self, log, rev):
643 645 """Return list of meaningful (or all if debug) parentrevs for rev.
644 646
645 647 For merges (two non-nullrev revisions) both parents are meaningful.
646 648 Otherwise the first parent revision is considered meaningful if it
647 649 is not the preceding revision.
648 650 """
649 651 parents = log.parentrevs(rev)
650 652 if not self.ui.debugflag and parents[1] == nullrev:
651 653 if parents[0] >= rev - 1:
652 654 parents = []
653 655 else:
654 656 parents = [parents[0]]
655 657 return parents
656 658
657 659
658 660 class changeset_templater(changeset_printer):
659 661 '''format changeset information.'''
660 662
661 663 def __init__(self, ui, repo, patch, mapfile, buffered):
662 664 changeset_printer.__init__(self, ui, repo, patch, buffered)
663 665 filters = templater.common_filters.copy()
664 666 filters['formatnode'] = (ui.debugflag and (lambda x: x)
665 667 or (lambda x: x[:12]))
666 668 self.t = templater.templater(mapfile, filters,
667 669 cache={
668 670 'parent': '{rev}:{node|formatnode} ',
669 671 'manifest': '{rev}:{node|formatnode}',
670 672 'filecopy': '{name} ({source})'})
671 673
672 674 def use_template(self, t):
673 675 '''set template string to use'''
674 676 self.t.cache['changeset'] = t
675 677
676 678 def _show(self, rev, changenode, copies, props):
677 679 '''show a single changeset or file revision'''
678 680 log = self.repo.changelog
679 681 if changenode is None:
680 682 changenode = log.node(rev)
681 683 elif not rev:
682 684 rev = log.rev(changenode)
683 685
684 686 changes = log.read(changenode)
685 687
686 688 def showlist(name, values, plural=None, **args):
687 689 '''expand set of values.
688 690 name is name of key in template map.
689 691 values is list of strings or dicts.
690 692 plural is plural of name, if not simply name + 's'.
691 693
692 694 expansion works like this, given name 'foo'.
693 695
694 696 if values is empty, expand 'no_foos'.
695 697
696 698 if 'foo' not in template map, return values as a string,
697 699 joined by space.
698 700
699 701 expand 'start_foos'.
700 702
701 703 for each value, expand 'foo'. if 'last_foo' in template
702 704 map, expand it instead of 'foo' for last key.
703 705
704 706 expand 'end_foos'.
705 707 '''
706 708 if plural: names = plural
707 709 else: names = name + 's'
708 710 if not values:
709 711 noname = 'no_' + names
710 712 if noname in self.t:
711 713 yield self.t(noname, **args)
712 714 return
713 715 if name not in self.t:
714 716 if isinstance(values[0], str):
715 717 yield ' '.join(values)
716 718 else:
717 719 for v in values:
718 720 yield dict(v, **args)
719 721 return
720 722 startname = 'start_' + names
721 723 if startname in self.t:
722 724 yield self.t(startname, **args)
723 725 vargs = args.copy()
724 726 def one(v, tag=name):
725 727 try:
726 728 vargs.update(v)
727 729 except (AttributeError, ValueError):
728 730 try:
729 731 for a, b in v:
730 732 vargs[a] = b
731 733 except ValueError:
732 734 vargs[name] = v
733 735 return self.t(tag, **vargs)
734 736 lastname = 'last_' + name
735 737 if lastname in self.t:
736 738 last = values.pop()
737 739 else:
738 740 last = None
739 741 for v in values:
740 742 yield one(v)
741 743 if last is not None:
742 744 yield one(last, tag=lastname)
743 745 endname = 'end_' + names
744 746 if endname in self.t:
745 747 yield self.t(endname, **args)
746 748
747 749 def showbranches(**args):
748 750 branch = changes[5].get("branch")
749 751 if branch != 'default':
750 752 branch = util.tolocal(branch)
751 753 return showlist('branch', [branch], plural='branches', **args)
752 754
753 755 def showparents(**args):
754 756 parents = [[('rev', p), ('node', hex(log.node(p)))]
755 757 for p in self._meaningful_parentrevs(log, rev)]
756 758 return showlist('parent', parents, **args)
757 759
758 760 def showtags(**args):
759 761 return showlist('tag', self.repo.nodetags(changenode), **args)
760 762
761 763 def showextras(**args):
762 764 extras = changes[5].items()
763 765 extras.sort()
764 766 for key, value in extras:
765 767 args = args.copy()
766 768 args.update(dict(key=key, value=value))
767 769 yield self.t('extra', **args)
768 770
769 771 def showcopies(**args):
770 772 c = [{'name': x[0], 'source': x[1]} for x in copies]
771 773 return showlist('file_copy', c, plural='file_copies', **args)
772 774
773 775 files = []
774 776 def getfiles():
775 777 if not files:
776 778 files[:] = self.repo.status(
777 779 log.parents(changenode)[0], changenode)[:3]
778 780 return files
779 781 def showfiles(**args):
780 782 return showlist('file', changes[3], **args)
781 783 def showmods(**args):
782 784 return showlist('file_mod', getfiles()[0], **args)
783 785 def showadds(**args):
784 786 return showlist('file_add', getfiles()[1], **args)
785 787 def showdels(**args):
786 788 return showlist('file_del', getfiles()[2], **args)
787 789 def showmanifest(**args):
788 790 args = args.copy()
789 791 args.update(dict(rev=self.repo.manifest.rev(changes[0]),
790 792 node=hex(changes[0])))
791 793 return self.t('manifest', **args)
792 794
793 795 defprops = {
794 796 'author': changes[1],
795 797 'branches': showbranches,
796 798 'date': changes[2],
797 799 'desc': changes[4].strip(),
798 800 'file_adds': showadds,
799 801 'file_dels': showdels,
800 802 'file_mods': showmods,
801 803 'files': showfiles,
802 804 'file_copies': showcopies,
803 805 'manifest': showmanifest,
804 806 'node': hex(changenode),
805 807 'parents': showparents,
806 808 'rev': rev,
807 809 'tags': showtags,
808 810 'extras': showextras,
809 811 }
810 812 props = props.copy()
811 813 props.update(defprops)
812 814
813 815 try:
814 816 if self.ui.debugflag and 'header_debug' in self.t:
815 817 key = 'header_debug'
816 818 elif self.ui.quiet and 'header_quiet' in self.t:
817 819 key = 'header_quiet'
818 820 elif self.ui.verbose and 'header_verbose' in self.t:
819 821 key = 'header_verbose'
820 822 elif 'header' in self.t:
821 823 key = 'header'
822 824 else:
823 825 key = ''
824 826 if key:
825 827 h = templater.stringify(self.t(key, **props))
826 828 if self.buffered:
827 829 self.header[rev] = h
828 830 else:
829 831 self.ui.write(h)
830 832 if self.ui.debugflag and 'changeset_debug' in self.t:
831 833 key = 'changeset_debug'
832 834 elif self.ui.quiet and 'changeset_quiet' in self.t:
833 835 key = 'changeset_quiet'
834 836 elif self.ui.verbose and 'changeset_verbose' in self.t:
835 837 key = 'changeset_verbose'
836 838 else:
837 839 key = 'changeset'
838 840 self.ui.write(templater.stringify(self.t(key, **props)))
839 841 self.showpatch(changenode)
840 842 except KeyError, inst:
841 843 raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
842 844 inst.args[0]))
843 845 except SyntaxError, inst:
844 846 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
845 847
846 848 def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
847 849 """show one changeset using template or regular display.
848 850
849 851 Display format will be the first non-empty hit of:
850 852 1. option 'template'
851 853 2. option 'style'
852 854 3. [ui] setting 'logtemplate'
853 855 4. [ui] setting 'style'
854 856 If all of these values are either the unset or the empty string,
855 857 regular display via changeset_printer() is done.
856 858 """
857 859 # options
858 860 patch = False
859 861 if opts.get('patch'):
860 862 patch = matchfn or util.always
861 863
862 864 tmpl = opts.get('template')
863 865 mapfile = None
864 866 if tmpl:
865 867 tmpl = templater.parsestring(tmpl, quoted=False)
866 868 else:
867 869 mapfile = opts.get('style')
868 870 # ui settings
869 871 if not mapfile:
870 872 tmpl = ui.config('ui', 'logtemplate')
871 873 if tmpl:
872 874 tmpl = templater.parsestring(tmpl)
873 875 else:
874 876 mapfile = ui.config('ui', 'style')
875 877
876 878 if tmpl or mapfile:
877 879 if mapfile:
878 880 if not os.path.split(mapfile)[0]:
879 881 mapname = (templater.templatepath('map-cmdline.' + mapfile)
880 882 or templater.templatepath(mapfile))
881 883 if mapname: mapfile = mapname
882 884 try:
883 885 t = changeset_templater(ui, repo, patch, mapfile, buffered)
884 886 except SyntaxError, inst:
885 887 raise util.Abort(inst.args[0])
886 888 if tmpl: t.use_template(tmpl)
887 889 return t
888 890 return changeset_printer(ui, repo, patch, buffered)
889 891
890 892 def finddate(ui, repo, date):
891 893 """Find the tipmost changeset that matches the given date spec"""
892 894 df = util.matchdate(date + " to " + date)
893 895 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
894 896 changeiter, matchfn = walkchangerevs(ui, repo, [], get, {'rev':None})
895 897 results = {}
896 898 for st, rev, fns in changeiter:
897 899 if st == 'add':
898 900 d = get(rev)[2]
899 901 if df(d[0]):
900 902 results[rev] = d
901 903 elif st == 'iter':
902 904 if rev in results:
903 905 ui.status("Found revision %s from %s\n" %
904 906 (rev, util.datestr(results[rev])))
905 907 return str(rev)
906 908
907 909 raise util.Abort(_("revision matching date not found"))
908 910
909 911 def walkchangerevs(ui, repo, pats, change, opts):
910 912 '''Iterate over files and the revs they changed in.
911 913
912 914 Callers most commonly need to iterate backwards over the history
913 915 it is interested in. Doing so has awful (quadratic-looking)
914 916 performance, so we use iterators in a "windowed" way.
915 917
916 918 We walk a window of revisions in the desired order. Within the
917 919 window, we first walk forwards to gather data, then in the desired
918 920 order (usually backwards) to display it.
919 921
920 922 This function returns an (iterator, matchfn) tuple. The iterator
921 923 yields 3-tuples. They will be of one of the following forms:
922 924
923 925 "window", incrementing, lastrev: stepping through a window,
924 926 positive if walking forwards through revs, last rev in the
925 927 sequence iterated over - use to reset state for the current window
926 928
927 929 "add", rev, fns: out-of-order traversal of the given file names
928 930 fns, which changed during revision rev - use to gather data for
929 931 possible display
930 932
931 933 "iter", rev, None: in-order traversal of the revs earlier iterated
932 934 over with "add" - use to display data'''
933 935
934 936 def increasing_windows(start, end, windowsize=8, sizelimit=512):
935 937 if start < end:
936 938 while start < end:
937 939 yield start, min(windowsize, end-start)
938 940 start += windowsize
939 941 if windowsize < sizelimit:
940 942 windowsize *= 2
941 943 else:
942 944 while start > end:
943 945 yield start, min(windowsize, start-end-1)
944 946 start -= windowsize
945 947 if windowsize < sizelimit:
946 948 windowsize *= 2
947 949
948 950 files, matchfn, anypats = matchpats(repo, pats, opts)
949 951 follow = opts.get('follow') or opts.get('follow_first')
950 952
951 953 if repo.changelog.count() == 0:
952 954 return [], matchfn
953 955
954 956 if follow:
955 957 defrange = '%s:0' % repo.changectx().rev()
956 958 else:
957 959 defrange = 'tip:0'
958 960 revs = revrange(repo, opts['rev'] or [defrange])
959 961 wanted = {}
960 962 slowpath = anypats or opts.get('removed')
961 963 fncache = {}
962 964
963 965 if not slowpath and not files:
964 966 # No files, no patterns. Display all revs.
965 967 wanted = dict.fromkeys(revs)
966 968 copies = []
967 969 if not slowpath:
968 970 # Only files, no patterns. Check the history of each file.
969 971 def filerevgen(filelog, node):
970 972 cl_count = repo.changelog.count()
971 973 if node is None:
972 974 last = filelog.count() - 1
973 975 else:
974 976 last = filelog.rev(node)
975 977 for i, window in increasing_windows(last, nullrev):
976 978 revs = []
977 979 for j in xrange(i - window, i + 1):
978 980 n = filelog.node(j)
979 981 revs.append((filelog.linkrev(n),
980 982 follow and filelog.renamed(n)))
981 983 revs.reverse()
982 984 for rev in revs:
983 985 # only yield rev for which we have the changelog, it can
984 986 # happen while doing "hg log" during a pull or commit
985 987 if rev[0] < cl_count:
986 988 yield rev
987 989 def iterfiles():
988 990 for filename in files:
989 991 yield filename, None
990 992 for filename_node in copies:
991 993 yield filename_node
992 994 minrev, maxrev = min(revs), max(revs)
993 995 for file_, node in iterfiles():
994 996 filelog = repo.file(file_)
995 997 # A zero count may be a directory or deleted file, so
996 998 # try to find matching entries on the slow path.
997 999 if filelog.count() == 0:
998 1000 slowpath = True
999 1001 break
1000 1002 for rev, copied in filerevgen(filelog, node):
1001 1003 if rev <= maxrev:
1002 1004 if rev < minrev:
1003 1005 break
1004 1006 fncache.setdefault(rev, [])
1005 1007 fncache[rev].append(file_)
1006 1008 wanted[rev] = 1
1007 1009 if follow and copied:
1008 1010 copies.append(copied)
1009 1011 if slowpath:
1010 1012 if follow:
1011 1013 raise util.Abort(_('can only follow copies/renames for explicit '
1012 1014 'file names'))
1013 1015
1014 1016 # The slow path checks files modified in every changeset.
1015 1017 def changerevgen():
1016 1018 for i, window in increasing_windows(repo.changelog.count()-1,
1017 1019 nullrev):
1018 1020 for j in xrange(i - window, i + 1):
1019 1021 yield j, change(j)[3]
1020 1022
1021 1023 for rev, changefiles in changerevgen():
1022 1024 matches = filter(matchfn, changefiles)
1023 1025 if matches:
1024 1026 fncache[rev] = matches
1025 1027 wanted[rev] = 1
1026 1028
1027 1029 class followfilter:
1028 1030 def __init__(self, onlyfirst=False):
1029 1031 self.startrev = nullrev
1030 1032 self.roots = []
1031 1033 self.onlyfirst = onlyfirst
1032 1034
1033 1035 def match(self, rev):
1034 1036 def realparents(rev):
1035 1037 if self.onlyfirst:
1036 1038 return repo.changelog.parentrevs(rev)[0:1]
1037 1039 else:
1038 1040 return filter(lambda x: x != nullrev,
1039 1041 repo.changelog.parentrevs(rev))
1040 1042
1041 1043 if self.startrev == nullrev:
1042 1044 self.startrev = rev
1043 1045 return True
1044 1046
1045 1047 if rev > self.startrev:
1046 1048 # forward: all descendants
1047 1049 if not self.roots:
1048 1050 self.roots.append(self.startrev)
1049 1051 for parent in realparents(rev):
1050 1052 if parent in self.roots:
1051 1053 self.roots.append(rev)
1052 1054 return True
1053 1055 else:
1054 1056 # backwards: all parents
1055 1057 if not self.roots:
1056 1058 self.roots.extend(realparents(self.startrev))
1057 1059 if rev in self.roots:
1058 1060 self.roots.remove(rev)
1059 1061 self.roots.extend(realparents(rev))
1060 1062 return True
1061 1063
1062 1064 return False
1063 1065
1064 1066 # it might be worthwhile to do this in the iterator if the rev range
1065 1067 # is descending and the prune args are all within that range
1066 1068 for rev in opts.get('prune', ()):
1067 1069 rev = repo.changelog.rev(repo.lookup(rev))
1068 1070 ff = followfilter()
1069 1071 stop = min(revs[0], revs[-1])
1070 1072 for x in xrange(rev, stop-1, -1):
1071 1073 if ff.match(x) and x in wanted:
1072 1074 del wanted[x]
1073 1075
1074 1076 def iterate():
1075 1077 if follow and not files:
1076 1078 ff = followfilter(onlyfirst=opts.get('follow_first'))
1077 1079 def want(rev):
1078 1080 if ff.match(rev) and rev in wanted:
1079 1081 return True
1080 1082 return False
1081 1083 else:
1082 1084 def want(rev):
1083 1085 return rev in wanted
1084 1086
1085 1087 for i, window in increasing_windows(0, len(revs)):
1086 1088 yield 'window', revs[0] < revs[-1], revs[-1]
1087 1089 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
1088 1090 srevs = list(nrevs)
1089 1091 srevs.sort()
1090 1092 for rev in srevs:
1091 1093 fns = fncache.get(rev)
1092 1094 if not fns:
1093 1095 def fns_generator():
1094 1096 for f in change(rev)[3]:
1095 1097 if matchfn(f):
1096 1098 yield f
1097 1099 fns = fns_generator()
1098 1100 yield 'add', rev, fns
1099 1101 for rev in nrevs:
1100 1102 yield 'iter', rev, None
1101 1103 return iterate(), matchfn
1102 1104
1103 1105 def commit(ui, repo, commitfunc, pats, opts):
1104 1106 '''commit the specified files or all outstanding changes'''
1105 1107 message = logmessage(opts)
1106 1108
1107 1109 if opts['addremove']:
1108 1110 addremove(repo, pats, opts)
1109 1111 fns, match, anypats = matchpats(repo, pats, opts)
1110 1112 if pats:
1111 1113 status = repo.status(files=fns, match=match)
1112 1114 modified, added, removed, deleted, unknown = status[:5]
1113 1115 files = modified + added + removed
1114 1116 slist = None
1115 1117 for f in fns:
1116 1118 if f == '.':
1117 1119 continue
1118 1120 if f not in files:
1119 1121 rf = repo.wjoin(f)
1120 1122 try:
1121 1123 mode = os.lstat(rf)[stat.ST_MODE]
1122 1124 except OSError:
1123 1125 raise util.Abort(_("file %s not found!") % rf)
1124 1126 if stat.S_ISDIR(mode):
1125 1127 name = f + '/'
1126 1128 if slist is None:
1127 1129 slist = list(files)
1128 1130 slist.sort()
1129 1131 i = bisect.bisect(slist, name)
1130 1132 if i >= len(slist) or not slist[i].startswith(name):
1131 1133 raise util.Abort(_("no match under directory %s!")
1132 1134 % rf)
1133 1135 elif not (stat.S_ISREG(mode) or stat.S_ISLNK(mode)):
1134 1136 raise util.Abort(_("can't commit %s: "
1135 1137 "unsupported file type!") % rf)
1136 1138 elif f not in repo.dirstate:
1137 1139 raise util.Abort(_("file %s not tracked!") % rf)
1138 1140 else:
1139 1141 files = []
1140 1142 try:
1141 1143 return commitfunc(ui, repo, files, message, match, opts)
1142 1144 except ValueError, inst:
1143 1145 raise util.Abort(str(inst))
@@ -1,2974 +1,2965 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
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from node import *
9 9 from i18n import _
10 10 import os, re, sys, urllib
11 11 import hg, util, revlog, bundlerepo, extensions
12 12 import difflib, patch, time, help, mdiff, tempfile
13 13 import errno, version, socket
14 14 import archival, changegroup, cmdutil, hgweb.server, sshserver
15 15
16 16 # Commands start here, listed alphabetically
17 17
18 18 def add(ui, repo, *pats, **opts):
19 19 """add the specified files on the next commit
20 20
21 21 Schedule files to be version controlled and added to the repository.
22 22
23 23 The files will be added to the repository at the next commit. To
24 24 undo an add before that, see hg revert.
25 25
26 26 If no names are given, add all files in the repository.
27 27 """
28 28
29 29 names = []
30 30 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
31 31 if exact:
32 32 if ui.verbose:
33 33 ui.status(_('adding %s\n') % rel)
34 34 names.append(abs)
35 35 elif abs not in repo.dirstate:
36 36 ui.status(_('adding %s\n') % rel)
37 37 names.append(abs)
38 38 if not opts.get('dry_run'):
39 39 repo.add(names)
40 40
41 41 def addremove(ui, repo, *pats, **opts):
42 42 """add all new files, delete all missing files
43 43
44 44 Add all new files and remove all missing files from the repository.
45 45
46 46 New files are ignored if they match any of the patterns in .hgignore. As
47 47 with add, these changes take effect at the next commit.
48 48
49 49 Use the -s option to detect renamed files. With a parameter > 0,
50 50 this compares every removed file with every added file and records
51 51 those similar enough as renames. This option takes a percentage
52 52 between 0 (disabled) and 100 (files must be identical) as its
53 53 parameter. Detecting renamed files this way can be expensive.
54 54 """
55 55 try:
56 56 sim = float(opts.get('similarity') or 0)
57 57 except ValueError:
58 58 raise util.Abort(_('similarity must be a number'))
59 59 if sim < 0 or sim > 100:
60 60 raise util.Abort(_('similarity must be between 0 and 100'))
61 61 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
62 62
63 63 def annotate(ui, repo, *pats, **opts):
64 64 """show changeset information per file line
65 65
66 66 List changes in files, showing the revision id responsible for each line
67 67
68 68 This command is useful to discover who did a change or when a change took
69 69 place.
70 70
71 71 Without the -a option, annotate will avoid processing files it
72 72 detects as binary. With -a, annotate will generate an annotation
73 73 anyway, probably with undesirable results.
74 74 """
75 75 getdate = util.cachefunc(lambda x: util.datestr(x[0].date()))
76 76
77 77 if not pats:
78 78 raise util.Abort(_('at least one file name or pattern required'))
79 79
80 80 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
81 81 ('number', lambda x: str(x[0].rev())),
82 82 ('changeset', lambda x: short(x[0].node())),
83 83 ('date', getdate),
84 84 ('follow', lambda x: x[0].path()),
85 85 ]
86 86
87 87 if (not opts['user'] and not opts['changeset'] and not opts['date']
88 88 and not opts['follow']):
89 89 opts['number'] = 1
90 90
91 91 linenumber = opts.get('line_number') is not None
92 92 if (linenumber and (not opts['changeset']) and (not opts['number'])):
93 93 raise util.Abort(_('at least one of -n/-c is required for -l'))
94 94
95 95 funcmap = [func for op, func in opmap if opts.get(op)]
96 96 if linenumber:
97 97 lastfunc = funcmap[-1]
98 98 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
99 99
100 100 ctx = repo.changectx(opts['rev'])
101 101
102 102 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
103 103 node=ctx.node()):
104 104 fctx = ctx.filectx(abs)
105 105 if not opts['text'] and util.binary(fctx.data()):
106 106 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
107 107 continue
108 108
109 109 lines = fctx.annotate(follow=opts.get('follow'),
110 110 linenumber=linenumber)
111 111 pieces = []
112 112
113 113 for f in funcmap:
114 114 l = [f(n) for n, dummy in lines]
115 115 if l:
116 116 m = max(map(len, l))
117 117 pieces.append(["%*s" % (m, x) for x in l])
118 118
119 119 if pieces:
120 120 for p, l in zip(zip(*pieces), lines):
121 121 ui.write("%s: %s" % (" ".join(p), l[1]))
122 122
123 123 def archive(ui, repo, dest, **opts):
124 124 '''create unversioned archive of a repository revision
125 125
126 126 By default, the revision used is the parent of the working
127 127 directory; use "-r" to specify a different revision.
128 128
129 129 To specify the type of archive to create, use "-t". Valid
130 130 types are:
131 131
132 132 "files" (default): a directory full of files
133 133 "tar": tar archive, uncompressed
134 134 "tbz2": tar archive, compressed using bzip2
135 135 "tgz": tar archive, compressed using gzip
136 136 "uzip": zip archive, uncompressed
137 137 "zip": zip archive, compressed using deflate
138 138
139 139 The exact name of the destination archive or directory is given
140 140 using a format string; see "hg help export" for details.
141 141
142 142 Each member added to an archive file has a directory prefix
143 143 prepended. Use "-p" to specify a format string for the prefix.
144 144 The default is the basename of the archive, with suffixes removed.
145 145 '''
146 146
147 147 ctx = repo.changectx(opts['rev'])
148 148 if not ctx:
149 149 raise util.Abort(_('repository has no revisions'))
150 150 node = ctx.node()
151 151 dest = cmdutil.make_filename(repo, dest, node)
152 152 if os.path.realpath(dest) == repo.root:
153 153 raise util.Abort(_('repository root cannot be destination'))
154 154 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
155 155 kind = opts.get('type') or 'files'
156 156 prefix = opts['prefix']
157 157 if dest == '-':
158 158 if kind == 'files':
159 159 raise util.Abort(_('cannot archive plain files to stdout'))
160 160 dest = sys.stdout
161 161 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
162 162 prefix = cmdutil.make_filename(repo, prefix, node)
163 163 archival.archive(repo, dest, node, kind, not opts['no_decode'],
164 164 matchfn, prefix)
165 165
166 166 def backout(ui, repo, node=None, rev=None, **opts):
167 167 '''reverse effect of earlier changeset
168 168
169 169 Commit the backed out changes as a new changeset. The new
170 170 changeset is a child of the backed out changeset.
171 171
172 172 If you back out a changeset other than the tip, a new head is
173 173 created. This head is the parent of the working directory. If
174 174 you back out an old changeset, your working directory will appear
175 175 old after the backout. You should merge the backout changeset
176 176 with another head.
177 177
178 178 The --merge option remembers the parent of the working directory
179 179 before starting the backout, then merges the new head with that
180 180 changeset afterwards. This saves you from doing the merge by
181 181 hand. The result of this merge is not committed, as for a normal
182 182 merge.'''
183 183 if rev and node:
184 184 raise util.Abort(_("please specify just one revision"))
185 185
186 186 if not rev:
187 187 rev = node
188 188
189 189 if not rev:
190 190 raise util.Abort(_("please specify a revision to backout"))
191 191
192 192 cmdutil.bail_if_changed(repo)
193 193 op1, op2 = repo.dirstate.parents()
194 194 if op2 != nullid:
195 195 raise util.Abort(_('outstanding uncommitted merge'))
196 196 node = repo.lookup(rev)
197 197
198 198 a = repo.changelog.ancestor(op1, node)
199 199 if a != node:
200 200 raise util.Abort(_('cannot back out change on a different branch'))
201 201
202 202 p1, p2 = repo.changelog.parents(node)
203 203 if p1 == nullid:
204 204 raise util.Abort(_('cannot back out a change with no parents'))
205 205 if p2 != nullid:
206 206 if not opts['parent']:
207 207 raise util.Abort(_('cannot back out a merge changeset without '
208 208 '--parent'))
209 209 p = repo.lookup(opts['parent'])
210 210 if p not in (p1, p2):
211 211 raise util.Abort(_('%s is not a parent of %s') %
212 212 (short(p), short(node)))
213 213 parent = p
214 214 else:
215 215 if opts['parent']:
216 216 raise util.Abort(_('cannot use --parent on non-merge changeset'))
217 217 parent = p1
218 218
219 219 hg.clean(repo, node, show_stats=False)
220 220 revert_opts = opts.copy()
221 221 revert_opts['date'] = None
222 222 revert_opts['all'] = True
223 223 revert_opts['rev'] = hex(parent)
224 224 revert(ui, repo, **revert_opts)
225 225 commit_opts = opts.copy()
226 226 commit_opts['addremove'] = False
227 227 if not commit_opts['message'] and not commit_opts['logfile']:
228 228 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
229 229 commit_opts['force_editor'] = True
230 230 commit(ui, repo, **commit_opts)
231 231 def nice(node):
232 232 return '%d:%s' % (repo.changelog.rev(node), short(node))
233 233 ui.status(_('changeset %s backs out changeset %s\n') %
234 234 (nice(repo.changelog.tip()), nice(node)))
235 235 if op1 != node:
236 236 if opts['merge']:
237 237 ui.status(_('merging with changeset %s\n') % nice(op1))
238 238 hg.merge(repo, hex(op1))
239 239 else:
240 240 ui.status(_('the backout changeset is a new head - '
241 241 'do not forget to merge\n'))
242 242 ui.status(_('(use "backout --merge" '
243 243 'if you want to auto-merge)\n'))
244 244
245 245 def branch(ui, repo, label=None, **opts):
246 246 """set or show the current branch name
247 247
248 248 With no argument, show the current branch name. With one argument,
249 249 set the working directory branch name (the branch does not exist in
250 250 the repository until the next commit).
251 251
252 252 Unless --force is specified, branch will not let you set a
253 253 branch name that shadows an existing branch.
254 254 """
255 255
256 256 if label:
257 257 if not opts.get('force') and label in repo.branchtags():
258 258 if label not in [p.branch() for p in repo.workingctx().parents()]:
259 259 raise util.Abort(_('a branch of the same name already exists'
260 260 ' (use --force to override)'))
261 261 repo.dirstate.setbranch(util.fromlocal(label))
262 262 ui.status(_('marked working directory as branch %s\n') % label)
263 263 else:
264 264 ui.write("%s\n" % util.tolocal(repo.dirstate.branch()))
265 265
266 266 def branches(ui, repo, active=False):
267 267 """list repository named branches
268 268
269 269 List the repository's named branches, indicating which ones are
270 270 inactive. If active is specified, only show active branches.
271 271
272 272 A branch is considered active if it contains unmerged heads.
273 273 """
274 274 b = repo.branchtags()
275 275 heads = dict.fromkeys(repo.heads(), 1)
276 276 l = [((n in heads), repo.changelog.rev(n), n, t) for t, n in b.items()]
277 277 l.sort()
278 278 l.reverse()
279 279 for ishead, r, n, t in l:
280 280 if active and not ishead:
281 281 # If we're only displaying active branches, abort the loop on
282 282 # encountering the first inactive head
283 283 break
284 284 else:
285 285 hexfunc = ui.debugflag and hex or short
286 286 if ui.quiet:
287 287 ui.write("%s\n" % t)
288 288 else:
289 289 spaces = " " * (30 - util.locallen(t))
290 290 # The code only gets here if inactive branches are being
291 291 # displayed or the branch is active.
292 292 isinactive = ((not ishead) and " (inactive)") or ''
293 293 ui.write("%s%s %s:%s%s\n" % (t, spaces, r, hexfunc(n), isinactive))
294 294
295 295 def bundle(ui, repo, fname, dest=None, **opts):
296 296 """create a changegroup file
297 297
298 298 Generate a compressed changegroup file collecting changesets not
299 299 found in the other repository.
300 300
301 301 If no destination repository is specified the destination is assumed
302 302 to have all the nodes specified by one or more --base parameters.
303 303
304 304 The bundle file can then be transferred using conventional means and
305 305 applied to another repository with the unbundle or pull command.
306 306 This is useful when direct push and pull are not available or when
307 307 exporting an entire repository is undesirable.
308 308
309 309 Applying bundles preserves all changeset contents including
310 310 permissions, copy/rename information, and revision history.
311 311 """
312 312 revs = opts.get('rev') or None
313 313 if revs:
314 314 revs = [repo.lookup(rev) for rev in revs]
315 315 base = opts.get('base')
316 316 if base:
317 317 if dest:
318 318 raise util.Abort(_("--base is incompatible with specifiying "
319 319 "a destination"))
320 320 base = [repo.lookup(rev) for rev in base]
321 321 # create the right base
322 322 # XXX: nodesbetween / changegroup* should be "fixed" instead
323 323 o = []
324 324 has = {nullid: None}
325 325 for n in base:
326 326 has.update(repo.changelog.reachable(n))
327 327 if revs:
328 328 visit = list(revs)
329 329 else:
330 330 visit = repo.changelog.heads()
331 331 seen = {}
332 332 while visit:
333 333 n = visit.pop(0)
334 334 parents = [p for p in repo.changelog.parents(n) if p not in has]
335 335 if len(parents) == 0:
336 336 o.insert(0, n)
337 337 else:
338 338 for p in parents:
339 339 if p not in seen:
340 340 seen[p] = 1
341 341 visit.append(p)
342 342 else:
343 343 cmdutil.setremoteconfig(ui, opts)
344 344 dest, revs, checkout = hg.parseurl(
345 345 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
346 346 other = hg.repository(ui, dest)
347 347 o = repo.findoutgoing(other, force=opts['force'])
348 348
349 349 if revs:
350 350 cg = repo.changegroupsubset(o, revs, 'bundle')
351 351 else:
352 352 cg = repo.changegroup(o, 'bundle')
353 353 changegroup.writebundle(cg, fname, "HG10BZ")
354 354
355 355 def cat(ui, repo, file1, *pats, **opts):
356 356 """output the current or given revision of files
357 357
358 358 Print the specified files as they were at the given revision.
359 359 If no revision is given, the parent of the working directory is used,
360 360 or tip if no revision is checked out.
361 361
362 362 Output may be to a file, in which case the name of the file is
363 363 given using a format string. The formatting rules are the same as
364 364 for the export command, with the following additions:
365 365
366 366 %s basename of file being printed
367 367 %d dirname of file being printed, or '.' if in repo root
368 368 %p root-relative path name of file being printed
369 369 """
370 370 ctx = repo.changectx(opts['rev'])
371 371 err = 1
372 372 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
373 373 ctx.node()):
374 374 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
375 375 fp.write(ctx.filectx(abs).data())
376 376 err = 0
377 377 return err
378 378
379 379 def clone(ui, source, dest=None, **opts):
380 380 """make a copy of an existing repository
381 381
382 382 Create a copy of an existing repository in a new directory.
383 383
384 384 If no destination directory name is specified, it defaults to the
385 385 basename of the source.
386 386
387 387 The location of the source is added to the new repository's
388 388 .hg/hgrc file, as the default to be used for future pulls.
389 389
390 390 For efficiency, hardlinks are used for cloning whenever the source
391 391 and destination are on the same filesystem (note this applies only
392 392 to the repository data, not to the checked out files). Some
393 393 filesystems, such as AFS, implement hardlinking incorrectly, but
394 394 do not report errors. In these cases, use the --pull option to
395 395 avoid hardlinking.
396 396
397 397 You can safely clone repositories and checked out files using full
398 398 hardlinks with
399 399
400 400 $ cp -al REPO REPOCLONE
401 401
402 402 which is the fastest way to clone. However, the operation is not
403 403 atomic (making sure REPO is not modified during the operation is
404 404 up to you) and you have to make sure your editor breaks hardlinks
405 405 (Emacs and most Linux Kernel tools do so).
406 406
407 407 If you use the -r option to clone up to a specific revision, no
408 408 subsequent revisions will be present in the cloned repository.
409 409 This option implies --pull, even on local repositories.
410 410
411 411 See pull for valid source format details.
412 412
413 413 It is possible to specify an ssh:// URL as the destination, but no
414 414 .hg/hgrc and working directory will be created on the remote side.
415 415 Look at the help text for the pull command for important details
416 416 about ssh:// URLs.
417 417 """
418 418 cmdutil.setremoteconfig(ui, opts)
419 419 hg.clone(ui, source, dest,
420 420 pull=opts['pull'],
421 421 stream=opts['uncompressed'],
422 422 rev=opts['rev'],
423 423 update=not opts['noupdate'])
424 424
425 425 def commit(ui, repo, *pats, **opts):
426 426 """commit the specified files or all outstanding changes
427 427
428 428 Commit changes to the given files into the repository.
429 429
430 430 If a list of files is omitted, all changes reported by "hg status"
431 431 will be committed.
432 432
433 433 If no commit message is specified, the editor configured in your hgrc
434 434 or in the EDITOR environment variable is started to enter a message.
435 435 """
436 436 def commitfunc(ui, repo, files, message, match, opts):
437 437 return repo.commit(files, message, opts['user'], opts['date'], match,
438 438 force_editor=opts.get('force_editor'))
439 439 cmdutil.commit(ui, repo, commitfunc, pats, opts)
440 440
441 441 def copy(ui, repo, *pats, **opts):
442 442 """mark files as copied for the next commit
443 443
444 444 Mark dest as having copies of source files. If dest is a
445 445 directory, copies are put in that directory. If dest is a file,
446 446 there can only be one source.
447 447
448 448 By default, this command copies the contents of files as they
449 449 stand in the working directory. If invoked with --after, the
450 450 operation is recorded, but no copying is performed.
451 451
452 452 This command takes effect in the next commit. To undo a copy
453 453 before that, see hg revert.
454 454 """
455 455 wlock = repo.wlock(False)
456 456 try:
457 errs, copied = cmdutil.copy(ui, repo, pats, opts)
457 return cmdutil.copy(ui, repo, pats, opts)
458 458 finally:
459 459 del wlock
460 return errs
461 460
462 461 def debugancestor(ui, index, rev1, rev2):
463 462 """find the ancestor revision of two revisions in a given index"""
464 463 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
465 464 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
466 465 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
467 466
468 467 def debugcomplete(ui, cmd='', **opts):
469 468 """returns the completion list associated with the given command"""
470 469
471 470 if opts['options']:
472 471 options = []
473 472 otables = [globalopts]
474 473 if cmd:
475 474 aliases, entry = cmdutil.findcmd(ui, cmd, table)
476 475 otables.append(entry[1])
477 476 for t in otables:
478 477 for o in t:
479 478 if o[0]:
480 479 options.append('-%s' % o[0])
481 480 options.append('--%s' % o[1])
482 481 ui.write("%s\n" % "\n".join(options))
483 482 return
484 483
485 484 clist = cmdutil.findpossible(ui, cmd, table).keys()
486 485 clist.sort()
487 486 ui.write("%s\n" % "\n".join(clist))
488 487
489 488 def debugrebuildstate(ui, repo, rev=""):
490 489 """rebuild the dirstate as it would look like for the given revision"""
491 490 if rev == "":
492 491 rev = repo.changelog.tip()
493 492 ctx = repo.changectx(rev)
494 493 files = ctx.manifest()
495 494 wlock = repo.wlock()
496 495 try:
497 496 repo.dirstate.rebuild(rev, files)
498 497 finally:
499 498 del wlock
500 499
501 500 def debugcheckstate(ui, repo):
502 501 """validate the correctness of the current dirstate"""
503 502 parent1, parent2 = repo.dirstate.parents()
504 503 m1 = repo.changectx(parent1).manifest()
505 504 m2 = repo.changectx(parent2).manifest()
506 505 errors = 0
507 506 for f in repo.dirstate:
508 507 state = repo.dirstate[f]
509 508 if state in "nr" and f not in m1:
510 509 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
511 510 errors += 1
512 511 if state in "a" and f in m1:
513 512 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
514 513 errors += 1
515 514 if state in "m" and f not in m1 and f not in m2:
516 515 ui.warn(_("%s in state %s, but not in either manifest\n") %
517 516 (f, state))
518 517 errors += 1
519 518 for f in m1:
520 519 state = repo.dirstate[f]
521 520 if state not in "nrm":
522 521 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
523 522 errors += 1
524 523 if errors:
525 524 error = _(".hg/dirstate inconsistent with current parent's manifest")
526 525 raise util.Abort(error)
527 526
528 527 def showconfig(ui, repo, *values, **opts):
529 528 """show combined config settings from all hgrc files
530 529
531 530 With no args, print names and values of all config items.
532 531
533 532 With one arg of the form section.name, print just the value of
534 533 that config item.
535 534
536 535 With multiple args, print names and values of all config items
537 536 with matching section names."""
538 537
539 538 untrusted = bool(opts.get('untrusted'))
540 539 if values:
541 540 if len([v for v in values if '.' in v]) > 1:
542 541 raise util.Abort(_('only one config item permitted'))
543 542 for section, name, value in ui.walkconfig(untrusted=untrusted):
544 543 sectname = section + '.' + name
545 544 if values:
546 545 for v in values:
547 546 if v == section:
548 547 ui.write('%s=%s\n' % (sectname, value))
549 548 elif v == sectname:
550 549 ui.write(value, '\n')
551 550 else:
552 551 ui.write('%s=%s\n' % (sectname, value))
553 552
554 553 def debugsetparents(ui, repo, rev1, rev2=None):
555 554 """manually set the parents of the current working directory
556 555
557 556 This is useful for writing repository conversion tools, but should
558 557 be used with care.
559 558 """
560 559
561 560 if not rev2:
562 561 rev2 = hex(nullid)
563 562
564 563 wlock = repo.wlock()
565 564 try:
566 565 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
567 566 finally:
568 567 del wlock
569 568
570 569 def debugstate(ui, repo):
571 570 """show the contents of the current dirstate"""
572 571 k = repo.dirstate._map.items()
573 572 k.sort()
574 573 for file_, ent in k:
575 574 if ent[3] == -1:
576 575 # Pad or slice to locale representation
577 576 locale_len = len(time.strftime("%x %X", time.localtime(0)))
578 577 timestr = 'unset'
579 578 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
580 579 else:
581 580 timestr = time.strftime("%x %X", time.localtime(ent[3]))
582 581 if ent[1] & 020000:
583 582 mode = 'lnk'
584 583 else:
585 584 mode = '%3o' % (ent[1] & 0777)
586 585 ui.write("%c %s %10d %s %s\n" % (ent[0], mode, ent[2], timestr, file_))
587 586 for f in repo.dirstate.copies():
588 587 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
589 588
590 589 def debugdata(ui, file_, rev):
591 590 """dump the contents of a data file revision"""
592 591 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
593 592 try:
594 593 ui.write(r.revision(r.lookup(rev)))
595 594 except KeyError:
596 595 raise util.Abort(_('invalid revision identifier %s') % rev)
597 596
598 597 def debugdate(ui, date, range=None, **opts):
599 598 """parse and display a date"""
600 599 if opts["extended"]:
601 600 d = util.parsedate(date, util.extendeddateformats)
602 601 else:
603 602 d = util.parsedate(date)
604 603 ui.write("internal: %s %s\n" % d)
605 604 ui.write("standard: %s\n" % util.datestr(d))
606 605 if range:
607 606 m = util.matchdate(range)
608 607 ui.write("match: %s\n" % m(d[0]))
609 608
610 609 def debugindex(ui, file_):
611 610 """dump the contents of an index file"""
612 611 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
613 612 ui.write(" rev offset length base linkrev" +
614 613 " nodeid p1 p2\n")
615 614 for i in xrange(r.count()):
616 615 node = r.node(i)
617 616 try:
618 617 pp = r.parents(node)
619 618 except:
620 619 pp = [nullid, nullid]
621 620 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
622 621 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
623 622 short(node), short(pp[0]), short(pp[1])))
624 623
625 624 def debugindexdot(ui, file_):
626 625 """dump an index DAG as a .dot file"""
627 626 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
628 627 ui.write("digraph G {\n")
629 628 for i in xrange(r.count()):
630 629 node = r.node(i)
631 630 pp = r.parents(node)
632 631 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
633 632 if pp[1] != nullid:
634 633 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
635 634 ui.write("}\n")
636 635
637 636 def debuginstall(ui):
638 637 '''test Mercurial installation'''
639 638
640 639 def writetemp(contents):
641 640 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
642 641 f = os.fdopen(fd, "wb")
643 642 f.write(contents)
644 643 f.close()
645 644 return name
646 645
647 646 problems = 0
648 647
649 648 # encoding
650 649 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
651 650 try:
652 651 util.fromlocal("test")
653 652 except util.Abort, inst:
654 653 ui.write(" %s\n" % inst)
655 654 ui.write(_(" (check that your locale is properly set)\n"))
656 655 problems += 1
657 656
658 657 # compiled modules
659 658 ui.status(_("Checking extensions...\n"))
660 659 try:
661 660 import bdiff, mpatch, base85
662 661 except Exception, inst:
663 662 ui.write(" %s\n" % inst)
664 663 ui.write(_(" One or more extensions could not be found"))
665 664 ui.write(_(" (check that you compiled the extensions)\n"))
666 665 problems += 1
667 666
668 667 # templates
669 668 ui.status(_("Checking templates...\n"))
670 669 try:
671 670 import templater
672 671 t = templater.templater(templater.templatepath("map-cmdline.default"))
673 672 except Exception, inst:
674 673 ui.write(" %s\n" % inst)
675 674 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
676 675 problems += 1
677 676
678 677 # patch
679 678 ui.status(_("Checking patch...\n"))
680 679 patchproblems = 0
681 680 a = "1\n2\n3\n4\n"
682 681 b = "1\n2\n3\ninsert\n4\n"
683 682 fa = writetemp(a)
684 683 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
685 684 os.path.basename(fa))
686 685 fd = writetemp(d)
687 686
688 687 files = {}
689 688 try:
690 689 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
691 690 except util.Abort, e:
692 691 ui.write(_(" patch call failed:\n"))
693 692 ui.write(" " + str(e) + "\n")
694 693 patchproblems += 1
695 694 else:
696 695 if list(files) != [os.path.basename(fa)]:
697 696 ui.write(_(" unexpected patch output!\n"))
698 697 patchproblems += 1
699 698 a = file(fa).read()
700 699 if a != b:
701 700 ui.write(_(" patch test failed!\n"))
702 701 patchproblems += 1
703 702
704 703 if patchproblems:
705 704 if ui.config('ui', 'patch'):
706 705 ui.write(_(" (Current patch tool may be incompatible with patch,"
707 706 " or misconfigured. Please check your .hgrc file)\n"))
708 707 else:
709 708 ui.write(_(" Internal patcher failure, please report this error"
710 709 " to http://www.selenic.com/mercurial/bts\n"))
711 710 problems += patchproblems
712 711
713 712 os.unlink(fa)
714 713 os.unlink(fd)
715 714
716 715 # merge helper
717 716 ui.status(_("Checking merge helper...\n"))
718 717 cmd = (os.environ.get("HGMERGE") or ui.config("ui", "merge")
719 718 or "hgmerge")
720 719 cmdpath = util.find_exe(cmd) or util.find_exe(cmd.split()[0])
721 720 if not cmdpath:
722 721 if cmd == 'hgmerge':
723 722 ui.write(_(" No merge helper set and can't find default"
724 723 " hgmerge script in PATH\n"))
725 724 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
726 725 else:
727 726 ui.write(_(" Can't find merge helper '%s' in PATH\n") % cmd)
728 727 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
729 728 problems += 1
730 729 else:
731 730 # actually attempt a patch here
732 731 fa = writetemp("1\n2\n3\n4\n")
733 732 fl = writetemp("1\n2\n3\ninsert\n4\n")
734 733 fr = writetemp("begin\n1\n2\n3\n4\n")
735 734 r = util.system('%s "%s" "%s" "%s"' % (cmd, fl, fa, fr))
736 735 if r:
737 736 ui.write(_(" Got unexpected merge error %d!\n") % r)
738 737 problems += 1
739 738 m = file(fl).read()
740 739 if m != "begin\n1\n2\n3\ninsert\n4\n":
741 740 ui.write(_(" Got unexpected merge results!\n"))
742 741 ui.write(_(" (your merge helper may have the"
743 742 " wrong argument order)\n"))
744 743 ui.write(_(" Result: %r\n") % m)
745 744 problems += 1
746 745 os.unlink(fa)
747 746 os.unlink(fl)
748 747 os.unlink(fr)
749 748
750 749 # editor
751 750 ui.status(_("Checking commit editor...\n"))
752 751 editor = (os.environ.get("HGEDITOR") or
753 752 ui.config("ui", "editor") or
754 753 os.environ.get("EDITOR", "vi"))
755 754 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
756 755 if not cmdpath:
757 756 if editor == 'vi':
758 757 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
759 758 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
760 759 else:
761 760 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
762 761 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
763 762 problems += 1
764 763
765 764 # check username
766 765 ui.status(_("Checking username...\n"))
767 766 user = os.environ.get("HGUSER")
768 767 if user is None:
769 768 user = ui.config("ui", "username")
770 769 if user is None:
771 770 user = os.environ.get("EMAIL")
772 771 if not user:
773 772 ui.warn(" ")
774 773 ui.username()
775 774 ui.write(_(" (specify a username in your .hgrc file)\n"))
776 775
777 776 if not problems:
778 777 ui.status(_("No problems detected\n"))
779 778 else:
780 779 ui.write(_("%s problems detected,"
781 780 " please check your install!\n") % problems)
782 781
783 782 return problems
784 783
785 784 def debugrename(ui, repo, file1, *pats, **opts):
786 785 """dump rename information"""
787 786
788 787 ctx = repo.changectx(opts.get('rev', 'tip'))
789 788 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
790 789 ctx.node()):
791 790 m = ctx.filectx(abs).renamed()
792 791 if m:
793 792 ui.write(_("%s renamed from %s:%s\n") % (rel, m[0], hex(m[1])))
794 793 else:
795 794 ui.write(_("%s not renamed\n") % rel)
796 795
797 796 def debugwalk(ui, repo, *pats, **opts):
798 797 """show how files match on given patterns"""
799 798 items = list(cmdutil.walk(repo, pats, opts))
800 799 if not items:
801 800 return
802 801 fmt = '%%s %%-%ds %%-%ds %%s' % (
803 802 max([len(abs) for (src, abs, rel, exact) in items]),
804 803 max([len(rel) for (src, abs, rel, exact) in items]))
805 804 for src, abs, rel, exact in items:
806 805 line = fmt % (src, abs, rel, exact and 'exact' or '')
807 806 ui.write("%s\n" % line.rstrip())
808 807
809 808 def diff(ui, repo, *pats, **opts):
810 809 """diff repository (or selected files)
811 810
812 811 Show differences between revisions for the specified files.
813 812
814 813 Differences between files are shown using the unified diff format.
815 814
816 815 NOTE: diff may generate unexpected results for merges, as it will
817 816 default to comparing against the working directory's first parent
818 817 changeset if no revisions are specified.
819 818
820 819 When two revision arguments are given, then changes are shown
821 820 between those revisions. If only one revision is specified then
822 821 that revision is compared to the working directory, and, when no
823 822 revisions are specified, the working directory files are compared
824 823 to its parent.
825 824
826 825 Without the -a option, diff will avoid generating diffs of files
827 826 it detects as binary. With -a, diff will generate a diff anyway,
828 827 probably with undesirable results.
829 828 """
830 829 node1, node2 = cmdutil.revpair(repo, opts['rev'])
831 830
832 831 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
833 832
834 833 patch.diff(repo, node1, node2, fns, match=matchfn,
835 834 opts=patch.diffopts(ui, opts))
836 835
837 836 def export(ui, repo, *changesets, **opts):
838 837 """dump the header and diffs for one or more changesets
839 838
840 839 Print the changeset header and diffs for one or more revisions.
841 840
842 841 The information shown in the changeset header is: author,
843 842 changeset hash, parent(s) and commit comment.
844 843
845 844 NOTE: export may generate unexpected diff output for merge changesets,
846 845 as it will compare the merge changeset against its first parent only.
847 846
848 847 Output may be to a file, in which case the name of the file is
849 848 given using a format string. The formatting rules are as follows:
850 849
851 850 %% literal "%" character
852 851 %H changeset hash (40 bytes of hexadecimal)
853 852 %N number of patches being generated
854 853 %R changeset revision number
855 854 %b basename of the exporting repository
856 855 %h short-form changeset hash (12 bytes of hexadecimal)
857 856 %n zero-padded sequence number, starting at 1
858 857 %r zero-padded changeset revision number
859 858
860 859 Without the -a option, export will avoid generating diffs of files
861 860 it detects as binary. With -a, export will generate a diff anyway,
862 861 probably with undesirable results.
863 862
864 863 With the --switch-parent option, the diff will be against the second
865 864 parent. It can be useful to review a merge.
866 865 """
867 866 if not changesets:
868 867 raise util.Abort(_("export requires at least one changeset"))
869 868 revs = cmdutil.revrange(repo, changesets)
870 869 if len(revs) > 1:
871 870 ui.note(_('exporting patches:\n'))
872 871 else:
873 872 ui.note(_('exporting patch:\n'))
874 873 patch.export(repo, revs, template=opts['output'],
875 874 switch_parent=opts['switch_parent'],
876 875 opts=patch.diffopts(ui, opts))
877 876
878 877 def grep(ui, repo, pattern, *pats, **opts):
879 878 """search for a pattern in specified files and revisions
880 879
881 880 Search revisions of files for a regular expression.
882 881
883 882 This command behaves differently than Unix grep. It only accepts
884 883 Python/Perl regexps. It searches repository history, not the
885 884 working directory. It always prints the revision number in which
886 885 a match appears.
887 886
888 887 By default, grep only prints output for the first revision of a
889 888 file in which it finds a match. To get it to print every revision
890 889 that contains a change in match status ("-" for a match that
891 890 becomes a non-match, or "+" for a non-match that becomes a match),
892 891 use the --all flag.
893 892 """
894 893 reflags = 0
895 894 if opts['ignore_case']:
896 895 reflags |= re.I
897 896 try:
898 897 regexp = re.compile(pattern, reflags)
899 898 except Exception, inst:
900 899 ui.warn(_("grep: invalid match pattern: %s!\n") % inst)
901 900 return None
902 901 sep, eol = ':', '\n'
903 902 if opts['print0']:
904 903 sep = eol = '\0'
905 904
906 905 fcache = {}
907 906 def getfile(fn):
908 907 if fn not in fcache:
909 908 fcache[fn] = repo.file(fn)
910 909 return fcache[fn]
911 910
912 911 def matchlines(body):
913 912 begin = 0
914 913 linenum = 0
915 914 while True:
916 915 match = regexp.search(body, begin)
917 916 if not match:
918 917 break
919 918 mstart, mend = match.span()
920 919 linenum += body.count('\n', begin, mstart) + 1
921 920 lstart = body.rfind('\n', begin, mstart) + 1 or begin
922 921 lend = body.find('\n', mend)
923 922 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
924 923 begin = lend + 1
925 924
926 925 class linestate(object):
927 926 def __init__(self, line, linenum, colstart, colend):
928 927 self.line = line
929 928 self.linenum = linenum
930 929 self.colstart = colstart
931 930 self.colend = colend
932 931
933 932 def __eq__(self, other):
934 933 return self.line == other.line
935 934
936 935 matches = {}
937 936 copies = {}
938 937 def grepbody(fn, rev, body):
939 938 matches[rev].setdefault(fn, [])
940 939 m = matches[rev][fn]
941 940 for lnum, cstart, cend, line in matchlines(body):
942 941 s = linestate(line, lnum, cstart, cend)
943 942 m.append(s)
944 943
945 944 def difflinestates(a, b):
946 945 sm = difflib.SequenceMatcher(None, a, b)
947 946 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
948 947 if tag == 'insert':
949 948 for i in xrange(blo, bhi):
950 949 yield ('+', b[i])
951 950 elif tag == 'delete':
952 951 for i in xrange(alo, ahi):
953 952 yield ('-', a[i])
954 953 elif tag == 'replace':
955 954 for i in xrange(alo, ahi):
956 955 yield ('-', a[i])
957 956 for i in xrange(blo, bhi):
958 957 yield ('+', b[i])
959 958
960 959 prev = {}
961 960 def display(fn, rev, states, prevstates):
962 961 found = False
963 962 filerevmatches = {}
964 963 r = prev.get(fn, -1)
965 964 if opts['all']:
966 965 iter = difflinestates(states, prevstates)
967 966 else:
968 967 iter = [('', l) for l in prevstates]
969 968 for change, l in iter:
970 969 cols = [fn, str(r)]
971 970 if opts['line_number']:
972 971 cols.append(str(l.linenum))
973 972 if opts['all']:
974 973 cols.append(change)
975 974 if opts['user']:
976 975 cols.append(ui.shortuser(get(r)[1]))
977 976 if opts['files_with_matches']:
978 977 c = (fn, r)
979 978 if c in filerevmatches:
980 979 continue
981 980 filerevmatches[c] = 1
982 981 else:
983 982 cols.append(l.line)
984 983 ui.write(sep.join(cols), eol)
985 984 found = True
986 985 return found
987 986
988 987 fstate = {}
989 988 skip = {}
990 989 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
991 990 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
992 991 found = False
993 992 follow = opts.get('follow')
994 993 for st, rev, fns in changeiter:
995 994 if st == 'window':
996 995 matches.clear()
997 996 elif st == 'add':
998 997 mf = repo.changectx(rev).manifest()
999 998 matches[rev] = {}
1000 999 for fn in fns:
1001 1000 if fn in skip:
1002 1001 continue
1003 1002 try:
1004 1003 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1005 1004 fstate.setdefault(fn, [])
1006 1005 if follow:
1007 1006 copied = getfile(fn).renamed(mf[fn])
1008 1007 if copied:
1009 1008 copies.setdefault(rev, {})[fn] = copied[0]
1010 1009 except KeyError:
1011 1010 pass
1012 1011 elif st == 'iter':
1013 1012 states = matches[rev].items()
1014 1013 states.sort()
1015 1014 for fn, m in states:
1016 1015 copy = copies.get(rev, {}).get(fn)
1017 1016 if fn in skip:
1018 1017 if copy:
1019 1018 skip[copy] = True
1020 1019 continue
1021 1020 if fn in prev or fstate[fn]:
1022 1021 r = display(fn, rev, m, fstate[fn])
1023 1022 found = found or r
1024 1023 if r and not opts['all']:
1025 1024 skip[fn] = True
1026 1025 if copy:
1027 1026 skip[copy] = True
1028 1027 fstate[fn] = m
1029 1028 if copy:
1030 1029 fstate[copy] = m
1031 1030 prev[fn] = rev
1032 1031
1033 1032 fstate = fstate.items()
1034 1033 fstate.sort()
1035 1034 for fn, state in fstate:
1036 1035 if fn in skip:
1037 1036 continue
1038 1037 if fn not in copies.get(prev[fn], {}):
1039 1038 found = display(fn, rev, {}, state) or found
1040 1039 return (not found and 1) or 0
1041 1040
1042 1041 def heads(ui, repo, *branchrevs, **opts):
1043 1042 """show current repository heads or show branch heads
1044 1043
1045 1044 With no arguments, show all repository head changesets.
1046 1045
1047 1046 If branch or revisions names are given this will show the heads of
1048 1047 the specified branches or the branches those revisions are tagged
1049 1048 with.
1050 1049
1051 1050 Repository "heads" are changesets that don't have child
1052 1051 changesets. They are where development generally takes place and
1053 1052 are the usual targets for update and merge operations.
1054 1053
1055 1054 Branch heads are changesets that have a given branch tag, but have
1056 1055 no child changesets with that tag. They are usually where
1057 1056 development on the given branch takes place.
1058 1057 """
1059 1058 if opts['rev']:
1060 1059 start = repo.lookup(opts['rev'])
1061 1060 else:
1062 1061 start = None
1063 1062 if not branchrevs:
1064 1063 # Assume we're looking repo-wide heads if no revs were specified.
1065 1064 heads = repo.heads(start)
1066 1065 else:
1067 1066 heads = []
1068 1067 visitedset = util.set()
1069 1068 for branchrev in branchrevs:
1070 1069 branch = repo.changectx(branchrev).branch()
1071 1070 if branch in visitedset:
1072 1071 continue
1073 1072 visitedset.add(branch)
1074 1073 bheads = repo.branchheads(branch, start)
1075 1074 if not bheads:
1076 1075 if branch != branchrev:
1077 1076 ui.warn(_("no changes on branch %s containing %s are "
1078 1077 "reachable from %s\n")
1079 1078 % (branch, branchrev, opts['rev']))
1080 1079 else:
1081 1080 ui.warn(_("no changes on branch %s are reachable from %s\n")
1082 1081 % (branch, opts['rev']))
1083 1082 heads.extend(bheads)
1084 1083 if not heads:
1085 1084 return 1
1086 1085 displayer = cmdutil.show_changeset(ui, repo, opts)
1087 1086 for n in heads:
1088 1087 displayer.show(changenode=n)
1089 1088
1090 1089 def help_(ui, name=None, with_version=False):
1091 1090 """show help for a command, extension, or list of commands
1092 1091
1093 1092 With no arguments, print a list of commands and short help.
1094 1093
1095 1094 Given a command name, print help for that command.
1096 1095
1097 1096 Given an extension name, print help for that extension, and the
1098 1097 commands it provides."""
1099 1098 option_lists = []
1100 1099
1101 1100 def addglobalopts(aliases):
1102 1101 if ui.verbose:
1103 1102 option_lists.append((_("global options:"), globalopts))
1104 1103 if name == 'shortlist':
1105 1104 option_lists.append((_('use "hg help" for the full list '
1106 1105 'of commands'), ()))
1107 1106 else:
1108 1107 if name == 'shortlist':
1109 1108 msg = _('use "hg help" for the full list of commands '
1110 1109 'or "hg -v" for details')
1111 1110 elif aliases:
1112 1111 msg = _('use "hg -v help%s" to show aliases and '
1113 1112 'global options') % (name and " " + name or "")
1114 1113 else:
1115 1114 msg = _('use "hg -v help %s" to show global options') % name
1116 1115 option_lists.append((msg, ()))
1117 1116
1118 1117 def helpcmd(name):
1119 1118 if with_version:
1120 1119 version_(ui)
1121 1120 ui.write('\n')
1122 1121 aliases, i = cmdutil.findcmd(ui, name, table)
1123 1122 # synopsis
1124 1123 ui.write("%s\n\n" % i[2])
1125 1124
1126 1125 # description
1127 1126 doc = i[0].__doc__
1128 1127 if not doc:
1129 1128 doc = _("(No help text available)")
1130 1129 if ui.quiet:
1131 1130 doc = doc.splitlines(0)[0]
1132 1131 ui.write("%s\n" % doc.rstrip())
1133 1132
1134 1133 if not ui.quiet:
1135 1134 # aliases
1136 1135 if len(aliases) > 1:
1137 1136 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1138 1137
1139 1138 # options
1140 1139 if i[1]:
1141 1140 option_lists.append((_("options:\n"), i[1]))
1142 1141
1143 1142 addglobalopts(False)
1144 1143
1145 1144 def helplist(header, select=None):
1146 1145 h = {}
1147 1146 cmds = {}
1148 1147 for c, e in table.items():
1149 1148 f = c.split("|", 1)[0]
1150 1149 if select and not select(f):
1151 1150 continue
1152 1151 if name == "shortlist" and not f.startswith("^"):
1153 1152 continue
1154 1153 f = f.lstrip("^")
1155 1154 if not ui.debugflag and f.startswith("debug"):
1156 1155 continue
1157 1156 doc = e[0].__doc__
1158 1157 if not doc:
1159 1158 doc = _("(No help text available)")
1160 1159 h[f] = doc.splitlines(0)[0].rstrip()
1161 1160 cmds[f] = c.lstrip("^")
1162 1161
1163 1162 if not h:
1164 1163 ui.status(_('no commands defined\n'))
1165 1164 return
1166 1165
1167 1166 ui.status(header)
1168 1167 fns = h.keys()
1169 1168 fns.sort()
1170 1169 m = max(map(len, fns))
1171 1170 for f in fns:
1172 1171 if ui.verbose:
1173 1172 commands = cmds[f].replace("|",", ")
1174 1173 ui.write(" %s:\n %s\n"%(commands, h[f]))
1175 1174 else:
1176 1175 ui.write(' %-*s %s\n' % (m, f, h[f]))
1177 1176
1178 1177 if not ui.quiet:
1179 1178 addglobalopts(True)
1180 1179
1181 1180 def helptopic(name):
1182 1181 v = None
1183 1182 for i in help.helptable:
1184 1183 l = i.split('|')
1185 1184 if name in l:
1186 1185 v = i
1187 1186 header = l[-1]
1188 1187 if not v:
1189 1188 raise cmdutil.UnknownCommand(name)
1190 1189
1191 1190 # description
1192 1191 doc = help.helptable[v]
1193 1192 if not doc:
1194 1193 doc = _("(No help text available)")
1195 1194 if callable(doc):
1196 1195 doc = doc()
1197 1196
1198 1197 ui.write("%s\n" % header)
1199 1198 ui.write("%s\n" % doc.rstrip())
1200 1199
1201 1200 def helpext(name):
1202 1201 try:
1203 1202 mod = extensions.find(name)
1204 1203 except KeyError:
1205 1204 raise cmdutil.UnknownCommand(name)
1206 1205
1207 1206 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1208 1207 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1209 1208 for d in doc[1:]:
1210 1209 ui.write(d, '\n')
1211 1210
1212 1211 ui.status('\n')
1213 1212
1214 1213 try:
1215 1214 ct = mod.cmdtable
1216 1215 except AttributeError:
1217 1216 ct = {}
1218 1217
1219 1218 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1220 1219 helplist(_('list of commands:\n\n'), modcmds.has_key)
1221 1220
1222 1221 if name and name != 'shortlist':
1223 1222 i = None
1224 1223 for f in (helpcmd, helptopic, helpext):
1225 1224 try:
1226 1225 f(name)
1227 1226 i = None
1228 1227 break
1229 1228 except cmdutil.UnknownCommand, inst:
1230 1229 i = inst
1231 1230 if i:
1232 1231 raise i
1233 1232
1234 1233 else:
1235 1234 # program name
1236 1235 if ui.verbose or with_version:
1237 1236 version_(ui)
1238 1237 else:
1239 1238 ui.status(_("Mercurial Distributed SCM\n"))
1240 1239 ui.status('\n')
1241 1240
1242 1241 # list of commands
1243 1242 if name == "shortlist":
1244 1243 header = _('basic commands:\n\n')
1245 1244 else:
1246 1245 header = _('list of commands:\n\n')
1247 1246
1248 1247 helplist(header)
1249 1248
1250 1249 # list all option lists
1251 1250 opt_output = []
1252 1251 for title, options in option_lists:
1253 1252 opt_output.append(("\n%s" % title, None))
1254 1253 for shortopt, longopt, default, desc in options:
1255 1254 if "DEPRECATED" in desc and not ui.verbose: continue
1256 1255 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1257 1256 longopt and " --%s" % longopt),
1258 1257 "%s%s" % (desc,
1259 1258 default
1260 1259 and _(" (default: %s)") % default
1261 1260 or "")))
1262 1261
1263 1262 if opt_output:
1264 1263 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1265 1264 for first, second in opt_output:
1266 1265 if second:
1267 1266 ui.write(" %-*s %s\n" % (opts_len, first, second))
1268 1267 else:
1269 1268 ui.write("%s\n" % first)
1270 1269
1271 1270 def identify(ui, repo, source=None,
1272 1271 rev=None, num=None, id=None, branch=None, tags=None):
1273 1272 """identify the working copy or specified revision
1274 1273
1275 1274 With no revision, print a summary of the current state of the repo.
1276 1275
1277 1276 With a path, do a lookup in another repository.
1278 1277
1279 1278 This summary identifies the repository state using one or two parent
1280 1279 hash identifiers, followed by a "+" if there are uncommitted changes
1281 1280 in the working directory, a list of tags for this revision and a branch
1282 1281 name for non-default branches.
1283 1282 """
1284 1283
1285 1284 if not repo and not source:
1286 1285 raise util.Abort(_("There is no Mercurial repository here "
1287 1286 "(.hg not found)"))
1288 1287
1289 1288 hexfunc = ui.debugflag and hex or short
1290 1289 default = not (num or id or branch or tags)
1291 1290 output = []
1292 1291
1293 1292 if source:
1294 1293 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1295 1294 srepo = hg.repository(ui, source)
1296 1295 if not rev and revs:
1297 1296 rev = revs[0]
1298 1297 if not rev:
1299 1298 rev = "tip"
1300 1299 if num or branch or tags:
1301 1300 raise util.Abort(
1302 1301 "can't query remote revision number, branch, or tags")
1303 1302 output = [hexfunc(srepo.lookup(rev))]
1304 1303 elif not rev:
1305 1304 ctx = repo.workingctx()
1306 1305 parents = ctx.parents()
1307 1306 changed = False
1308 1307 if default or id or num:
1309 1308 changed = ctx.files() + ctx.deleted()
1310 1309 if default or id:
1311 1310 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1312 1311 (changed) and "+" or "")]
1313 1312 if num:
1314 1313 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1315 1314 (changed) and "+" or ""))
1316 1315 else:
1317 1316 ctx = repo.changectx(rev)
1318 1317 if default or id:
1319 1318 output = [hexfunc(ctx.node())]
1320 1319 if num:
1321 1320 output.append(str(ctx.rev()))
1322 1321
1323 1322 if not source and default and not ui.quiet:
1324 1323 b = util.tolocal(ctx.branch())
1325 1324 if b != 'default':
1326 1325 output.append("(%s)" % b)
1327 1326
1328 1327 # multiple tags for a single parent separated by '/'
1329 1328 t = "/".join(ctx.tags())
1330 1329 if t:
1331 1330 output.append(t)
1332 1331
1333 1332 if branch:
1334 1333 output.append(util.tolocal(ctx.branch()))
1335 1334
1336 1335 if tags:
1337 1336 output.extend(ctx.tags())
1338 1337
1339 1338 ui.write("%s\n" % ' '.join(output))
1340 1339
1341 1340 def import_(ui, repo, patch1, *patches, **opts):
1342 1341 """import an ordered set of patches
1343 1342
1344 1343 Import a list of patches and commit them individually.
1345 1344
1346 1345 If there are outstanding changes in the working directory, import
1347 1346 will abort unless given the -f flag.
1348 1347
1349 1348 You can import a patch straight from a mail message. Even patches
1350 1349 as attachments work (body part must be type text/plain or
1351 1350 text/x-patch to be used). From and Subject headers of email
1352 1351 message are used as default committer and commit message. All
1353 1352 text/plain body parts before first diff are added to commit
1354 1353 message.
1355 1354
1356 1355 If the imported patch was generated by hg export, user and description
1357 1356 from patch override values from message headers and body. Values
1358 1357 given on command line with -m and -u override these.
1359 1358
1360 1359 If --exact is specified, import will set the working directory
1361 1360 to the parent of each patch before applying it, and will abort
1362 1361 if the resulting changeset has a different ID than the one
1363 1362 recorded in the patch. This may happen due to character set
1364 1363 problems or other deficiencies in the text patch format.
1365 1364
1366 1365 To read a patch from standard input, use patch name "-".
1367 1366 """
1368 1367 patches = (patch1,) + patches
1369 1368
1370 1369 if opts.get('exact') or not opts['force']:
1371 1370 cmdutil.bail_if_changed(repo)
1372 1371
1373 1372 d = opts["base"]
1374 1373 strip = opts["strip"]
1375 1374 wlock = lock = None
1376 1375 try:
1377 1376 wlock = repo.wlock()
1378 1377 lock = repo.lock()
1379 1378 for p in patches:
1380 1379 pf = os.path.join(d, p)
1381 1380
1382 1381 if pf == '-':
1383 1382 ui.status(_("applying patch from stdin\n"))
1384 1383 data = patch.extract(ui, sys.stdin)
1385 1384 else:
1386 1385 ui.status(_("applying %s\n") % p)
1387 1386 if os.path.exists(pf):
1388 1387 data = patch.extract(ui, file(pf, 'rb'))
1389 1388 else:
1390 1389 data = patch.extract(ui, urllib.urlopen(pf))
1391 1390 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1392 1391
1393 1392 if tmpname is None:
1394 1393 raise util.Abort(_('no diffs found'))
1395 1394
1396 1395 try:
1397 1396 cmdline_message = cmdutil.logmessage(opts)
1398 1397 if cmdline_message:
1399 1398 # pickup the cmdline msg
1400 1399 message = cmdline_message
1401 1400 elif message:
1402 1401 # pickup the patch msg
1403 1402 message = message.strip()
1404 1403 else:
1405 1404 # launch the editor
1406 1405 message = None
1407 1406 ui.debug(_('message:\n%s\n') % message)
1408 1407
1409 1408 wp = repo.workingctx().parents()
1410 1409 if opts.get('exact'):
1411 1410 if not nodeid or not p1:
1412 1411 raise util.Abort(_('not a mercurial patch'))
1413 1412 p1 = repo.lookup(p1)
1414 1413 p2 = repo.lookup(p2 or hex(nullid))
1415 1414
1416 1415 if p1 != wp[0].node():
1417 1416 hg.clean(repo, p1)
1418 1417 repo.dirstate.setparents(p1, p2)
1419 1418 elif p2:
1420 1419 try:
1421 1420 p1 = repo.lookup(p1)
1422 1421 p2 = repo.lookup(p2)
1423 1422 if p1 == wp[0].node():
1424 1423 repo.dirstate.setparents(p1, p2)
1425 1424 except hg.RepoError:
1426 1425 pass
1427 1426 if opts.get('exact') or opts.get('import_branch'):
1428 1427 repo.dirstate.setbranch(branch or 'default')
1429 1428
1430 1429 files = {}
1431 1430 try:
1432 1431 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1433 1432 files=files)
1434 1433 finally:
1435 1434 files = patch.updatedir(ui, repo, files)
1436 1435 n = repo.commit(files, message, user, date)
1437 1436 if opts.get('exact'):
1438 1437 if hex(n) != nodeid:
1439 1438 repo.rollback()
1440 1439 raise util.Abort(_('patch is damaged'
1441 1440 ' or loses information'))
1442 1441 finally:
1443 1442 os.unlink(tmpname)
1444 1443 finally:
1445 1444 del lock, wlock
1446 1445
1447 1446 def incoming(ui, repo, source="default", **opts):
1448 1447 """show new changesets found in source
1449 1448
1450 1449 Show new changesets found in the specified path/URL or the default
1451 1450 pull location. These are the changesets that would be pulled if a pull
1452 1451 was requested.
1453 1452
1454 1453 For remote repository, using --bundle avoids downloading the changesets
1455 1454 twice if the incoming is followed by a pull.
1456 1455
1457 1456 See pull for valid source format details.
1458 1457 """
1459 1458 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts['rev'])
1460 1459 cmdutil.setremoteconfig(ui, opts)
1461 1460
1462 1461 other = hg.repository(ui, source)
1463 1462 ui.status(_('comparing with %s\n') % util.hidepassword(source))
1464 1463 if revs:
1465 1464 revs = [other.lookup(rev) for rev in revs]
1466 1465 incoming = repo.findincoming(other, heads=revs, force=opts["force"])
1467 1466 if not incoming:
1468 1467 try:
1469 1468 os.unlink(opts["bundle"])
1470 1469 except:
1471 1470 pass
1472 1471 ui.status(_("no changes found\n"))
1473 1472 return 1
1474 1473
1475 1474 cleanup = None
1476 1475 try:
1477 1476 fname = opts["bundle"]
1478 1477 if fname or not other.local():
1479 1478 # create a bundle (uncompressed if other repo is not local)
1480 1479 if revs is None:
1481 1480 cg = other.changegroup(incoming, "incoming")
1482 1481 else:
1483 1482 cg = other.changegroupsubset(incoming, revs, 'incoming')
1484 1483 bundletype = other.local() and "HG10BZ" or "HG10UN"
1485 1484 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1486 1485 # keep written bundle?
1487 1486 if opts["bundle"]:
1488 1487 cleanup = None
1489 1488 if not other.local():
1490 1489 # use the created uncompressed bundlerepo
1491 1490 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1492 1491
1493 1492 o = other.changelog.nodesbetween(incoming, revs)[0]
1494 1493 if opts['newest_first']:
1495 1494 o.reverse()
1496 1495 displayer = cmdutil.show_changeset(ui, other, opts)
1497 1496 for n in o:
1498 1497 parents = [p for p in other.changelog.parents(n) if p != nullid]
1499 1498 if opts['no_merges'] and len(parents) == 2:
1500 1499 continue
1501 1500 displayer.show(changenode=n)
1502 1501 finally:
1503 1502 if hasattr(other, 'close'):
1504 1503 other.close()
1505 1504 if cleanup:
1506 1505 os.unlink(cleanup)
1507 1506
1508 1507 def init(ui, dest=".", **opts):
1509 1508 """create a new repository in the given directory
1510 1509
1511 1510 Initialize a new repository in the given directory. If the given
1512 1511 directory does not exist, it is created.
1513 1512
1514 1513 If no directory is given, the current directory is used.
1515 1514
1516 1515 It is possible to specify an ssh:// URL as the destination.
1517 1516 Look at the help text for the pull command for important details
1518 1517 about ssh:// URLs.
1519 1518 """
1520 1519 cmdutil.setremoteconfig(ui, opts)
1521 1520 hg.repository(ui, dest, create=1)
1522 1521
1523 1522 def locate(ui, repo, *pats, **opts):
1524 1523 """locate files matching specific patterns
1525 1524
1526 1525 Print all files under Mercurial control whose names match the
1527 1526 given patterns.
1528 1527
1529 1528 This command searches the entire repository by default. To search
1530 1529 just the current directory and its subdirectories, use
1531 1530 "--include .".
1532 1531
1533 1532 If no patterns are given to match, this command prints all file
1534 1533 names.
1535 1534
1536 1535 If you want to feed the output of this command into the "xargs"
1537 1536 command, use the "-0" option to both this command and "xargs".
1538 1537 This will avoid the problem of "xargs" treating single filenames
1539 1538 that contain white space as multiple filenames.
1540 1539 """
1541 1540 end = opts['print0'] and '\0' or '\n'
1542 1541 rev = opts['rev']
1543 1542 if rev:
1544 1543 node = repo.lookup(rev)
1545 1544 else:
1546 1545 node = None
1547 1546
1548 1547 ret = 1
1549 1548 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1550 1549 badmatch=util.always,
1551 1550 default='relglob'):
1552 1551 if src == 'b':
1553 1552 continue
1554 1553 if not node and abs not in repo.dirstate:
1555 1554 continue
1556 1555 if opts['fullpath']:
1557 1556 ui.write(os.path.join(repo.root, abs), end)
1558 1557 else:
1559 1558 ui.write(((pats and rel) or abs), end)
1560 1559 ret = 0
1561 1560
1562 1561 return ret
1563 1562
1564 1563 def log(ui, repo, *pats, **opts):
1565 1564 """show revision history of entire repository or files
1566 1565
1567 1566 Print the revision history of the specified files or the entire
1568 1567 project.
1569 1568
1570 1569 File history is shown without following rename or copy history of
1571 1570 files. Use -f/--follow with a file name to follow history across
1572 1571 renames and copies. --follow without a file name will only show
1573 1572 ancestors or descendants of the starting revision. --follow-first
1574 1573 only follows the first parent of merge revisions.
1575 1574
1576 1575 If no revision range is specified, the default is tip:0 unless
1577 1576 --follow is set, in which case the working directory parent is
1578 1577 used as the starting revision.
1579 1578
1580 1579 By default this command outputs: changeset id and hash, tags,
1581 1580 non-trivial parents, user, date and time, and a summary for each
1582 1581 commit. When the -v/--verbose switch is used, the list of changed
1583 1582 files and full commit message is shown.
1584 1583
1585 1584 NOTE: log -p may generate unexpected diff output for merge
1586 1585 changesets, as it will compare the merge changeset against its
1587 1586 first parent only. Also, the files: list will only reflect files
1588 1587 that are different from BOTH parents.
1589 1588
1590 1589 """
1591 1590
1592 1591 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1593 1592 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1594 1593
1595 1594 if opts['limit']:
1596 1595 try:
1597 1596 limit = int(opts['limit'])
1598 1597 except ValueError:
1599 1598 raise util.Abort(_('limit must be a positive integer'))
1600 1599 if limit <= 0: raise util.Abort(_('limit must be positive'))
1601 1600 else:
1602 1601 limit = sys.maxint
1603 1602 count = 0
1604 1603
1605 1604 if opts['copies'] and opts['rev']:
1606 1605 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1607 1606 else:
1608 1607 endrev = repo.changelog.count()
1609 1608 rcache = {}
1610 1609 ncache = {}
1611 1610 dcache = []
1612 1611 def getrenamed(fn, rev, man):
1613 1612 '''looks up all renames for a file (up to endrev) the first
1614 1613 time the file is given. It indexes on the changerev and only
1615 1614 parses the manifest if linkrev != changerev.
1616 1615 Returns rename info for fn at changerev rev.'''
1617 1616 if fn not in rcache:
1618 1617 rcache[fn] = {}
1619 1618 ncache[fn] = {}
1620 1619 fl = repo.file(fn)
1621 1620 for i in xrange(fl.count()):
1622 1621 node = fl.node(i)
1623 1622 lr = fl.linkrev(node)
1624 1623 renamed = fl.renamed(node)
1625 1624 rcache[fn][lr] = renamed
1626 1625 if renamed:
1627 1626 ncache[fn][node] = renamed
1628 1627 if lr >= endrev:
1629 1628 break
1630 1629 if rev in rcache[fn]:
1631 1630 return rcache[fn][rev]
1632 1631 mr = repo.manifest.rev(man)
1633 1632 if repo.manifest.parentrevs(mr) != (mr - 1, nullrev):
1634 1633 return ncache[fn].get(repo.manifest.find(man, fn)[0])
1635 1634 if not dcache or dcache[0] != man:
1636 1635 dcache[:] = [man, repo.manifest.readdelta(man)]
1637 1636 if fn in dcache[1]:
1638 1637 return ncache[fn].get(dcache[1][fn])
1639 1638 return None
1640 1639
1641 1640 df = False
1642 1641 if opts["date"]:
1643 1642 df = util.matchdate(opts["date"])
1644 1643
1645 1644 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1646 1645 for st, rev, fns in changeiter:
1647 1646 if st == 'add':
1648 1647 changenode = repo.changelog.node(rev)
1649 1648 parents = [p for p in repo.changelog.parentrevs(rev)
1650 1649 if p != nullrev]
1651 1650 if opts['no_merges'] and len(parents) == 2:
1652 1651 continue
1653 1652 if opts['only_merges'] and len(parents) != 2:
1654 1653 continue
1655 1654
1656 1655 if df:
1657 1656 changes = get(rev)
1658 1657 if not df(changes[2][0]):
1659 1658 continue
1660 1659
1661 1660 if opts['keyword']:
1662 1661 changes = get(rev)
1663 1662 miss = 0
1664 1663 for k in [kw.lower() for kw in opts['keyword']]:
1665 1664 if not (k in changes[1].lower() or
1666 1665 k in changes[4].lower() or
1667 1666 k in " ".join(changes[3]).lower()):
1668 1667 miss = 1
1669 1668 break
1670 1669 if miss:
1671 1670 continue
1672 1671
1673 1672 copies = []
1674 1673 if opts.get('copies') and rev:
1675 1674 mf = get(rev)[0]
1676 1675 for fn in get(rev)[3]:
1677 1676 rename = getrenamed(fn, rev, mf)
1678 1677 if rename:
1679 1678 copies.append((fn, rename[0]))
1680 1679 displayer.show(rev, changenode, copies=copies)
1681 1680 elif st == 'iter':
1682 1681 if count == limit: break
1683 1682 if displayer.flush(rev):
1684 1683 count += 1
1685 1684
1686 1685 def manifest(ui, repo, node=None, rev=None):
1687 1686 """output the current or given revision of the project manifest
1688 1687
1689 1688 Print a list of version controlled files for the given revision.
1690 1689 If no revision is given, the parent of the working directory is used,
1691 1690 or tip if no revision is checked out.
1692 1691
1693 1692 The manifest is the list of files being version controlled. If no revision
1694 1693 is given then the first parent of the working directory is used.
1695 1694
1696 1695 With -v flag, print file permissions, symlink and executable bits. With
1697 1696 --debug flag, print file revision hashes.
1698 1697 """
1699 1698
1700 1699 if rev and node:
1701 1700 raise util.Abort(_("please specify just one revision"))
1702 1701
1703 1702 if not node:
1704 1703 node = rev
1705 1704
1706 1705 m = repo.changectx(node).manifest()
1707 1706 files = m.keys()
1708 1707 files.sort()
1709 1708
1710 1709 for f in files:
1711 1710 if ui.debugflag:
1712 1711 ui.write("%40s " % hex(m[f]))
1713 1712 if ui.verbose:
1714 1713 type = m.execf(f) and "*" or m.linkf(f) and "@" or " "
1715 1714 perm = m.execf(f) and "755" or "644"
1716 1715 ui.write("%3s %1s " % (perm, type))
1717 1716 ui.write("%s\n" % f)
1718 1717
1719 1718 def merge(ui, repo, node=None, force=None, rev=None):
1720 1719 """merge working directory with another revision
1721 1720
1722 1721 Merge the contents of the current working directory and the
1723 1722 requested revision. Files that changed between either parent are
1724 1723 marked as changed for the next commit and a commit must be
1725 1724 performed before any further updates are allowed.
1726 1725
1727 1726 If no revision is specified, the working directory's parent is a
1728 1727 head revision, and the repository contains exactly one other head,
1729 1728 the other head is merged with by default. Otherwise, an explicit
1730 1729 revision to merge with must be provided.
1731 1730 """
1732 1731
1733 1732 if rev and node:
1734 1733 raise util.Abort(_("please specify just one revision"))
1735 1734 if not node:
1736 1735 node = rev
1737 1736
1738 1737 if not node:
1739 1738 heads = repo.heads()
1740 1739 if len(heads) > 2:
1741 1740 raise util.Abort(_('repo has %d heads - '
1742 1741 'please merge with an explicit rev') %
1743 1742 len(heads))
1744 1743 parent = repo.dirstate.parents()[0]
1745 1744 if len(heads) == 1:
1746 1745 msg = _('there is nothing to merge')
1747 1746 if parent != repo.lookup(repo.workingctx().branch()):
1748 1747 msg = _('%s - use "hg update" instead') % msg
1749 1748 raise util.Abort(msg)
1750 1749
1751 1750 if parent not in heads:
1752 1751 raise util.Abort(_('working dir not at a head rev - '
1753 1752 'use "hg update" or merge with an explicit rev'))
1754 1753 node = parent == heads[0] and heads[-1] or heads[0]
1755 1754 return hg.merge(repo, node, force=force)
1756 1755
1757 1756 def outgoing(ui, repo, dest=None, **opts):
1758 1757 """show changesets not found in destination
1759 1758
1760 1759 Show changesets not found in the specified destination repository or
1761 1760 the default push location. These are the changesets that would be pushed
1762 1761 if a push was requested.
1763 1762
1764 1763 See pull for valid destination format details.
1765 1764 """
1766 1765 dest, revs, checkout = hg.parseurl(
1767 1766 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
1768 1767 cmdutil.setremoteconfig(ui, opts)
1769 1768 if revs:
1770 1769 revs = [repo.lookup(rev) for rev in revs]
1771 1770
1772 1771 other = hg.repository(ui, dest)
1773 1772 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
1774 1773 o = repo.findoutgoing(other, force=opts['force'])
1775 1774 if not o:
1776 1775 ui.status(_("no changes found\n"))
1777 1776 return 1
1778 1777 o = repo.changelog.nodesbetween(o, revs)[0]
1779 1778 if opts['newest_first']:
1780 1779 o.reverse()
1781 1780 displayer = cmdutil.show_changeset(ui, repo, opts)
1782 1781 for n in o:
1783 1782 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1784 1783 if opts['no_merges'] and len(parents) == 2:
1785 1784 continue
1786 1785 displayer.show(changenode=n)
1787 1786
1788 1787 def parents(ui, repo, file_=None, **opts):
1789 1788 """show the parents of the working dir or revision
1790 1789
1791 1790 Print the working directory's parent revisions. If a
1792 1791 revision is given via --rev, the parent of that revision
1793 1792 will be printed. If a file argument is given, revision in
1794 1793 which the file was last changed (before the working directory
1795 1794 revision or the argument to --rev if given) is printed.
1796 1795 """
1797 1796 rev = opts.get('rev')
1798 1797 if rev:
1799 1798 ctx = repo.changectx(rev)
1800 1799 else:
1801 1800 ctx = repo.workingctx()
1802 1801
1803 1802 if file_:
1804 1803 files, match, anypats = cmdutil.matchpats(repo, (file_,), opts)
1805 1804 if anypats or len(files) != 1:
1806 1805 raise util.Abort(_('can only specify an explicit file name'))
1807 1806 file_ = files[0]
1808 1807 filenodes = []
1809 1808 for cp in ctx.parents():
1810 1809 if not cp:
1811 1810 continue
1812 1811 try:
1813 1812 filenodes.append(cp.filenode(file_))
1814 1813 except revlog.LookupError:
1815 1814 pass
1816 1815 if not filenodes:
1817 1816 raise util.Abort(_("'%s' not found in manifest!") % file_)
1818 1817 fl = repo.file(file_)
1819 1818 p = [repo.lookup(fl.linkrev(fn)) for fn in filenodes]
1820 1819 else:
1821 1820 p = [cp.node() for cp in ctx.parents()]
1822 1821
1823 1822 displayer = cmdutil.show_changeset(ui, repo, opts)
1824 1823 for n in p:
1825 1824 if n != nullid:
1826 1825 displayer.show(changenode=n)
1827 1826
1828 1827 def paths(ui, repo, search=None):
1829 1828 """show definition of symbolic path names
1830 1829
1831 1830 Show definition of symbolic path name NAME. If no name is given, show
1832 1831 definition of available names.
1833 1832
1834 1833 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1835 1834 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1836 1835 """
1837 1836 if search:
1838 1837 for name, path in ui.configitems("paths"):
1839 1838 if name == search:
1840 1839 ui.write("%s\n" % path)
1841 1840 return
1842 1841 ui.warn(_("not found!\n"))
1843 1842 return 1
1844 1843 else:
1845 1844 for name, path in ui.configitems("paths"):
1846 1845 ui.write("%s = %s\n" % (name, path))
1847 1846
1848 1847 def postincoming(ui, repo, modheads, optupdate, checkout):
1849 1848 if modheads == 0:
1850 1849 return
1851 1850 if optupdate:
1852 1851 if modheads <= 1 or checkout:
1853 1852 return hg.update(repo, checkout)
1854 1853 else:
1855 1854 ui.status(_("not updating, since new heads added\n"))
1856 1855 if modheads > 1:
1857 1856 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
1858 1857 else:
1859 1858 ui.status(_("(run 'hg update' to get a working copy)\n"))
1860 1859
1861 1860 def pull(ui, repo, source="default", **opts):
1862 1861 """pull changes from the specified source
1863 1862
1864 1863 Pull changes from a remote repository to a local one.
1865 1864
1866 1865 This finds all changes from the repository at the specified path
1867 1866 or URL and adds them to the local repository. By default, this
1868 1867 does not update the copy of the project in the working directory.
1869 1868
1870 1869 Valid URLs are of the form:
1871 1870
1872 1871 local/filesystem/path (or file://local/filesystem/path)
1873 1872 http://[user@]host[:port]/[path]
1874 1873 https://[user@]host[:port]/[path]
1875 1874 ssh://[user@]host[:port]/[path]
1876 1875 static-http://host[:port]/[path]
1877 1876
1878 1877 Paths in the local filesystem can either point to Mercurial
1879 1878 repositories or to bundle files (as created by 'hg bundle' or
1880 1879 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
1881 1880 allows access to a Mercurial repository where you simply use a web
1882 1881 server to publish the .hg directory as static content.
1883 1882
1884 1883 An optional identifier after # indicates a particular branch, tag,
1885 1884 or changeset to pull.
1886 1885
1887 1886 Some notes about using SSH with Mercurial:
1888 1887 - SSH requires an accessible shell account on the destination machine
1889 1888 and a copy of hg in the remote path or specified with as remotecmd.
1890 1889 - path is relative to the remote user's home directory by default.
1891 1890 Use an extra slash at the start of a path to specify an absolute path:
1892 1891 ssh://example.com//tmp/repository
1893 1892 - Mercurial doesn't use its own compression via SSH; the right thing
1894 1893 to do is to configure it in your ~/.ssh/config, e.g.:
1895 1894 Host *.mylocalnetwork.example.com
1896 1895 Compression no
1897 1896 Host *
1898 1897 Compression yes
1899 1898 Alternatively specify "ssh -C" as your ssh command in your hgrc or
1900 1899 with the --ssh command line option.
1901 1900 """
1902 1901 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts['rev'])
1903 1902 cmdutil.setremoteconfig(ui, opts)
1904 1903
1905 1904 other = hg.repository(ui, source)
1906 1905 ui.status(_('pulling from %s\n') % util.hidepassword(source))
1907 1906 if revs:
1908 1907 try:
1909 1908 revs = [other.lookup(rev) for rev in revs]
1910 1909 except repo.NoCapability:
1911 1910 error = _("Other repository doesn't support revision lookup, "
1912 1911 "so a rev cannot be specified.")
1913 1912 raise util.Abort(error)
1914 1913
1915 1914 modheads = repo.pull(other, heads=revs, force=opts['force'])
1916 1915 return postincoming(ui, repo, modheads, opts['update'], checkout)
1917 1916
1918 1917 def push(ui, repo, dest=None, **opts):
1919 1918 """push changes to the specified destination
1920 1919
1921 1920 Push changes from the local repository to the given destination.
1922 1921
1923 1922 This is the symmetrical operation for pull. It helps to move
1924 1923 changes from the current repository to a different one. If the
1925 1924 destination is local this is identical to a pull in that directory
1926 1925 from the current one.
1927 1926
1928 1927 By default, push will refuse to run if it detects the result would
1929 1928 increase the number of remote heads. This generally indicates the
1930 1929 the client has forgotten to sync and merge before pushing.
1931 1930
1932 1931 Valid URLs are of the form:
1933 1932
1934 1933 local/filesystem/path (or file://local/filesystem/path)
1935 1934 ssh://[user@]host[:port]/[path]
1936 1935 http://[user@]host[:port]/[path]
1937 1936 https://[user@]host[:port]/[path]
1938 1937
1939 1938 An optional identifier after # indicates a particular branch, tag,
1940 1939 or changeset to push.
1941 1940
1942 1941 Look at the help text for the pull command for important details
1943 1942 about ssh:// URLs.
1944 1943
1945 1944 Pushing to http:// and https:// URLs is only possible, if this
1946 1945 feature is explicitly enabled on the remote Mercurial server.
1947 1946 """
1948 1947 dest, revs, checkout = hg.parseurl(
1949 1948 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
1950 1949 cmdutil.setremoteconfig(ui, opts)
1951 1950
1952 1951 other = hg.repository(ui, dest)
1953 1952 ui.status('pushing to %s\n' % util.hidepassword(dest))
1954 1953 if revs:
1955 1954 revs = [repo.lookup(rev) for rev in revs]
1956 1955 r = repo.push(other, opts['force'], revs=revs)
1957 1956 return r == 0
1958 1957
1959 1958 def rawcommit(ui, repo, *pats, **opts):
1960 1959 """raw commit interface (DEPRECATED)
1961 1960
1962 1961 (DEPRECATED)
1963 1962 Lowlevel commit, for use in helper scripts.
1964 1963
1965 1964 This command is not intended to be used by normal users, as it is
1966 1965 primarily useful for importing from other SCMs.
1967 1966
1968 1967 This command is now deprecated and will be removed in a future
1969 1968 release, please use debugsetparents and commit instead.
1970 1969 """
1971 1970
1972 1971 ui.warn(_("(the rawcommit command is deprecated)\n"))
1973 1972
1974 1973 message = cmdutil.logmessage(opts)
1975 1974
1976 1975 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
1977 1976 if opts['files']:
1978 1977 files += open(opts['files']).read().splitlines()
1979 1978
1980 1979 parents = [repo.lookup(p) for p in opts['parent']]
1981 1980
1982 1981 try:
1983 1982 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
1984 1983 except ValueError, inst:
1985 1984 raise util.Abort(str(inst))
1986 1985
1987 1986 def recover(ui, repo):
1988 1987 """roll back an interrupted transaction
1989 1988
1990 1989 Recover from an interrupted commit or pull.
1991 1990
1992 1991 This command tries to fix the repository status after an interrupted
1993 1992 operation. It should only be necessary when Mercurial suggests it.
1994 1993 """
1995 1994 if repo.recover():
1996 1995 return hg.verify(repo)
1997 1996 return 1
1998 1997
1999 1998 def remove(ui, repo, *pats, **opts):
2000 1999 """remove the specified files on the next commit
2001 2000
2002 2001 Schedule the indicated files for removal from the repository.
2003 2002
2004 2003 This only removes files from the current branch, not from the
2005 2004 entire project history. If the files still exist in the working
2006 2005 directory, they will be deleted from it. If invoked with --after,
2007 2006 files are marked as removed, but not actually unlinked unless --force
2008 2007 is also given. Without exact file names, --after will only mark
2009 2008 files as removed if they are no longer in the working directory.
2010 2009
2011 2010 This command schedules the files to be removed at the next commit.
2012 2011 To undo a remove before that, see hg revert.
2013 2012
2014 2013 Modified files and added files are not removed by default. To
2015 2014 remove them, use the -f/--force option.
2016 2015 """
2017 2016 if not opts['after'] and not pats:
2018 2017 raise util.Abort(_('no files specified'))
2019 2018 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2020 2019 exact = dict.fromkeys(files)
2021 2020 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2022 2021 modified, added, removed, deleted, unknown = mardu
2023 2022 remove, forget = [], []
2024 2023 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2025 2024 reason = None
2026 2025 if abs in modified and not opts['force']:
2027 2026 reason = _('is modified (use -f to force removal)')
2028 2027 elif abs in added:
2029 2028 if opts['force']:
2030 2029 forget.append(abs)
2031 2030 continue
2032 2031 reason = _('has been marked for add (use -f to force removal)')
2033 2032 elif abs not in repo.dirstate:
2034 2033 reason = _('is not managed')
2035 2034 elif opts['after'] and not exact and abs not in deleted:
2036 2035 continue
2037 2036 elif abs in removed:
2038 2037 continue
2039 2038 if reason:
2040 2039 if exact:
2041 2040 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2042 2041 else:
2043 2042 if ui.verbose or not exact:
2044 2043 ui.status(_('removing %s\n') % rel)
2045 2044 remove.append(abs)
2046 2045 repo.forget(forget)
2047 2046 repo.remove(remove, unlink=opts['force'] or not opts['after'])
2048 2047
2049 2048 def rename(ui, repo, *pats, **opts):
2050 2049 """rename files; equivalent of copy + remove
2051 2050
2052 2051 Mark dest as copies of sources; mark sources for deletion. If
2053 2052 dest is a directory, copies are put in that directory. If dest is
2054 2053 a file, there can only be one source.
2055 2054
2056 2055 By default, this command copies the contents of files as they
2057 2056 stand in the working directory. If invoked with --after, the
2058 2057 operation is recorded, but no copying is performed.
2059 2058
2060 2059 This command takes effect in the next commit. To undo a rename
2061 2060 before that, see hg revert.
2062 2061 """
2063 2062 wlock = repo.wlock(False)
2064 2063 try:
2065 errs, copied = cmdutil.copy(ui, repo, pats, opts)
2066 names = []
2067 for abs, rel, exact in copied:
2068 if ui.verbose or not exact:
2069 ui.status(_('removing %s\n') % rel)
2070 names.append(abs)
2071 if not opts.get('dry_run'):
2072 repo.remove(names, True)
2073 return errs
2064 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2074 2065 finally:
2075 2066 del wlock
2076 2067
2077 2068 def revert(ui, repo, *pats, **opts):
2078 2069 """restore individual files or dirs to an earlier state
2079 2070
2080 2071 (use update -r to check out earlier revisions, revert does not
2081 2072 change the working dir parents)
2082 2073
2083 2074 With no revision specified, revert the named files or directories
2084 2075 to the contents they had in the parent of the working directory.
2085 2076 This restores the contents of the affected files to an unmodified
2086 2077 state and unschedules adds, removes, copies, and renames. If the
2087 2078 working directory has two parents, you must explicitly specify the
2088 2079 revision to revert to.
2089 2080
2090 2081 Using the -r option, revert the given files or directories to their
2091 2082 contents as of a specific revision. This can be helpful to "roll
2092 2083 back" some or all of an earlier change.
2093 2084
2094 2085 Revert modifies the working directory. It does not commit any
2095 2086 changes, or change the parent of the working directory. If you
2096 2087 revert to a revision other than the parent of the working
2097 2088 directory, the reverted files will thus appear modified
2098 2089 afterwards.
2099 2090
2100 2091 If a file has been deleted, it is restored. If the executable
2101 2092 mode of a file was changed, it is reset.
2102 2093
2103 2094 If names are given, all files matching the names are reverted.
2104 2095
2105 2096 If no arguments are given, no files are reverted.
2106 2097
2107 2098 Modified files are saved with a .orig suffix before reverting.
2108 2099 To disable these backups, use --no-backup.
2109 2100 """
2110 2101
2111 2102 if opts["date"]:
2112 2103 if opts["rev"]:
2113 2104 raise util.Abort(_("you can't specify a revision and a date"))
2114 2105 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2115 2106
2116 2107 if not pats and not opts['all']:
2117 2108 raise util.Abort(_('no files or directories specified; '
2118 2109 'use --all to revert the whole repo'))
2119 2110
2120 2111 parent, p2 = repo.dirstate.parents()
2121 2112 if not opts['rev'] and p2 != nullid:
2122 2113 raise util.Abort(_('uncommitted merge - please provide a '
2123 2114 'specific revision'))
2124 2115 ctx = repo.changectx(opts['rev'])
2125 2116 node = ctx.node()
2126 2117 mf = ctx.manifest()
2127 2118 if node == parent:
2128 2119 pmf = mf
2129 2120 else:
2130 2121 pmf = None
2131 2122
2132 2123 # need all matching names in dirstate and manifest of target rev,
2133 2124 # so have to walk both. do not print errors if files exist in one
2134 2125 # but not other.
2135 2126
2136 2127 names = {}
2137 2128 target_only = {}
2138 2129
2139 2130 wlock = repo.wlock()
2140 2131 try:
2141 2132 # walk dirstate.
2142 2133 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2143 2134 badmatch=mf.has_key):
2144 2135 names[abs] = (rel, exact)
2145 2136 if src == 'b':
2146 2137 target_only[abs] = True
2147 2138
2148 2139 # walk target manifest.
2149 2140
2150 2141 def badmatch(path):
2151 2142 if path in names:
2152 2143 return True
2153 2144 path_ = path + '/'
2154 2145 for f in names:
2155 2146 if f.startswith(path_):
2156 2147 return True
2157 2148 return False
2158 2149
2159 2150 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2160 2151 badmatch=badmatch):
2161 2152 if abs in names or src == 'b':
2162 2153 continue
2163 2154 names[abs] = (rel, exact)
2164 2155 target_only[abs] = True
2165 2156
2166 2157 changes = repo.status(match=names.has_key)[:5]
2167 2158 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2168 2159
2169 2160 # if f is a rename, also revert the source
2170 2161 cwd = repo.getcwd()
2171 2162 for f in added:
2172 2163 src = repo.dirstate.copied(f)
2173 2164 if src and src not in names and repo.dirstate[src] == 'r':
2174 2165 removed[src] = None
2175 2166 names[src] = (repo.pathto(src, cwd), True)
2176 2167
2177 2168 revert = ([], _('reverting %s\n'))
2178 2169 add = ([], _('adding %s\n'))
2179 2170 remove = ([], _('removing %s\n'))
2180 2171 forget = ([], _('forgetting %s\n'))
2181 2172 undelete = ([], _('undeleting %s\n'))
2182 2173 update = {}
2183 2174
2184 2175 disptable = (
2185 2176 # dispatch table:
2186 2177 # file state
2187 2178 # action if in target manifest
2188 2179 # action if not in target manifest
2189 2180 # make backup if in target manifest
2190 2181 # make backup if not in target manifest
2191 2182 (modified, revert, remove, True, True),
2192 2183 (added, revert, forget, True, False),
2193 2184 (removed, undelete, None, False, False),
2194 2185 (deleted, revert, remove, False, False),
2195 2186 (unknown, add, None, True, False),
2196 2187 (target_only, add, None, False, False),
2197 2188 )
2198 2189
2199 2190 entries = names.items()
2200 2191 entries.sort()
2201 2192
2202 2193 for abs, (rel, exact) in entries:
2203 2194 mfentry = mf.get(abs)
2204 2195 target = repo.wjoin(abs)
2205 2196 def handle(xlist, dobackup):
2206 2197 xlist[0].append(abs)
2207 2198 update[abs] = 1
2208 2199 if dobackup and not opts['no_backup'] and util.lexists(target):
2209 2200 bakname = "%s.orig" % rel
2210 2201 ui.note(_('saving current version of %s as %s\n') %
2211 2202 (rel, bakname))
2212 2203 if not opts.get('dry_run'):
2213 2204 util.copyfile(target, bakname)
2214 2205 if ui.verbose or not exact:
2215 2206 ui.status(xlist[1] % rel)
2216 2207 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2217 2208 if abs not in table: continue
2218 2209 # file has changed in dirstate
2219 2210 if mfentry:
2220 2211 handle(hitlist, backuphit)
2221 2212 elif misslist is not None:
2222 2213 handle(misslist, backupmiss)
2223 2214 else:
2224 2215 if exact: ui.warn(_('file not managed: %s\n') % rel)
2225 2216 break
2226 2217 else:
2227 2218 # file has not changed in dirstate
2228 2219 if node == parent:
2229 2220 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2230 2221 continue
2231 2222 if pmf is None:
2232 2223 # only need parent manifest in this unlikely case,
2233 2224 # so do not read by default
2234 2225 pmf = repo.changectx(parent).manifest()
2235 2226 if abs in pmf:
2236 2227 if mfentry:
2237 2228 # if version of file is same in parent and target
2238 2229 # manifests, do nothing
2239 2230 if pmf[abs] != mfentry:
2240 2231 handle(revert, False)
2241 2232 else:
2242 2233 handle(remove, False)
2243 2234
2244 2235 if not opts.get('dry_run'):
2245 2236 for f in forget[0]:
2246 2237 repo.dirstate.forget(f)
2247 2238 r = hg.revert(repo, node, update.has_key)
2248 2239 for f in add[0]:
2249 2240 repo.dirstate.add(f)
2250 2241 for f in undelete[0]:
2251 2242 repo.dirstate.normal(f)
2252 2243 for f in remove[0]:
2253 2244 repo.dirstate.remove(f)
2254 2245 return r
2255 2246 finally:
2256 2247 del wlock
2257 2248
2258 2249 def rollback(ui, repo):
2259 2250 """roll back the last transaction
2260 2251
2261 2252 This command should be used with care. There is only one level of
2262 2253 rollback, and there is no way to undo a rollback. It will also
2263 2254 restore the dirstate at the time of the last transaction, losing
2264 2255 any dirstate changes since that time.
2265 2256
2266 2257 Transactions are used to encapsulate the effects of all commands
2267 2258 that create new changesets or propagate existing changesets into a
2268 2259 repository. For example, the following commands are transactional,
2269 2260 and their effects can be rolled back:
2270 2261
2271 2262 commit
2272 2263 import
2273 2264 pull
2274 2265 push (with this repository as destination)
2275 2266 unbundle
2276 2267
2277 2268 This command is not intended for use on public repositories. Once
2278 2269 changes are visible for pull by other users, rolling a transaction
2279 2270 back locally is ineffective (someone else may already have pulled
2280 2271 the changes). Furthermore, a race is possible with readers of the
2281 2272 repository; for example an in-progress pull from the repository
2282 2273 may fail if a rollback is performed.
2283 2274 """
2284 2275 repo.rollback()
2285 2276
2286 2277 def root(ui, repo):
2287 2278 """print the root (top) of the current working dir
2288 2279
2289 2280 Print the root directory of the current repository.
2290 2281 """
2291 2282 ui.write(repo.root + "\n")
2292 2283
2293 2284 def serve(ui, repo, **opts):
2294 2285 """export the repository via HTTP
2295 2286
2296 2287 Start a local HTTP repository browser and pull server.
2297 2288
2298 2289 By default, the server logs accesses to stdout and errors to
2299 2290 stderr. Use the "-A" and "-E" options to log to files.
2300 2291 """
2301 2292
2302 2293 if opts["stdio"]:
2303 2294 if repo is None:
2304 2295 raise hg.RepoError(_("There is no Mercurial repository here"
2305 2296 " (.hg not found)"))
2306 2297 s = sshserver.sshserver(ui, repo)
2307 2298 s.serve_forever()
2308 2299
2309 2300 parentui = ui.parentui or ui
2310 2301 optlist = ("name templates style address port ipv6"
2311 2302 " accesslog errorlog webdir_conf certificate")
2312 2303 for o in optlist.split():
2313 2304 if opts[o]:
2314 2305 parentui.setconfig("web", o, str(opts[o]))
2315 2306 if (repo is not None) and (repo.ui != parentui):
2316 2307 repo.ui.setconfig("web", o, str(opts[o]))
2317 2308
2318 2309 if repo is None and not ui.config("web", "webdir_conf"):
2319 2310 raise hg.RepoError(_("There is no Mercurial repository here"
2320 2311 " (.hg not found)"))
2321 2312
2322 2313 class service:
2323 2314 def init(self):
2324 2315 util.set_signal_handler()
2325 2316 try:
2326 2317 self.httpd = hgweb.server.create_server(parentui, repo)
2327 2318 except socket.error, inst:
2328 2319 raise util.Abort(_('cannot start server: ') + inst.args[1])
2329 2320
2330 2321 if not ui.verbose: return
2331 2322
2332 2323 if self.httpd.port != 80:
2333 2324 ui.status(_('listening at http://%s:%d/\n') %
2334 2325 (self.httpd.addr, self.httpd.port))
2335 2326 else:
2336 2327 ui.status(_('listening at http://%s/\n') % self.httpd.addr)
2337 2328
2338 2329 def run(self):
2339 2330 self.httpd.serve_forever()
2340 2331
2341 2332 service = service()
2342 2333
2343 2334 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2344 2335
2345 2336 def status(ui, repo, *pats, **opts):
2346 2337 """show changed files in the working directory
2347 2338
2348 2339 Show status of files in the repository. If names are given, only
2349 2340 files that match are shown. Files that are clean or ignored, are
2350 2341 not listed unless -c (clean), -i (ignored) or -A is given.
2351 2342
2352 2343 NOTE: status may appear to disagree with diff if permissions have
2353 2344 changed or a merge has occurred. The standard diff format does not
2354 2345 report permission changes and diff only reports changes relative
2355 2346 to one merge parent.
2356 2347
2357 2348 If one revision is given, it is used as the base revision.
2358 2349 If two revisions are given, the difference between them is shown.
2359 2350
2360 2351 The codes used to show the status of files are:
2361 2352 M = modified
2362 2353 A = added
2363 2354 R = removed
2364 2355 C = clean
2365 2356 ! = deleted, but still tracked
2366 2357 ? = not tracked
2367 2358 I = ignored (not shown by default)
2368 2359 = the previous added file was copied from here
2369 2360 """
2370 2361
2371 2362 all = opts['all']
2372 2363 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2373 2364
2374 2365 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2375 2366 cwd = (pats and repo.getcwd()) or ''
2376 2367 modified, added, removed, deleted, unknown, ignored, clean = [
2377 2368 n for n in repo.status(node1=node1, node2=node2, files=files,
2378 2369 match=matchfn,
2379 2370 list_ignored=all or opts['ignored'],
2380 2371 list_clean=all or opts['clean'])]
2381 2372
2382 2373 changetypes = (('modified', 'M', modified),
2383 2374 ('added', 'A', added),
2384 2375 ('removed', 'R', removed),
2385 2376 ('deleted', '!', deleted),
2386 2377 ('unknown', '?', unknown),
2387 2378 ('ignored', 'I', ignored))
2388 2379
2389 2380 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2390 2381
2391 2382 end = opts['print0'] and '\0' or '\n'
2392 2383
2393 2384 for opt, char, changes in ([ct for ct in explicit_changetypes
2394 2385 if all or opts[ct[0]]]
2395 2386 or changetypes):
2396 2387 if opts['no_status']:
2397 2388 format = "%%s%s" % end
2398 2389 else:
2399 2390 format = "%s %%s%s" % (char, end)
2400 2391
2401 2392 for f in changes:
2402 2393 ui.write(format % repo.pathto(f, cwd))
2403 2394 if ((all or opts.get('copies')) and not opts.get('no_status')):
2404 2395 copied = repo.dirstate.copied(f)
2405 2396 if copied:
2406 2397 ui.write(' %s%s' % (repo.pathto(copied, cwd), end))
2407 2398
2408 2399 def tag(ui, repo, name, rev_=None, **opts):
2409 2400 """add a tag for the current or given revision
2410 2401
2411 2402 Name a particular revision using <name>.
2412 2403
2413 2404 Tags are used to name particular revisions of the repository and are
2414 2405 very useful to compare different revision, to go back to significant
2415 2406 earlier versions or to mark branch points as releases, etc.
2416 2407
2417 2408 If no revision is given, the parent of the working directory is used,
2418 2409 or tip if no revision is checked out.
2419 2410
2420 2411 To facilitate version control, distribution, and merging of tags,
2421 2412 they are stored as a file named ".hgtags" which is managed
2422 2413 similarly to other project files and can be hand-edited if
2423 2414 necessary. The file '.hg/localtags' is used for local tags (not
2424 2415 shared among repositories).
2425 2416 """
2426 2417 if name in ['tip', '.', 'null']:
2427 2418 raise util.Abort(_("the name '%s' is reserved") % name)
2428 2419 if rev_ is not None:
2429 2420 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2430 2421 "please use 'hg tag [-r REV] NAME' instead\n"))
2431 2422 if opts['rev']:
2432 2423 raise util.Abort(_("use only one form to specify the revision"))
2433 2424 if opts['rev'] and opts['remove']:
2434 2425 raise util.Abort(_("--rev and --remove are incompatible"))
2435 2426 if opts['rev']:
2436 2427 rev_ = opts['rev']
2437 2428 message = opts['message']
2438 2429 if opts['remove']:
2439 2430 if not name in repo.tags():
2440 2431 raise util.Abort(_('tag %s does not exist') % name)
2441 2432 rev_ = nullid
2442 2433 if not message:
2443 2434 message = _('Removed tag %s') % name
2444 2435 elif name in repo.tags() and not opts['force']:
2445 2436 raise util.Abort(_('a tag named %s already exists (use -f to force)')
2446 2437 % name)
2447 2438 if not rev_ and repo.dirstate.parents()[1] != nullid:
2448 2439 raise util.Abort(_('uncommitted merge - please provide a '
2449 2440 'specific revision'))
2450 2441 r = repo.changectx(rev_).node()
2451 2442
2452 2443 if not message:
2453 2444 message = _('Added tag %s for changeset %s') % (name, short(r))
2454 2445
2455 2446 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2456 2447
2457 2448 def tags(ui, repo):
2458 2449 """list repository tags
2459 2450
2460 2451 List the repository tags.
2461 2452
2462 2453 This lists both regular and local tags.
2463 2454 """
2464 2455
2465 2456 l = repo.tagslist()
2466 2457 l.reverse()
2467 2458 hexfunc = ui.debugflag and hex or short
2468 2459 for t, n in l:
2469 2460 try:
2470 2461 hn = hexfunc(n)
2471 2462 r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
2472 2463 except revlog.LookupError:
2473 2464 r = " ?:%s" % hn
2474 2465 if ui.quiet:
2475 2466 ui.write("%s\n" % t)
2476 2467 else:
2477 2468 spaces = " " * (30 - util.locallen(t))
2478 2469 ui.write("%s%s %s\n" % (t, spaces, r))
2479 2470
2480 2471 def tip(ui, repo, **opts):
2481 2472 """show the tip revision
2482 2473
2483 2474 Show the tip revision.
2484 2475 """
2485 2476 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2486 2477
2487 2478 def unbundle(ui, repo, fname1, *fnames, **opts):
2488 2479 """apply one or more changegroup files
2489 2480
2490 2481 Apply one or more compressed changegroup files generated by the
2491 2482 bundle command.
2492 2483 """
2493 2484 fnames = (fname1,) + fnames
2494 2485 for fname in fnames:
2495 2486 if os.path.exists(fname):
2496 2487 f = open(fname, "rb")
2497 2488 else:
2498 2489 f = urllib.urlopen(fname)
2499 2490 gen = changegroup.readbundle(f, fname)
2500 2491 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2501 2492
2502 2493 return postincoming(ui, repo, modheads, opts['update'], None)
2503 2494
2504 2495 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2505 2496 """update working directory
2506 2497
2507 2498 Update the working directory to the specified revision, or the
2508 2499 tip of the current branch if none is specified.
2509 2500
2510 2501 If there are no outstanding changes in the working directory and
2511 2502 there is a linear relationship between the current version and the
2512 2503 requested version, the result is the requested version.
2513 2504
2514 2505 To merge the working directory with another revision, use the
2515 2506 merge command.
2516 2507
2517 2508 By default, update will refuse to run if doing so would require
2518 2509 discarding local changes.
2519 2510 """
2520 2511 if rev and node:
2521 2512 raise util.Abort(_("please specify just one revision"))
2522 2513
2523 2514 if not rev:
2524 2515 rev = node
2525 2516
2526 2517 if date:
2527 2518 if rev:
2528 2519 raise util.Abort(_("you can't specify a revision and a date"))
2529 2520 rev = cmdutil.finddate(ui, repo, date)
2530 2521
2531 2522 if clean:
2532 2523 return hg.clean(repo, rev)
2533 2524 else:
2534 2525 return hg.update(repo, rev)
2535 2526
2536 2527 def verify(ui, repo):
2537 2528 """verify the integrity of the repository
2538 2529
2539 2530 Verify the integrity of the current repository.
2540 2531
2541 2532 This will perform an extensive check of the repository's
2542 2533 integrity, validating the hashes and checksums of each entry in
2543 2534 the changelog, manifest, and tracked files, as well as the
2544 2535 integrity of their crosslinks and indices.
2545 2536 """
2546 2537 return hg.verify(repo)
2547 2538
2548 2539 def version_(ui):
2549 2540 """output version and copyright information"""
2550 2541 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2551 2542 % version.get_version())
2552 2543 ui.status(_(
2553 2544 "\nCopyright (C) 2005-2007 Matt Mackall <mpm@selenic.com> and others\n"
2554 2545 "This is free software; see the source for copying conditions. "
2555 2546 "There is NO\nwarranty; "
2556 2547 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2557 2548 ))
2558 2549
2559 2550 # Command options and aliases are listed here, alphabetically
2560 2551
2561 2552 globalopts = [
2562 2553 ('R', 'repository', '',
2563 2554 _('repository root directory or symbolic path name')),
2564 2555 ('', 'cwd', '', _('change working directory')),
2565 2556 ('y', 'noninteractive', None,
2566 2557 _('do not prompt, assume \'yes\' for any required answers')),
2567 2558 ('q', 'quiet', None, _('suppress output')),
2568 2559 ('v', 'verbose', None, _('enable additional output')),
2569 2560 ('', 'config', [], _('set/override config option')),
2570 2561 ('', 'debug', None, _('enable debugging output')),
2571 2562 ('', 'debugger', None, _('start debugger')),
2572 2563 ('', 'encoding', util._encoding, _('set the charset encoding')),
2573 2564 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2574 2565 ('', 'lsprof', None, _('print improved command execution profile')),
2575 2566 ('', 'traceback', None, _('print traceback on exception')),
2576 2567 ('', 'time', None, _('time how long the command takes')),
2577 2568 ('', 'profile', None, _('print command execution profile')),
2578 2569 ('', 'version', None, _('output version information and exit')),
2579 2570 ('h', 'help', None, _('display help and exit')),
2580 2571 ]
2581 2572
2582 2573 dryrunopts = [('n', 'dry-run', None,
2583 2574 _('do not perform actions, just print output'))]
2584 2575
2585 2576 remoteopts = [
2586 2577 ('e', 'ssh', '', _('specify ssh command to use')),
2587 2578 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2588 2579 ]
2589 2580
2590 2581 walkopts = [
2591 2582 ('I', 'include', [], _('include names matching the given patterns')),
2592 2583 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2593 2584 ]
2594 2585
2595 2586 commitopts = [
2596 2587 ('m', 'message', '', _('use <text> as commit message')),
2597 2588 ('l', 'logfile', '', _('read commit message from <file>')),
2598 2589 ]
2599 2590
2600 2591 commitopts2 = [
2601 2592 ('d', 'date', '', _('record datecode as commit date')),
2602 2593 ('u', 'user', '', _('record user as committer')),
2603 2594 ]
2604 2595
2605 2596 table = {
2606 2597 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2607 2598 "addremove":
2608 2599 (addremove,
2609 2600 [('s', 'similarity', '',
2610 2601 _('guess renamed files by similarity (0<=s<=100)')),
2611 2602 ] + walkopts + dryrunopts,
2612 2603 _('hg addremove [OPTION]... [FILE]...')),
2613 2604 "^annotate":
2614 2605 (annotate,
2615 2606 [('r', 'rev', '', _('annotate the specified revision')),
2616 2607 ('f', 'follow', None, _('follow file copies and renames')),
2617 2608 ('a', 'text', None, _('treat all files as text')),
2618 2609 ('u', 'user', None, _('list the author')),
2619 2610 ('d', 'date', None, _('list the date')),
2620 2611 ('n', 'number', None, _('list the revision number (default)')),
2621 2612 ('c', 'changeset', None, _('list the changeset')),
2622 2613 ('l', 'line-number', None,
2623 2614 _('show line number at the first appearance'))
2624 2615 ] + walkopts,
2625 2616 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
2626 2617 "archive":
2627 2618 (archive,
2628 2619 [('', 'no-decode', None, _('do not pass files through decoders')),
2629 2620 ('p', 'prefix', '', _('directory prefix for files in archive')),
2630 2621 ('r', 'rev', '', _('revision to distribute')),
2631 2622 ('t', 'type', '', _('type of distribution to create')),
2632 2623 ] + walkopts,
2633 2624 _('hg archive [OPTION]... DEST')),
2634 2625 "backout":
2635 2626 (backout,
2636 2627 [('', 'merge', None,
2637 2628 _('merge with old dirstate parent after backout')),
2638 2629 ('', 'parent', '', _('parent to choose when backing out merge')),
2639 2630 ('r', 'rev', '', _('revision to backout')),
2640 2631 ] + walkopts + commitopts + commitopts2,
2641 2632 _('hg backout [OPTION]... [-r] REV')),
2642 2633 "branch":
2643 2634 (branch,
2644 2635 [('f', 'force', None,
2645 2636 _('set branch name even if it shadows an existing branch'))],
2646 2637 _('hg branch [NAME]')),
2647 2638 "branches":
2648 2639 (branches,
2649 2640 [('a', 'active', False,
2650 2641 _('show only branches that have unmerged heads'))],
2651 2642 _('hg branches [-a]')),
2652 2643 "bundle":
2653 2644 (bundle,
2654 2645 [('f', 'force', None,
2655 2646 _('run even when remote repository is unrelated')),
2656 2647 ('r', 'rev', [],
2657 2648 _('a changeset you would like to bundle')),
2658 2649 ('', 'base', [],
2659 2650 _('a base changeset to specify instead of a destination')),
2660 2651 ] + remoteopts,
2661 2652 _('hg bundle [-f] [-r REV]... [--base REV]... FILE [DEST]')),
2662 2653 "cat":
2663 2654 (cat,
2664 2655 [('o', 'output', '', _('print output to file with formatted name')),
2665 2656 ('r', 'rev', '', _('print the given revision')),
2666 2657 ] + walkopts,
2667 2658 _('hg cat [OPTION]... FILE...')),
2668 2659 "^clone":
2669 2660 (clone,
2670 2661 [('U', 'noupdate', None, _('do not update the new working directory')),
2671 2662 ('r', 'rev', [],
2672 2663 _('a changeset you would like to have after cloning')),
2673 2664 ('', 'pull', None, _('use pull protocol to copy metadata')),
2674 2665 ('', 'uncompressed', None,
2675 2666 _('use uncompressed transfer (fast over LAN)')),
2676 2667 ] + remoteopts,
2677 2668 _('hg clone [OPTION]... SOURCE [DEST]')),
2678 2669 "^commit|ci":
2679 2670 (commit,
2680 2671 [('A', 'addremove', None,
2681 2672 _('mark new/missing files as added/removed before committing')),
2682 2673 ] + walkopts + commitopts + commitopts2,
2683 2674 _('hg commit [OPTION]... [FILE]...')),
2684 2675 "copy|cp":
2685 2676 (copy,
2686 2677 [('A', 'after', None, _('record a copy that has already occurred')),
2687 2678 ('f', 'force', None,
2688 2679 _('forcibly copy over an existing managed file')),
2689 2680 ] + walkopts + dryrunopts,
2690 2681 _('hg copy [OPTION]... [SOURCE]... DEST')),
2691 2682 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2692 2683 "debugcomplete":
2693 2684 (debugcomplete,
2694 2685 [('o', 'options', None, _('show the command options'))],
2695 2686 _('debugcomplete [-o] CMD')),
2696 2687 "debuginstall": (debuginstall, [], _('debuginstall')),
2697 2688 "debugrebuildstate":
2698 2689 (debugrebuildstate,
2699 2690 [('r', 'rev', '', _('revision to rebuild to'))],
2700 2691 _('debugrebuildstate [-r REV] [REV]')),
2701 2692 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2702 2693 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2703 2694 "debugstate": (debugstate, [], _('debugstate')),
2704 2695 "debugdate":
2705 2696 (debugdate,
2706 2697 [('e', 'extended', None, _('try extended date formats'))],
2707 2698 _('debugdate [-e] DATE [RANGE]')),
2708 2699 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2709 2700 "debugindex": (debugindex, [], _('debugindex FILE')),
2710 2701 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2711 2702 "debugrename":
2712 2703 (debugrename,
2713 2704 [('r', 'rev', '', _('revision to debug'))],
2714 2705 _('debugrename [-r REV] FILE')),
2715 2706 "debugwalk": (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')),
2716 2707 "^diff":
2717 2708 (diff,
2718 2709 [('r', 'rev', [], _('revision')),
2719 2710 ('a', 'text', None, _('treat all files as text')),
2720 2711 ('p', 'show-function', None,
2721 2712 _('show which function each change is in')),
2722 2713 ('g', 'git', None, _('use git extended diff format')),
2723 2714 ('', 'nodates', None, _("don't include dates in diff headers")),
2724 2715 ('w', 'ignore-all-space', None,
2725 2716 _('ignore white space when comparing lines')),
2726 2717 ('b', 'ignore-space-change', None,
2727 2718 _('ignore changes in the amount of white space')),
2728 2719 ('B', 'ignore-blank-lines', None,
2729 2720 _('ignore changes whose lines are all blank')),
2730 2721 ] + walkopts,
2731 2722 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
2732 2723 "^export":
2733 2724 (export,
2734 2725 [('o', 'output', '', _('print output to file with formatted name')),
2735 2726 ('a', 'text', None, _('treat all files as text')),
2736 2727 ('g', 'git', None, _('use git extended diff format')),
2737 2728 ('', 'nodates', None, _("don't include dates in diff headers")),
2738 2729 ('', 'switch-parent', None, _('diff against the second parent'))],
2739 2730 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
2740 2731 "grep":
2741 2732 (grep,
2742 2733 [('0', 'print0', None, _('end fields with NUL')),
2743 2734 ('', 'all', None, _('print all revisions that match')),
2744 2735 ('f', 'follow', None,
2745 2736 _('follow changeset history, or file history across copies and renames')),
2746 2737 ('i', 'ignore-case', None, _('ignore case when matching')),
2747 2738 ('l', 'files-with-matches', None,
2748 2739 _('print only filenames and revs that match')),
2749 2740 ('n', 'line-number', None, _('print matching line numbers')),
2750 2741 ('r', 'rev', [], _('search in given revision range')),
2751 2742 ('u', 'user', None, _('print user who committed change')),
2752 2743 ] + walkopts,
2753 2744 _('hg grep [OPTION]... PATTERN [FILE]...')),
2754 2745 "heads":
2755 2746 (heads,
2756 2747 [('', 'style', '', _('display using template map file')),
2757 2748 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2758 2749 ('', 'template', '', _('display with template'))],
2759 2750 _('hg heads [-r REV] [REV]...')),
2760 2751 "help": (help_, [], _('hg help [COMMAND]')),
2761 2752 "identify|id":
2762 2753 (identify,
2763 2754 [('r', 'rev', '', _('identify the specified rev')),
2764 2755 ('n', 'num', None, _('show local revision number')),
2765 2756 ('i', 'id', None, _('show global revision id')),
2766 2757 ('b', 'branch', None, _('show branch')),
2767 2758 ('t', 'tags', None, _('show tags'))],
2768 2759 _('hg identify [-nibt] [-r REV] [SOURCE]')),
2769 2760 "import|patch":
2770 2761 (import_,
2771 2762 [('p', 'strip', 1,
2772 2763 _('directory strip option for patch. This has the same\n'
2773 2764 'meaning as the corresponding patch option')),
2774 2765 ('b', 'base', '', _('base path')),
2775 2766 ('f', 'force', None,
2776 2767 _('skip check for outstanding uncommitted changes')),
2777 2768 ('', 'exact', None,
2778 2769 _('apply patch to the nodes from which it was generated')),
2779 2770 ('', 'import-branch', None,
2780 2771 _('Use any branch information in patch (implied by --exact)'))] + commitopts,
2781 2772 _('hg import [-p NUM] [-m MESSAGE] [-f] PATCH...')),
2782 2773 "incoming|in": (incoming,
2783 2774 [('M', 'no-merges', None, _('do not show merges')),
2784 2775 ('f', 'force', None,
2785 2776 _('run even when remote repository is unrelated')),
2786 2777 ('', 'style', '', _('display using template map file')),
2787 2778 ('n', 'newest-first', None, _('show newest record first')),
2788 2779 ('', 'bundle', '', _('file to store the bundles into')),
2789 2780 ('p', 'patch', None, _('show patch')),
2790 2781 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2791 2782 ('', 'template', '', _('display with template')),
2792 2783 ] + remoteopts,
2793 2784 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
2794 2785 ' [--bundle FILENAME] [SOURCE]')),
2795 2786 "^init":
2796 2787 (init,
2797 2788 remoteopts,
2798 2789 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
2799 2790 "locate":
2800 2791 (locate,
2801 2792 [('r', 'rev', '', _('search the repository as it stood at rev')),
2802 2793 ('0', 'print0', None,
2803 2794 _('end filenames with NUL, for use with xargs')),
2804 2795 ('f', 'fullpath', None,
2805 2796 _('print complete paths from the filesystem root')),
2806 2797 ] + walkopts,
2807 2798 _('hg locate [OPTION]... [PATTERN]...')),
2808 2799 "^log|history":
2809 2800 (log,
2810 2801 [('f', 'follow', None,
2811 2802 _('follow changeset history, or file history across copies and renames')),
2812 2803 ('', 'follow-first', None,
2813 2804 _('only follow the first parent of merge changesets')),
2814 2805 ('d', 'date', '', _('show revs matching date spec')),
2815 2806 ('C', 'copies', None, _('show copied files')),
2816 2807 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
2817 2808 ('l', 'limit', '', _('limit number of changes displayed')),
2818 2809 ('r', 'rev', [], _('show the specified revision or range')),
2819 2810 ('', 'removed', None, _('include revs where files were removed')),
2820 2811 ('M', 'no-merges', None, _('do not show merges')),
2821 2812 ('', 'style', '', _('display using template map file')),
2822 2813 ('m', 'only-merges', None, _('show only merges')),
2823 2814 ('p', 'patch', None, _('show patch')),
2824 2815 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
2825 2816 ('', 'template', '', _('display with template')),
2826 2817 ] + walkopts,
2827 2818 _('hg log [OPTION]... [FILE]')),
2828 2819 "manifest": (manifest, [('r', 'rev', '', _('revision to display'))],
2829 2820 _('hg manifest [-r REV]')),
2830 2821 "^merge":
2831 2822 (merge,
2832 2823 [('f', 'force', None, _('force a merge with outstanding changes')),
2833 2824 ('r', 'rev', '', _('revision to merge')),
2834 2825 ],
2835 2826 _('hg merge [-f] [[-r] REV]')),
2836 2827 "outgoing|out": (outgoing,
2837 2828 [('M', 'no-merges', None, _('do not show merges')),
2838 2829 ('f', 'force', None,
2839 2830 _('run even when remote repository is unrelated')),
2840 2831 ('p', 'patch', None, _('show patch')),
2841 2832 ('', 'style', '', _('display using template map file')),
2842 2833 ('r', 'rev', [], _('a specific revision you would like to push')),
2843 2834 ('n', 'newest-first', None, _('show newest record first')),
2844 2835 ('', 'template', '', _('display with template')),
2845 2836 ] + remoteopts,
2846 2837 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
2847 2838 "^parents":
2848 2839 (parents,
2849 2840 [('r', 'rev', '', _('show parents from the specified rev')),
2850 2841 ('', 'style', '', _('display using template map file')),
2851 2842 ('', 'template', '', _('display with template'))],
2852 2843 _('hg parents [-r REV] [FILE]')),
2853 2844 "paths": (paths, [], _('hg paths [NAME]')),
2854 2845 "^pull":
2855 2846 (pull,
2856 2847 [('u', 'update', None,
2857 2848 _('update to new tip if changesets were pulled')),
2858 2849 ('f', 'force', None,
2859 2850 _('run even when remote repository is unrelated')),
2860 2851 ('r', 'rev', [],
2861 2852 _('a specific revision up to which you would like to pull')),
2862 2853 ] + remoteopts,
2863 2854 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
2864 2855 "^push":
2865 2856 (push,
2866 2857 [('f', 'force', None, _('force push')),
2867 2858 ('r', 'rev', [], _('a specific revision you would like to push')),
2868 2859 ] + remoteopts,
2869 2860 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
2870 2861 "debugrawcommit|rawcommit":
2871 2862 (rawcommit,
2872 2863 [('p', 'parent', [], _('parent')),
2873 2864 ('F', 'files', '', _('file list'))
2874 2865 ] + commitopts + commitopts2,
2875 2866 _('hg debugrawcommit [OPTION]... [FILE]...')),
2876 2867 "recover": (recover, [], _('hg recover')),
2877 2868 "^remove|rm":
2878 2869 (remove,
2879 2870 [('A', 'after', None, _('record remove without deleting')),
2880 2871 ('f', 'force', None, _('remove file even if modified')),
2881 2872 ] + walkopts,
2882 2873 _('hg remove [OPTION]... FILE...')),
2883 2874 "rename|mv":
2884 2875 (rename,
2885 2876 [('A', 'after', None, _('record a rename that has already occurred')),
2886 2877 ('f', 'force', None,
2887 2878 _('forcibly copy over an existing managed file')),
2888 2879 ] + walkopts + dryrunopts,
2889 2880 _('hg rename [OPTION]... SOURCE... DEST')),
2890 2881 "revert":
2891 2882 (revert,
2892 2883 [('a', 'all', None, _('revert all changes when no arguments given')),
2893 2884 ('d', 'date', '', _('tipmost revision matching date')),
2894 2885 ('r', 'rev', '', _('revision to revert to')),
2895 2886 ('', 'no-backup', None, _('do not save backup copies of files')),
2896 2887 ] + walkopts + dryrunopts,
2897 2888 _('hg revert [OPTION]... [-r REV] [NAME]...')),
2898 2889 "rollback": (rollback, [], _('hg rollback')),
2899 2890 "root": (root, [], _('hg root')),
2900 2891 "showconfig|debugconfig":
2901 2892 (showconfig,
2902 2893 [('u', 'untrusted', None, _('show untrusted configuration options'))],
2903 2894 _('showconfig [-u] [NAME]...')),
2904 2895 "^serve":
2905 2896 (serve,
2906 2897 [('A', 'accesslog', '', _('name of access log file to write to')),
2907 2898 ('d', 'daemon', None, _('run server in background')),
2908 2899 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
2909 2900 ('E', 'errorlog', '', _('name of error log file to write to')),
2910 2901 ('p', 'port', 0, _('port to use (default: 8000)')),
2911 2902 ('a', 'address', '', _('address to use')),
2912 2903 ('n', 'name', '',
2913 2904 _('name to show in web pages (default: working dir)')),
2914 2905 ('', 'webdir-conf', '', _('name of the webdir config file'
2915 2906 ' (serve more than one repo)')),
2916 2907 ('', 'pid-file', '', _('name of file to write process ID to')),
2917 2908 ('', 'stdio', None, _('for remote clients')),
2918 2909 ('t', 'templates', '', _('web templates to use')),
2919 2910 ('', 'style', '', _('template style to use')),
2920 2911 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
2921 2912 ('', 'certificate', '', _('SSL certificate file'))],
2922 2913 _('hg serve [OPTION]...')),
2923 2914 "^status|st":
2924 2915 (status,
2925 2916 [('A', 'all', None, _('show status of all files')),
2926 2917 ('m', 'modified', None, _('show only modified files')),
2927 2918 ('a', 'added', None, _('show only added files')),
2928 2919 ('r', 'removed', None, _('show only removed files')),
2929 2920 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
2930 2921 ('c', 'clean', None, _('show only files without changes')),
2931 2922 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
2932 2923 ('i', 'ignored', None, _('show only ignored files')),
2933 2924 ('n', 'no-status', None, _('hide status prefix')),
2934 2925 ('C', 'copies', None, _('show source of copied files')),
2935 2926 ('0', 'print0', None,
2936 2927 _('end filenames with NUL, for use with xargs')),
2937 2928 ('', 'rev', [], _('show difference from revision')),
2938 2929 ] + walkopts,
2939 2930 _('hg status [OPTION]... [FILE]...')),
2940 2931 "tag":
2941 2932 (tag,
2942 2933 [('f', 'force', None, _('replace existing tag')),
2943 2934 ('l', 'local', None, _('make the tag local')),
2944 2935 ('r', 'rev', '', _('revision to tag')),
2945 2936 ('', 'remove', None, _('remove a tag')),
2946 2937 # -l/--local is already there, commitopts cannot be used
2947 2938 ('m', 'message', '', _('use <text> as commit message')),
2948 2939 ] + commitopts2,
2949 2940 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
2950 2941 "tags": (tags, [], _('hg tags')),
2951 2942 "tip":
2952 2943 (tip,
2953 2944 [('', 'style', '', _('display using template map file')),
2954 2945 ('p', 'patch', None, _('show patch')),
2955 2946 ('', 'template', '', _('display with template'))],
2956 2947 _('hg tip [-p]')),
2957 2948 "unbundle":
2958 2949 (unbundle,
2959 2950 [('u', 'update', None,
2960 2951 _('update to new tip if changesets were unbundled'))],
2961 2952 _('hg unbundle [-u] FILE...')),
2962 2953 "^update|up|checkout|co":
2963 2954 (update,
2964 2955 [('C', 'clean', None, _('overwrite locally modified files')),
2965 2956 ('d', 'date', '', _('tipmost revision matching date')),
2966 2957 ('r', 'rev', '', _('revision'))],
2967 2958 _('hg update [-C] [-d DATE] [[-r] REV]')),
2968 2959 "verify": (verify, [], _('hg verify')),
2969 2960 "version": (version_, [], _('hg version')),
2970 2961 }
2971 2962
2972 2963 norepo = ("clone init version help debugancestor debugcomplete debugdata"
2973 2964 " debugindex debugindexdot debugdate debuginstall")
2974 2965 optionalrepo = ("identify paths serve showconfig")
@@ -1,11 +1,10 b''
1 1 adding src/a.c
2 copying src/a.c to source/a.c
3 removing src/a.c
2 moving src/a.c to source/a.c
4 3 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
5 4 ? src/a.o
6 5 merging src/a.c and source/a.c
7 6 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
8 7 (branch merge, don't forget to commit)
9 8 M source/a.c
10 9 R src/a.c
11 10 ? source/a.o
@@ -1,75 +1,73 b''
1 1 adding a/a
2 2 adding a/b
3 3 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
4 copying a/a to b/a
5 copying a/b to b/b
6 removing a/a
7 removing a/b
4 moving a/a to b/a
5 moving a/b to b/b
8 6 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
9 7 resolving manifests
10 8 overwrite None partial False
11 9 ancestor f9b20c0d4c51 local ce36d17b18fb+ remote 55119e611c80
12 10 searching for copies back to rev 1
13 11 unmatched files in local:
14 12 a/c
15 13 a/d
16 14 unmatched files in other:
17 15 b/a
18 16 b/b
19 17 all copies found (* = to merge, ! = divergent):
20 18 b/a -> a/a
21 19 b/b -> a/b
22 20 checking for directory renames
23 21 dir a/ -> b/
24 22 file a/c -> b/c
25 23 file a/d -> b/d
26 24 a/d: remote renamed directory to b/d -> d
27 25 a/c: remote renamed directory to b/c -> d
28 26 a/b: other deleted -> r
29 27 a/a: other deleted -> r
30 28 b/a: remote created -> g
31 29 b/b: remote created -> g
32 30 removing a/a
33 31 removing a/b
34 32 moving a/c to b/c
35 33 moving a/d to b/d
36 34 getting b/a
37 35 getting b/b
38 36 4 files updated, 0 files merged, 2 files removed, 0 files unresolved
39 37 (branch merge, don't forget to commit)
40 38 a/* b/a b/b b/c b/d
41 39 M b/a
42 40 M b/b
43 41 A b/c
44 42 a/c
45 43 R a/a
46 44 R a/b
47 45 R a/c
48 46 ? b/d
49 47 b/c renamed from a/c:354ae8da6e890359ef49ade27b68bbc361f3ca88
50 48 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
51 49 resolving manifests
52 50 overwrite None partial False
53 51 ancestor f9b20c0d4c51 local 55119e611c80+ remote ce36d17b18fb
54 52 searching for copies back to rev 1
55 53 unmatched files in local:
56 54 b/a
57 55 b/b
58 56 b/d
59 57 unmatched files in other:
60 58 a/c
61 59 all copies found (* = to merge, ! = divergent):
62 60 b/a -> a/a
63 61 b/b -> a/b
64 62 checking for directory renames
65 63 dir a/ -> b/
66 64 file a/c -> b/c
67 65 None: local renamed directory to b/c -> d
68 66 getting a/c to b/c
69 67 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
70 68 (branch merge, don't forget to commit)
71 69 a/* b/a b/b b/c b/d
72 70 A b/c
73 71 a/c
74 72 ? b/d
75 73 b/c renamed from a/c:354ae8da6e890359ef49ade27b68bbc361f3ca88
@@ -1,19 +1,18 b''
1 1 adding a/f
2 2 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
3 copying a/f to b/f
4 removing a/f
3 moving a/f to b/f
5 4 adding a/aa/g
6 5 pulling from ../r2
7 6 searching for changes
8 7 adding changesets
9 8 adding manifests
10 9 adding file changes
11 10 added 1 changesets with 1 changes to 1 files (+1 heads)
12 11 (run 'hg heads' to see heads, 'hg merge' to merge)
13 12 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
14 13 (branch merge, don't forget to commit)
15 14 M b/f
16 15 A b/aa/g
17 16 a/aa/g
18 17 R a/aa/g
19 18 R a/f
@@ -1,361 +1,315 b''
1 1 # rename a single file
2 2 A d2/c
3 3 d1/d11/a1
4 4 R d1/d11/a1
5 5 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
6 6 # rename --after a single file
7 7 A d2/c
8 8 d1/d11/a1
9 9 R d1/d11/a1
10 10 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
11 11 # move a single file to an existing directory
12 12 A d2/a1
13 13 d1/d11/a1
14 14 R d1/d11/a1
15 15 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
16 16 # move --after a single file to an existing directory
17 17 A d2/a1
18 18 d1/d11/a1
19 19 R d1/d11/a1
20 20 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
21 21 # rename a file using a relative path
22 22 A d1/d11/e
23 23 d2/b
24 24 R d2/b
25 25 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
26 26 # rename --after a file using a relative path
27 27 A d1/d11/e
28 28 d2/b
29 29 R d2/b
30 30 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
31 31 # rename directory d1 as d3
32 copying d1/a to d3/a
33 copying d1/b to d3/b
34 copying d1/ba to d3/ba
35 copying d1/d11/a1 to d3/d11/a1
36 removing d1/a
37 removing d1/b
38 removing d1/ba
39 removing d1/d11/a1
32 moving d1/a to d3/a
33 moving d1/b to d3/b
34 moving d1/ba to d3/ba
35 moving d1/d11/a1 to d3/d11/a1
40 36 A d3/a
41 37 d1/a
42 38 A d3/b
43 39 d1/b
44 40 A d3/ba
45 41 d1/ba
46 42 A d3/d11/a1
47 43 d1/d11/a1
48 44 R d1/a
49 45 R d1/b
50 46 R d1/ba
51 47 R d1/d11/a1
52 48 4 files updated, 0 files merged, 4 files removed, 0 files unresolved
53 49 # rename --after directory d1 as d3
54 copying d1/a to d3/a
55 copying d1/b to d3/b
56 copying d1/ba to d3/ba
57 copying d1/d11/a1 to d3/d11/a1
58 removing d1/a
59 removing d1/b
60 removing d1/ba
61 removing d1/d11/a1
50 moving d1/a to d3/a
51 moving d1/b to d3/b
52 moving d1/ba to d3/ba
53 moving d1/d11/a1 to d3/d11/a1
62 54 A d3/a
63 55 d1/a
64 56 A d3/b
65 57 d1/b
66 58 A d3/ba
67 59 d1/ba
68 60 A d3/d11/a1
69 61 d1/d11/a1
70 62 R d1/a
71 63 R d1/b
72 64 R d1/ba
73 65 R d1/d11/a1
74 66 4 files updated, 0 files merged, 4 files removed, 0 files unresolved
75 67 # move a directory using a relative path
76 copying ../d1/d11/a1 to d3/d11/a1
77 removing ../d1/d11/a1
68 moving ../d1/d11/a1 to d3/d11/a1
78 69 A d2/d3/d11/a1
79 70 d1/d11/a1
80 71 R d1/d11/a1
81 72 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
82 73 # move --after a directory using a relative path
83 copying ../d1/d11/a1 to d3/d11/a1
84 removing ../d1/d11/a1
74 moving ../d1/d11/a1 to d3/d11/a1
85 75 A d2/d3/d11/a1
86 76 d1/d11/a1
87 77 R d1/d11/a1
88 78 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
89 79 # move directory d1/d11 to an existing directory d2 (removes empty d1)
90 copying d1/d11/a1 to d2/d11/a1
91 removing d1/d11/a1
80 moving d1/d11/a1 to d2/d11/a1
92 81 A d2/d11/a1
93 82 d1/d11/a1
94 83 R d1/d11/a1
95 84 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
96 85 # move directories d1 and d2 to a new directory d3
97 copying d1/a to d3/d1/a
98 copying d1/b to d3/d1/b
99 copying d1/ba to d3/d1/ba
100 copying d1/d11/a1 to d3/d1/d11/a1
101 copying d2/b to d3/d2/b
102 removing d1/a
103 removing d1/b
104 removing d1/ba
105 removing d1/d11/a1
106 removing d2/b
86 moving d1/a to d3/d1/a
87 moving d1/b to d3/d1/b
88 moving d1/ba to d3/d1/ba
89 moving d1/d11/a1 to d3/d1/d11/a1
90 moving d2/b to d3/d2/b
107 91 A d3/d1/a
108 92 d1/a
109 93 A d3/d1/b
110 94 d1/b
111 95 A d3/d1/ba
112 96 d1/ba
113 97 A d3/d1/d11/a1
114 98 d1/d11/a1
115 99 A d3/d2/b
116 100 d2/b
117 101 R d1/a
118 102 R d1/b
119 103 R d1/ba
120 104 R d1/d11/a1
121 105 R d2/b
122 106 5 files updated, 0 files merged, 5 files removed, 0 files unresolved
123 107 # move --after directories d1 and d2 to a new directory d3
124 copying d1/a to d3/d1/a
125 copying d1/b to d3/d1/b
126 copying d1/ba to d3/d1/ba
127 copying d1/d11/a1 to d3/d1/d11/a1
128 copying d2/b to d3/d2/b
129 removing d1/a
130 removing d1/b
131 removing d1/ba
132 removing d1/d11/a1
133 removing d2/b
108 moving d1/a to d3/d1/a
109 moving d1/b to d3/d1/b
110 moving d1/ba to d3/d1/ba
111 moving d1/d11/a1 to d3/d1/d11/a1
112 moving d2/b to d3/d2/b
134 113 A d3/d1/a
135 114 d1/a
136 115 A d3/d1/b
137 116 d1/b
138 117 A d3/d1/ba
139 118 d1/ba
140 119 A d3/d1/d11/a1
141 120 d1/d11/a1
142 121 A d3/d2/b
143 122 d2/b
144 123 R d1/a
145 124 R d1/b
146 125 R d1/ba
147 126 R d1/d11/a1
148 127 R d2/b
149 128 5 files updated, 0 files merged, 5 files removed, 0 files unresolved
150 129 # move everything under directory d1 to existing directory d2, do not
151 130 # overwrite existing files (d2/b)
152 131 d2/b: not overwriting - file exists
153 copying d1/d11/a1 to d2/d11/a1
154 removing d1/d11/a1
132 moving d1/d11/a1 to d2/d11/a1
155 133 A d2/a
156 134 d1/a
157 135 A d2/ba
158 136 d1/ba
159 137 A d2/d11/a1
160 138 d1/d11/a1
161 139 R d1/a
162 140 R d1/ba
163 141 R d1/d11/a1
164 142 1c1
165 143 < d1/b
166 144 ---
167 145 > d2/b
168 146 3 files updated, 0 files merged, 3 files removed, 0 files unresolved
169 147 # attempt to move one file into a non-existent directory
170 148 abort: destination dx/ is not a directory
171 149 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
172 150 # attempt to move potentially more than one file into a non-existent
173 151 # directory
174 152 abort: with multiple sources, destination must be an existing directory
175 153 # move every file under d1 to d2/d21 (glob)
176 copying d1/a to d2/d21/a
177 copying d1/b to d2/d21/b
178 copying d1/ba to d2/d21/ba
179 copying d1/d11/a1 to d2/d21/a1
180 removing d1/a
181 removing d1/b
182 removing d1/ba
183 removing d1/d11/a1
154 moving d1/a to d2/d21/a
155 moving d1/b to d2/d21/b
156 moving d1/ba to d2/d21/ba
157 moving d1/d11/a1 to d2/d21/a1
184 158 A d2/d21/a
185 159 d1/a
186 160 A d2/d21/a1
187 161 d1/d11/a1
188 162 A d2/d21/b
189 163 d1/b
190 164 A d2/d21/ba
191 165 d1/ba
192 166 R d1/a
193 167 R d1/b
194 168 R d1/ba
195 169 R d1/d11/a1
196 170 4 files updated, 0 files merged, 4 files removed, 0 files unresolved
197 171 # move --after some files under d1 to d2/d21 (glob)
198 copying d1/a to d2/d21/a
199 copying d1/d11/a1 to d2/d21/a1
200 removing d1/a
201 removing d1/d11/a1
172 moving d1/a to d2/d21/a
173 moving d1/d11/a1 to d2/d21/a1
202 174 A d2/d21/a
203 175 d1/a
204 176 A d2/d21/a1
205 177 d1/d11/a1
206 178 R d1/a
207 179 R d1/d11/a1
208 180 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
209 181 # move every file under d1 starting with an 'a' to d2/d21 (regexp)
210 copying d1/a to d2/d21/a
211 copying d1/d11/a1 to d2/d21/a1
212 removing d1/a
213 removing d1/d11/a1
182 moving d1/a to d2/d21/a
183 moving d1/d11/a1 to d2/d21/a1
214 184 A d2/d21/a
215 185 d1/a
216 186 A d2/d21/a1
217 187 d1/d11/a1
218 188 R d1/a
219 189 R d1/d11/a1
220 190 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
221 191 # attempt to overwrite an existing file
222 192 d1/ca: not overwriting - file exists
223 193 ? d1/ca
224 194 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
225 195 # forced overwrite of an existing file
226 196 A d1/ca
227 197 d1/ba
228 198 R d1/ba
229 199 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
230 200 # replace a symlink with a file
231 201 A d1/ca
232 202 d1/ba
233 203 R d1/ba
234 204 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
235 205 # do not copy more than one source file to the same destination file
236 copying d1/d11/a1 to d3/d11/a1
206 moving d1/d11/a1 to d3/d11/a1
237 207 d3/b: not overwriting - d2/b collides with d1/b
238 removing d1/d11/a1
239 208 A d3/a
240 209 d1/a
241 210 A d3/b
242 211 d1/b
243 212 A d3/ba
244 213 d1/ba
245 214 A d3/d11/a1
246 215 d1/d11/a1
247 216 R d1/a
248 217 R d1/b
249 218 R d1/ba
250 219 R d1/d11/a1
251 220 4 files updated, 0 files merged, 4 files removed, 0 files unresolved
252 221 # move a whole subtree with "hg rename ."
253 copying a to ../d3/d1/a
254 copying b to ../d3/d1/b
255 copying ba to ../d3/d1/ba
256 copying d11/a1 to ../d3/d1/d11/a1
257 removing a
258 removing b
259 removing ba
260 removing d11/a1
222 moving a to ../d3/d1/a
223 moving b to ../d3/d1/b
224 moving ba to ../d3/d1/ba
225 moving d11/a1 to ../d3/d1/d11/a1
261 226 A d3/d1/a
262 227 d1/a
263 228 A d3/d1/b
264 229 d1/b
265 230 A d3/d1/ba
266 231 d1/ba
267 232 A d3/d1/d11/a1
268 233 d1/d11/a1
269 234 R d1/a
270 235 R d1/b
271 236 R d1/ba
272 237 R d1/d11/a1
273 238 4 files updated, 0 files merged, 4 files removed, 0 files unresolved
274 239 # move a whole subtree with "hg rename --after ."
275 copying a to ../d3/a
276 copying b to ../d3/b
277 copying ba to ../d3/ba
278 copying d11/a1 to ../d3/d11/a1
279 removing a
280 removing b
281 removing ba
282 removing d11/a1
240 moving a to ../d3/a
241 moving b to ../d3/b
242 moving ba to ../d3/ba
243 moving d11/a1 to ../d3/d11/a1
283 244 A d3/a
284 245 d1/a
285 246 A d3/b
286 247 d1/b
287 248 A d3/ba
288 249 d1/ba
289 250 A d3/d11/a1
290 251 d1/d11/a1
291 252 R d1/a
292 253 R d1/b
293 254 R d1/ba
294 255 R d1/d11/a1
295 256 4 files updated, 0 files merged, 4 files removed, 0 files unresolved
296 257 # move the parent tree with "hg rename .."
297 copying ../a to ../../d3/a
298 copying ../b to ../../d3/b
299 copying ../ba to ../../d3/ba
300 copying a1 to ../../d3/d11/a1
301 removing ../a
302 removing ../b
303 removing ../ba
304 removing a1
258 moving ../a to ../../d3/a
259 moving ../b to ../../d3/b
260 moving ../ba to ../../d3/ba
261 moving a1 to ../../d3/d11/a1
305 262 A d3/a
306 263 d1/a
307 264 A d3/b
308 265 d1/b
309 266 A d3/ba
310 267 d1/ba
311 268 A d3/d11/a1
312 269 d1/d11/a1
313 270 R d1/a
314 271 R d1/b
315 272 R d1/ba
316 273 R d1/d11/a1
317 274 4 files updated, 0 files merged, 4 files removed, 0 files unresolved
318 275 # skip removed files
319 copying d1/a to d3/a
320 copying d1/ba to d3/ba
321 copying d1/d11/a1 to d3/d11/a1
322 removing d1/a
323 removing d1/ba
324 removing d1/d11/a1
276 moving d1/a to d3/a
277 moving d1/ba to d3/ba
278 moving d1/d11/a1 to d3/d11/a1
325 279 A d3/a
326 280 d1/a
327 281 A d3/ba
328 282 d1/ba
329 283 A d3/d11/a1
330 284 d1/d11/a1
331 285 R d1/a
332 286 R d1/b
333 287 R d1/ba
334 288 R d1/d11/a1
335 289 4 files updated, 0 files merged, 3 files removed, 0 files unresolved
336 290 # transitive rename
337 291 A d1/bc
338 292 d1/b
339 293 R d1/b
340 294 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
341 295 # transitive rename --after
342 296 A d1/bc
343 297 d1/b
344 298 R d1/b
345 299 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
346 300 # idempotent renames (d1/b -> d1/bb followed by d1/bb -> d1/b)
347 301 M d1/b
348 302 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
349 303 # check illegal path components
350 304 abort: path contains illegal component: .hg/foo
351 305 abort: ../foo not under root
352 306 abort: path contains illegal component: .hg/foo
353 307 ! d1/d11/a1
354 308 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
355 309 abort: path contains illegal component: .hg/a1
356 310 abort: ../a1 not under root
357 311 abort: path contains illegal component: .hg/a1
358 312 ! d1/d11/a1
359 313 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
360 314 abort: path contains illegal component: .hg/foo
361 315 abort: ../../../foo not under root
General Comments 0
You need to be logged in to leave comments. Login now