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