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