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