##// END OF EJS Templates
Merge from BOS...
mpm@selenic.com -
r740:d2422f10 merge default
parent child Browse files
Show More
@@ -33,7 +33,8 b' COMMAND ELEMENTS'
33 ----------------
33 ----------------
34
34
35 files ...::
35 files ...::
36 indicates one or more filename or relative path filenames
36 indicates one or more filename or relative path filenames; see
37 "FILE NAME PATTERNS" for information on pattern matching
37
38
38 path::
39 path::
39 indicates a path on the local machine
40 indicates a path on the local machine
@@ -51,11 +52,14 b' repository path::'
51 COMMANDS
52 COMMANDS
52 --------
53 --------
53
54
54 add [files ...]::
55 add [options] [files ...]::
55 Schedule files to be version controlled and added to the repository.
56 Schedule files to be version controlled and added to the repository.
56
57
57 The files will be added to the repository at the next commit.
58 The files will be added to the repository at the next commit.
58
59
60 If no names are given, add all files in the current directory and
61 its subdirectories.
62
59 addremove::
63 addremove::
60 Add all new files and remove all missing files from the repository.
64 Add all new files and remove all missing files from the repository.
61
65
@@ -69,6 +73,8 b' annotate [-r <rev> -u -n -c] [files ...]'
69 place.
73 place.
70
74
71 options:
75 options:
76 -I, --include <pat> include directories matching the given patterns
77 -X, --exclude <pat> exclude directories matching the given patterns
72 -r, --revision <rev> annotate the specified revision
78 -r, --revision <rev> annotate the specified revision
73 -u, --user list the author
79 -u, --user list the author
74 -c, --changeset list the changeset
80 -c, --changeset list the changeset
@@ -129,6 +135,10 b' diff [-r revision] [-r revision] [files '
129 revisions are specified, the working directory files are compared
135 revisions are specified, the working directory files are compared
130 to its parent.
136 to its parent.
131
137
138 options:
139 -I, --include <pat> include directories matching the given patterns
140 -X, --exclude <pat> exclude directories matching the given patterns
141
132 export [-o filespec] [revision] ...::
142 export [-o filespec] [revision] ...::
133 Print the changeset header and diffs for one or more revisions.
143 Print the changeset header and diffs for one or more revisions.
134
144
@@ -183,14 +193,10 b' import [-p <n> -b <base> -q] <patches>::'
183 init::
193 init::
184 Initialize a new repository in the current directory.
194 Initialize a new repository in the current directory.
185
195
186 locate [options] [patterns]::
196 locate [options] [files]::
187 Print all files under Mercurial control whose basenames match the
197 Print all files under Mercurial control whose names match the
188 given patterns.
198 given patterns.
189
199
190 Patterns are shell-style globs. To restrict searches to specific
191 directories, use the "-i <pat>" option. To eliminate particular
192 directories from searching, use the "-x <pat>" option.
193
194 This command searches the current directory and its
200 This command searches the current directory and its
195 subdirectories. To search an entire repository, move to the root
201 subdirectories. To search an entire repository, move to the root
196 of the repository.
202 of the repository.
@@ -207,9 +213,9 b' locate [options] [patterns]::'
207
213
208 -0, --print0 end filenames with NUL, for use with xargs
214 -0, --print0 end filenames with NUL, for use with xargs
209 -f, --fullpath print complete paths from the filesystem root
215 -f, --fullpath print complete paths from the filesystem root
210 -i, --include <pat> include directories matching the given globs
216 -I, --include <pat> include directories matching the given patterns
211 -r, --rev <rev> search the repository as it stood at rev
217 -r, --rev <rev> search the repository as it stood at rev
212 -x, --exclude <pat> exclude directories matching the given globs
218 -X, --exclude <pat> exclude directories matching the given patterns
213
219
214 log [-r revision ...] [-p] [file]::
220 log [-r revision ...] [-p] [file]::
215 Print the revision history of the specified file or the entire project.
221 Print the revision history of the specified file or the entire project.
@@ -319,8 +325,10 b' serve [options]::'
319 -n, --name <name> name to show in web pages (default: working dir)
325 -n, --name <name> name to show in web pages (default: working dir)
320 -t, --templatedir <path> web templates to use
326 -t, --templatedir <path> web templates to use
321
327
322 status::
328 status [options] [files]::
323 Show changed files in the working directory.
329 Show changed files in the working directory. If no names are
330 given, all files are shown. Otherwise, only files matching the
331 given names are shown.
324
332
325 The codes used to show the status of files are:
333 The codes used to show the status of files are:
326
334
@@ -329,6 +337,11 b' status::'
329 R = removed
337 R = removed
330 ? = not tracked
338 ? = not tracked
331
339
340 options:
341
342 -I, --include <pat> include directories matching the given patterns
343 -X, --exclude <pat> exclude directories matching the given patterns
344
332 tag [-l -t <text> -d <datecode> -u <user>] <name> [revision]::
345 tag [-l -t <text> -d <datecode> -u <user>] <name> [revision]::
333 Name a particular revision using <name>.
346 Name a particular revision using <name>.
334
347
@@ -398,6 +411,52 b' verify::'
398 the changelog, manifest, and tracked files, as well as the
411 the changelog, manifest, and tracked files, as well as the
399 integrity of their crosslinks and indices.
412 integrity of their crosslinks and indices.
400
413
414 FILE NAME PATTERNS
415 ------------------
416
417 Mercurial accepts several notations for identifying one or more
418 file at a time.
419
420 By default, Mercurial treats file names as shell-style extended
421 glob patterns.
422
423 Alternate pattern notations must be specified explicitly.
424
425 To use a plain path name without any pattern matching, start a
426 name with "path:". These path names must match completely, from
427 the root of the current repository.
428
429 To use an extended glob, start a name with "glob:". Globs are
430 rooted at the current directory; a glob such as "*.c" will match
431 files ending in ".c" in the current directory only.
432
433 The supported glob syntax extensions are "**" to match any string
434 across path separators, and "{a,b}" to mean "a or b".
435
436 To use a Perl/Python regular expression, start a name with "re:".
437 Regexp pattern matching is anchored at the root of the repository.
438
439 Plain examples:
440
441 path:foo/bar a name bar in a directory named foo in the root of
442 the repository
443 path:path:name a file or directory named "path:name"
444
445 Glob examples:
446
447 glob:*.c any name ending in ".c" in the current directory
448 *.c any name ending in ".c" in the current directory
449 **.c any name ending in ".c" in the current directory, or
450 any subdirectory
451 foo/*.c any name ending in ".c" in the directory foo
452 foo/**.c any name ending in ".c" in the directory foo, or any
453 subdirectory
454
455 Regexp examples:
456
457 re:.*\.c$ any name ending in ".c", anywhere in the repsitory
458
459
401 SPECIFYING SINGLE REVISIONS
460 SPECIFYING SINGLE REVISIONS
402 ---------------------------
461 ---------------------------
403
462
@@ -14,6 +14,9 b' demandload(globals(), "errno socket vers'
14 class UnknownCommand(Exception):
14 class UnknownCommand(Exception):
15 """Exception raised if command is not in the command table."""
15 """Exception raised if command is not in the command table."""
16
16
17 class Abort(Exception):
18 """Raised if a command needs to print an error and exit."""
19
17 def filterfiles(filters, files):
20 def filterfiles(filters, files):
18 l = [x for x in files if x in filters]
21 l = [x for x in files if x in filters]
19
22
@@ -36,6 +39,41 b' def relpath(repo, args):'
36 for x in args]
39 for x in args]
37 return args
40 return args
38
41
42 def matchpats(ui, cwd, pats = [], opts = {}, emptyok = True):
43 if not pats and not emptyok:
44 raise Abort('at least one file name or pattern required')
45 head = ''
46 if opts.get('rootless'): head = '(?:.*/|)'
47 def reify(name, tail):
48 if name.startswith('re:'):
49 return name[3:]
50 elif name.startswith('glob:'):
51 return head + util.globre(name[5:], '', tail)
52 elif name.startswith('path:'):
53 return '^' + re.escape(name[5:]) + '$'
54 return head + util.globre(name, '', tail)
55 cwdsep = cwd + os.sep
56 def under(fn):
57 if not cwd or fn.startswith(cwdsep): return True
58 def matchfn(pats, tail, ifempty = util.always):
59 if not pats: return ifempty
60 pat = '(?:%s)' % '|'.join([reify(p, tail) for p in pats])
61 if cwd: pat = re.escape(cwd + os.sep) + pat
62 ui.debug('regexp: %s\n' % pat)
63 return re.compile(pat).match
64 patmatch = matchfn(pats, '$')
65 incmatch = matchfn(opts.get('include'), '(?:/|$)', under)
66 excmatch = matchfn(opts.get('exclude'), '(?:/|$)', util.never)
67 return lambda fn: (incmatch(fn) and not excmatch(fn) and
68 (fn.endswith('/') or patmatch(fn)))
69
70 def walk(repo, pats, opts, emptyok = True):
71 cwd = repo.getcwd()
72 if cwd: c = len(cwd) + 1
73 for src, fn in repo.walk(match = matchpats(repo.ui, cwd, pats, opts, emptyok)):
74 if cwd: yield src, fn, fn[c:]
75 else: yield src, fn, fn
76
39 revrangesep = ':'
77 revrangesep = ':'
40
78
41 def revrange(ui, repo, revs, revlog=None):
79 def revrange(ui, repo, revs, revlog=None):
@@ -60,8 +98,7 b' def revrange(ui, repo, revs, revlog=None'
60 try:
98 try:
61 num = revlog.rev(revlog.lookup(val))
99 num = revlog.rev(revlog.lookup(val))
62 except KeyError:
100 except KeyError:
63 ui.warn('abort: invalid revision identifier %s\n' % val)
101 raise Abort('invalid revision identifier %s', val)
64 sys.exit(1)
65 return num
102 return num
66 for spec in revs:
103 for spec in revs:
67 if spec.find(revrangesep) >= 0:
104 if spec.find(revrangesep) >= 0:
@@ -91,29 +128,45 b' def make_filename(repo, r, pat, node=Non'
91 'b': lambda: os.path.basename(repo.root),
128 'b': lambda: os.path.basename(repo.root),
92 }
129 }
93
130
94 if node:
131 try:
95 expander.update(node_expander)
132 if node:
96 if node and revwidth is not None:
133 expander.update(node_expander)
97 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
134 if node and revwidth is not None:
98 if total is not None:
135 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
99 expander['N'] = lambda: str(total)
136 if total is not None:
100 if seqno is not None:
137 expander['N'] = lambda: str(total)
101 expander['n'] = lambda: str(seqno)
138 if seqno is not None:
102 if total is not None and seqno is not None:
139 expander['n'] = lambda: str(seqno)
103 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
140 if total is not None and seqno is not None:
141 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
104
142
105 newname = []
143 newname = []
106 patlen = len(pat)
144 patlen = len(pat)
107 i = 0
145 i = 0
108 while i < patlen:
146 while i < patlen:
109 c = pat[i]
147 c = pat[i]
110 if c == '%':
148 if c == '%':
149 i += 1
150 c = pat[i]
151 c = expander[c]()
152 newname.append(c)
111 i += 1
153 i += 1
112 c = pat[i]
154 return ''.join(newname)
113 c = expander[c]()
155 except KeyError, inst:
114 newname.append(c)
156 raise Abort("invalid format spec '%%%s' in output file name",
115 i += 1
157 inst.args[0])
116 return ''.join(newname)
158
159 def make_file(repo, r, pat, node=None,
160 total=None, seqno=None, revwidth=None, mode='wb'):
161 if not pat or pat == '-':
162 if 'w' in mode: return sys.stdout
163 else: return sys.stdin
164 if hasattr(pat, 'write') and 'w' in mode:
165 return pat
166 if hasattr(pat, 'read') and 'r' in mode:
167 return pat
168 return open(make_filename(repo, r, pat, node, total, seqno, revwidth),
169 mode)
117
170
118 def dodiff(fp, ui, repo, files=None, node1=None, node2=None):
171 def dodiff(fp, ui, repo, files=None, node1=None, node2=None):
119 def date(c):
172 def date(c):
@@ -288,9 +341,17 b' def help_(ui, cmd=None):'
288
341
289 # Commands start here, listed alphabetically
342 # Commands start here, listed alphabetically
290
343
291 def add(ui, repo, file1, *files):
344 def add(ui, repo, *pats, **opts):
292 '''add the specified files on the next commit'''
345 '''add the specified files on the next commit'''
293 repo.add(relpath(repo, (file1,) + files))
346 names = []
347 q = dict(zip(pats, pats))
348 for src, abs, rel in walk(repo, pats, opts):
349 if rel in q or abs in q:
350 names.append(abs)
351 elif repo.dirstate.state(abs) == '?':
352 ui.status('adding %s\n' % rel)
353 names.append(abs)
354 repo.add(names)
294
355
295 def addremove(ui, repo, *files):
356 def addremove(ui, repo, *files):
296 """add all new files, delete all missing files"""
357 """add all new files, delete all missing files"""
@@ -307,11 +368,11 b' def addremove(ui, repo, *files):'
307 elif s not in 'nmai' and isfile:
368 elif s not in 'nmai' and isfile:
308 u.append(f)
369 u.append(f)
309 else:
370 else:
310 (c, a, d, u) = repo.changes(None, None)
371 (c, a, d, u) = repo.changes()
311 repo.add(u)
372 repo.add(u)
312 repo.remove(d)
373 repo.remove(d)
313
374
314 def annotate(ui, repo, file1, *files, **opts):
375 def annotate(ui, repo, *pats, **opts):
315 """show changeset information per file line"""
376 """show changeset information per file line"""
316 def getnode(rev):
377 def getnode(rev):
317 return hg.short(repo.changelog.node(rev))
378 return hg.short(repo.changelog.node(rev))
@@ -342,8 +403,8 b' def annotate(ui, repo, file1, *files, **'
342 node = repo.dirstate.parents()[0]
403 node = repo.dirstate.parents()[0]
343 change = repo.changelog.read(node)
404 change = repo.changelog.read(node)
344 mmap = repo.manifest.read(change[0])
405 mmap = repo.manifest.read(change[0])
345 for f in relpath(repo, (file1,) + files):
406 for src, abs, rel in walk(repo, pats, opts, emptyok = False):
346 lines = repo.file(f).annotate(mmap[f])
407 lines = repo.file(abs).annotate(mmap[abs])
347 pieces = []
408 pieces = []
348
409
349 for o, f in opmap:
410 for o, f in opmap:
@@ -362,16 +423,7 b' def cat(ui, repo, file1, rev=None, **opt'
362 n = r.lookup(rev)
423 n = r.lookup(rev)
363 else:
424 else:
364 n = r.tip()
425 n = r.tip()
365 if opts['output'] and opts['output'] != '-':
426 fp = make_file(repo, r, opts['output'], node=n)
366 try:
367 outname = make_filename(repo, r, opts['output'], node=n)
368 fp = open(outname, 'wb')
369 except KeyError, inst:
370 ui.warn("error: invlaid format spec '%%%s' in output file name\n" %
371 inst.args[0])
372 sys.exit(1);
373 else:
374 fp = sys.stdout
375 fp.write(r.read(n))
427 fp.write(r.read(n))
376
428
377 def clone(ui, source, dest=None, **opts):
429 def clone(ui, source, dest=None, **opts):
@@ -475,8 +527,7 b' def debugcheckstate(ui, repo):'
475 ui.warn("%s in manifest1, but listed as state %s" % (f, state))
527 ui.warn("%s in manifest1, but listed as state %s" % (f, state))
476 errors += 1
528 errors += 1
477 if errors:
529 if errors:
478 ui.warn(".hg/dirstate inconsistent with current parent's manifest\n")
530 raise Abort(".hg/dirstate inconsistent with current parent's manifest")
479 sys.exit(1)
480
531
481 def debugstate(ui, repo):
532 def debugstate(ui, repo):
482 """show the contents of the current dirstate"""
533 """show the contents of the current dirstate"""
@@ -509,21 +560,18 b' def debugindexdot(ui, file_):'
509 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
560 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
510 ui.write("}\n")
561 ui.write("}\n")
511
562
512 def diff(ui, repo, *files, **opts):
563 def diff(ui, repo, *pats, **opts):
513 """diff working directory (or selected files)"""
564 """diff working directory (or selected files)"""
514 revs = []
565 revs = []
515 if opts['rev']:
566 if opts['rev']:
516 revs = map(lambda x: repo.lookup(x), opts['rev'])
567 revs = map(lambda x: repo.lookup(x), opts['rev'])
517
568
518 if len(revs) > 2:
569 if len(revs) > 2:
519 ui.warn("too many revisions to diff\n")
570 raise Abort("too many revisions to diff")
520 sys.exit(1)
521
571
522 if files:
572 files = []
523 files = relpath(repo, files)
573 for src, abs, rel in walk(repo, pats, opts):
524 else:
574 files.append(abs)
525 files = relpath(repo, [""])
526
527 dodiff(sys.stdout, ui, repo, files, *revs)
575 dodiff(sys.stdout, ui, repo, files, *revs)
528
576
529 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
577 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
@@ -531,19 +579,11 b' def doexport(ui, repo, changeset, seqno,'
531 prev, other = repo.changelog.parents(node)
579 prev, other = repo.changelog.parents(node)
532 change = repo.changelog.read(node)
580 change = repo.changelog.read(node)
533
581
534 if opts['output'] and opts['output'] != '-':
582 fp = make_file(repo, repo.changelog, opts['output'],
535 try:
583 node=node, total=total, seqno=seqno,
536 outname = make_filename(repo, repo.changelog, opts['output'],
584 revwidth=revwidth)
537 node=node, total=total, seqno=seqno,
585 if fp != sys.stdout:
538 revwidth=revwidth)
586 ui.note("Exporting patch to '%s'.\n" % fp.name)
539 ui.note("Exporting patch to '%s'.\n" % outname)
540 fp = open(outname, 'wb')
541 except KeyError, inst:
542 ui.warn("error: invalid format spec '%%%s' in output file name\n" %
543 inst.args[0])
544 sys.exit(1)
545 else:
546 fp = sys.stdout
547
587
548 fp.write("# HG changeset patch\n")
588 fp.write("# HG changeset patch\n")
549 fp.write("# User %s\n" % change[1])
589 fp.write("# User %s\n" % change[1])
@@ -555,12 +595,12 b' def doexport(ui, repo, changeset, seqno,'
555 fp.write("\n\n")
595 fp.write("\n\n")
556
596
557 dodiff(fp, ui, repo, None, prev, node)
597 dodiff(fp, ui, repo, None, prev, node)
598 if fp != sys.stdout: fp.close()
558
599
559 def export(ui, repo, *changesets, **opts):
600 def export(ui, repo, *changesets, **opts):
560 """dump the header and diffs for one or more changesets"""
601 """dump the header and diffs for one or more changesets"""
561 if not changesets:
602 if not changesets:
562 ui.warn("error: export requires at least one changeset\n")
603 raise Abort("export requires at least one changeset")
563 sys.exit(1)
564 seqno = 0
604 seqno = 0
565 revs = list(revrange(ui, repo, changesets))
605 revs = list(revrange(ui, repo, changesets))
566 total = len(revs)
606 total = len(revs)
@@ -586,7 +626,7 b' def identify(ui, repo):'
586 return
626 return
587
627
588 hexfunc = ui.verbose and hg.hex or hg.short
628 hexfunc = ui.verbose and hg.hex or hg.short
589 (c, a, d, u) = repo.changes(None, None)
629 (c, a, d, u) = repo.changes()
590 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
630 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
591 (c or a or d) and "+" or "")]
631 (c or a or d) and "+" or "")]
592
632
@@ -654,8 +694,7 b' def import_(ui, repo, patch1, *patches, '
654 files.append(pf)
694 files.append(pf)
655 patcherr = f.close()
695 patcherr = f.close()
656 if patcherr:
696 if patcherr:
657 sys.stderr.write("patch failed")
697 raise Abort("patch failed")
658 sys.exit(1)
659
698
660 if len(files) > 0:
699 if len(files) > 0:
661 addremove(ui, repo, *files)
700 addremove(ui, repo, *files)
@@ -665,52 +704,20 b' def init(ui, source=None):'
665 """create a new repository in the current directory"""
704 """create a new repository in the current directory"""
666
705
667 if source:
706 if source:
668 ui.warn("no longer supported: use \"hg clone\" instead\n")
707 raise Abort("no longer supported: use \"hg clone\" instead")
669 sys.exit(1)
670 hg.repository(ui, ".", create=1)
708 hg.repository(ui, ".", create=1)
671
709
672 def locate(ui, repo, *pats, **opts):
710 def locate(ui, repo, *pats, **opts):
673 """locate files matching specific patterns"""
711 """locate files matching specific patterns"""
674 if [p for p in pats if os.sep in p]:
712 if opts['print0']: end = '\0'
675 ui.warn("error: patterns may not contain '%s'\n" % os.sep)
713 else: end = '\n'
676 ui.warn("use '-i <dir>' instead\n")
714 opts['rootless'] = True
677 sys.exit(1)
715 for src, abs, rel in walk(repo, pats, opts):
678 def compile(pats, head='^', tail=os.sep, on_empty=True):
716 if repo.dirstate.state(abs) == '?': continue
679 if not pats:
680 class c:
681 def match(self, x):
682 return on_empty
683 return c()
684 fnpats = [fnmatch.translate(os.path.normpath(os.path.normcase(p)))[:-1]
685 for p in pats]
686 regexp = r'%s(?:%s)%s' % (head, '|'.join(fnpats), tail)
687 return re.compile(regexp)
688 exclude = compile(opts['exclude'], on_empty=False)
689 include = compile(opts['include'])
690 pat = compile(pats, head='', tail='$')
691 end = opts['print0'] and '\0' or '\n'
692 if opts['rev']:
693 node = repo.manifest.lookup(opts['rev'])
694 else:
695 node = repo.manifest.tip()
696 manifest = repo.manifest.read(node)
697 cwd = repo.getcwd()
698 cwd_plus = cwd and (cwd + os.sep)
699 found = []
700 for f in manifest:
701 f = os.path.normcase(f)
702 if exclude.match(f) or not(include.match(f) and
703 f.startswith(cwd_plus) and
704 pat.match(os.path.basename(f))):
705 continue
706 if opts['fullpath']:
717 if opts['fullpath']:
707 f = os.path.join(repo.root, f)
718 ui.write(os.path.join(repo.root, abs), end)
708 elif cwd:
719 else:
709 f = f[len(cwd_plus):]
720 ui.write(rel, end)
710 found.append(f)
711 found.sort()
712 for f in found:
713 ui.write(f, end)
714
721
715 def log(ui, repo, f=None, **opts):
722 def log(ui, repo, f=None, **opts):
716 """show the revision history of the repository or a single file"""
723 """show the revision history of the repository or a single file"""
@@ -746,6 +753,11 b' def log(ui, repo, f=None, **opts):'
746 dodiff(sys.stdout, ui, repo, files, prev, changenode)
753 dodiff(sys.stdout, ui, repo, files, prev, changenode)
747 ui.write("\n\n")
754 ui.write("\n\n")
748
755
756 def ls(ui, repo, *pats, **opts):
757 """list files"""
758 for src, abs, rel in walk(repo, pats, opts):
759 ui.write(rel, '\n')
760
749 def manifest(ui, repo, rev=None):
761 def manifest(ui, repo, rev=None):
750 """output the latest or given revision of the project manifest"""
762 """output the latest or given revision of the project manifest"""
751 if rev:
763 if rev:
@@ -978,7 +990,7 b' def serve(ui, repo, **opts):'
978 ui.status('listening at http://%s/\n' % addr)
990 ui.status('listening at http://%s/\n' % addr)
979 httpd.serve_forever()
991 httpd.serve_forever()
980
992
981 def status(ui, repo):
993 def status(ui, repo, *pats, **opts):
982 '''show changed files in the working directory
994 '''show changed files in the working directory
983
995
984 C = changed
996 C = changed
@@ -986,7 +998,8 b' def status(ui, repo):'
986 R = removed
998 R = removed
987 ? = not tracked'''
999 ? = not tracked'''
988
1000
989 (c, a, d, u) = repo.changes(None, None)
1001 (c, a, d, u) = repo.changes(match = matchpats(ui, repo.getcwd(),
1002 pats, opts))
990 (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u))
1003 (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u))
991
1004
992 for f in c:
1005 for f in c:
@@ -1017,7 +1030,7 b' def tag(ui, repo, name, rev=None, **opts'
1017 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1030 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1018 return
1031 return
1019
1032
1020 (c, a, d, u) = repo.changes(None, None)
1033 (c, a, d, u) = repo.changes()
1021 for x in (c, a, d, u):
1034 for x in (c, a, d, u):
1022 if ".hgtags" in x:
1035 if ".hgtags" in x:
1023 ui.warn("abort: working copy of .hgtags is changed!\n")
1036 ui.warn("abort: working copy of .hgtags is changed!\n")
@@ -1088,11 +1101,16 b' def verify(ui, repo):'
1088 # Command options and aliases are listed here, alphabetically
1101 # Command options and aliases are listed here, alphabetically
1089
1102
1090 table = {
1103 table = {
1091 "^add": (add, [], "hg add FILE..."),
1104 "^add": (add,
1092 "addremove": (addremove, [], "hg addremove [FILE]..."),
1105 [('I', 'include', [], 'include path in search'),
1106 ('X', 'exclude', [], 'exclude path from search')],
1107 "hg add [OPTIONS] [FILES]"),
1108 "addremove": (addremove, [], "hg addremove [FILES]"),
1093 "^annotate":
1109 "^annotate":
1094 (annotate,
1110 (annotate,
1095 [('r', 'rev', '', 'revision'),
1111 [('I', 'include', [], 'include path in search'),
1112 ('X', 'exclude', [], 'exclude path from search'),
1113 ('r', 'rev', '', 'revision'),
1096 ('u', 'user', None, 'show user'),
1114 ('u', 'user', None, 'show user'),
1097 ('n', 'number', None, 'show revision number'),
1115 ('n', 'number', None, 'show revision number'),
1098 ('c', 'changeset', None, 'show changeset')],
1116 ('c', 'changeset', None, 'show changeset')],
@@ -1120,7 +1138,9 b' table = {'
1120 "debugindexdot": (debugindexdot, [], 'debugindexdot FILE'),
1138 "debugindexdot": (debugindexdot, [], 'debugindexdot FILE'),
1121 "^diff":
1139 "^diff":
1122 (diff,
1140 (diff,
1123 [('r', 'rev', [], 'revision')],
1141 [('I', 'include', [], 'include path in search'),
1142 ('X', 'exclude', [], 'exclude path from search'),
1143 ('r', 'rev', [], 'revision')],
1124 'hg diff [-r REV1 [-r REV2]] [FILE]...'),
1144 'hg diff [-r REV1 [-r REV2]] [FILE]...'),
1125 "^export":
1145 "^export":
1126 (export,
1146 (export,
@@ -1140,15 +1160,19 b' table = {'
1140 (locate,
1160 (locate,
1141 [('0', 'print0', None, 'end records with NUL'),
1161 [('0', 'print0', None, 'end records with NUL'),
1142 ('f', 'fullpath', None, 'print complete paths'),
1162 ('f', 'fullpath', None, 'print complete paths'),
1143 ('i', 'include', [], 'include path in search'),
1163 ('I', 'include', [], 'include path in search'),
1144 ('r', 'rev', '', 'revision'),
1164 ('r', 'rev', '', 'revision'),
1145 ('x', 'exclude', [], 'exclude path from search')],
1165 ('X', 'exclude', [], 'exclude path from search')],
1146 'hg locate [OPTION]... [PATTERN]...'),
1166 'hg locate [OPTION]... [PATTERN]...'),
1147 "^log|history":
1167 "^log|history":
1148 (log,
1168 (log,
1149 [('r', 'rev', [], 'revision'),
1169 [('r', 'rev', [], 'revision'),
1150 ('p', 'patch', None, 'show patch')],
1170 ('p', 'patch', None, 'show patch')],
1151 'hg log [-r REV1 [-r REV2]] [-p] [FILE]'),
1171 'hg log [-r REV1 [-r REV2]] [-p] [FILE]'),
1172 "list|ls": (ls,
1173 [('I', 'include', [], 'include path in search'),
1174 ('X', 'exclude', [], 'exclude path from search')],
1175 "hg ls [OPTION]... [PATTERN]...."),
1152 "manifest": (manifest, [], 'hg manifest [REV]'),
1176 "manifest": (manifest, [], 'hg manifest [REV]'),
1153 "parents": (parents, [], 'hg parents [REV]'),
1177 "parents": (parents, [], 'hg parents [REV]'),
1154 "^pull":
1178 "^pull":
@@ -1183,7 +1207,10 b' table = {'
1183 ('', 'stdio', None, 'for remote clients'),
1207 ('', 'stdio', None, 'for remote clients'),
1184 ('t', 'templates', "", 'template map')],
1208 ('t', 'templates', "", 'template map')],
1185 "hg serve [OPTION]..."),
1209 "hg serve [OPTION]..."),
1186 "^status": (status, [], 'hg status'),
1210 "^status": (status,
1211 [('I', 'include', [], 'include path in search'),
1212 ('X', 'exclude', [], 'exclude path from search')],
1213 'hg status [OPTION]... [FILE]...'),
1187 "tag":
1214 "tag":
1188 (tag,
1215 (tag,
1189 [('l', 'local', None, 'make the tag local'),
1216 [('l', 'local', None, 'make the tag local'),
@@ -1344,6 +1371,9 b' def dispatch(args):'
1344 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
1371 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
1345 else:
1372 else:
1346 u.warn("abort: %s\n" % inst.strerror)
1373 u.warn("abort: %s\n" % inst.strerror)
1374 except Abort, inst:
1375 u.warn('abort: ', inst.args[0] % inst.args[1:], '\n')
1376 sys.exit(1)
1347 except TypeError, inst:
1377 except TypeError, inst:
1348 # was this an argument error?
1378 # was this an argument error?
1349 tb = traceback.extract_tb(sys.exc_info()[2])
1379 tb = traceback.extract_tb(sys.exc_info()[2])
@@ -277,6 +277,36 b' class dirstate:'
277 self.map = None
277 self.map = None
278 self.pl = None
278 self.pl = None
279 self.copies = {}
279 self.copies = {}
280 self.ignorefunc = None
281
282 def wjoin(self, f):
283 return os.path.join(self.root, f)
284
285 def ignore(self, f):
286 if not self.ignorefunc:
287 bigpat = []
288 try:
289 l = file(self.wjoin(".hgignore"))
290 for pat in l:
291 if pat != "\n":
292 p = util.pconvert(pat[:-1])
293 try:
294 r = re.compile(p)
295 except:
296 self.ui.warn("ignoring invalid ignore"
297 + " regular expression '%s'\n" % p)
298 else:
299 bigpat.append(util.pconvert(pat[:-1]))
300 except IOError: pass
301
302 if bigpat:
303 s = "(?:%s)" % (")|(?:".join(bigpat))
304 r = re.compile(s)
305 self.ignorefunc = r.search
306 else:
307 self.ignorefunc = util.never
308
309 return self.ignorefunc(f)
280
310
281 def __del__(self):
311 def __del__(self):
282 if self.dirty:
312 if self.dirty:
@@ -298,8 +328,12 b' class dirstate:'
298 self.read()
328 self.read()
299 return self.pl
329 return self.pl
300
330
331 def markdirty(self):
332 if not self.dirty:
333 self.dirty = 1
334
301 def setparents(self, p1, p2 = nullid):
335 def setparents(self, p1, p2 = nullid):
302 self.dirty = 1
336 self.markdirty()
303 self.pl = p1, p2
337 self.pl = p1, p2
304
338
305 def state(self, key):
339 def state(self, key):
@@ -334,7 +368,7 b' class dirstate:'
334
368
335 def copy(self, source, dest):
369 def copy(self, source, dest):
336 self.read()
370 self.read()
337 self.dirty = 1
371 self.markdirty()
338 self.copies[dest] = source
372 self.copies[dest] = source
339
373
340 def copied(self, file):
374 def copied(self, file):
@@ -349,7 +383,7 b' class dirstate:'
349
383
350 if not files: return
384 if not files: return
351 self.read()
385 self.read()
352 self.dirty = 1
386 self.markdirty()
353 for f in files:
387 for f in files:
354 if state == "r":
388 if state == "r":
355 self.map[f] = ('r', 0, 0, 0)
389 self.map[f] = ('r', 0, 0, 0)
@@ -360,7 +394,7 b' class dirstate:'
360 def forget(self, files):
394 def forget(self, files):
361 if not files: return
395 if not files: return
362 self.read()
396 self.read()
363 self.dirty = 1
397 self.markdirty()
364 for f in files:
398 for f in files:
365 try:
399 try:
366 del self.map[f]
400 del self.map[f]
@@ -370,7 +404,7 b' class dirstate:'
370
404
371 def clear(self):
405 def clear(self):
372 self.map = {}
406 self.map = {}
373 self.dirty = 1
407 self.markdirty()
374
408
375 def write(self):
409 def write(self):
376 st = self.opener("dirstate", "w")
410 st = self.opener("dirstate", "w")
@@ -383,34 +417,50 b' class dirstate:'
383 st.write(e + f)
417 st.write(e + f)
384 self.dirty = 0
418 self.dirty = 0
385
419
386 def changes(self, files, ignore):
420 def walk(self, files = None, match = util.always):
387 self.read()
421 self.read()
388 dc = self.map.copy()
422 dc = self.map.copy()
389 lookup, changed, added, unknown = [], [], [], []
423 # walk all files by default
390
391 # compare all files by default
392 if not files: files = [self.root]
424 if not files: files = [self.root]
393
425 def traverse():
394 # recursive generator of all files listed
395 def walk(files):
396 for f in util.unique(files):
426 for f in util.unique(files):
397 f = os.path.join(self.root, f)
427 f = os.path.join(self.root, f)
398 if os.path.isdir(f):
428 if os.path.isdir(f):
399 for dir, subdirs, fl in os.walk(f):
429 for dir, subdirs, fl in os.walk(f):
400 d = dir[len(self.root) + 1:]
430 d = dir[len(self.root) + 1:]
431 if d == '.hg':
432 subdirs[:] = []
433 continue
401 for sd in subdirs:
434 for sd in subdirs:
402 if ignore(os.path.join(d, sd +'/')):
435 ds = os.path.join(d, sd +'/')
436 if self.ignore(ds) or not match(ds):
403 subdirs.remove(sd)
437 subdirs.remove(sd)
404 for fn in fl:
438 for fn in fl:
405 fn = util.pconvert(os.path.join(d, fn))
439 fn = util.pconvert(os.path.join(d, fn))
406 yield fn
440 yield 'f', fn
407 else:
441 else:
408 yield f[len(self.root) + 1:]
442 yield 'f', f[len(self.root) + 1:]
409
443
410 for k in dc.keys():
444 for k in dc.keys():
411 yield k
445 yield 'm', k
446
447 # yield only files that match: all in dirstate, others only if
448 # not in .hgignore
412
449
413 for fn in util.unique(walk(files)):
450 for src, fn in util.unique(traverse()):
451 if fn in dc:
452 del dc[fn]
453 elif self.ignore(fn):
454 continue
455 if match(fn):
456 yield src, fn
457
458 def changes(self, files = None, match = util.always):
459 self.read()
460 dc = self.map.copy()
461 lookup, changed, added, unknown = [], [], [], []
462
463 for src, fn in self.walk(files, match):
414 try: s = os.stat(os.path.join(self.root, fn))
464 try: s = os.stat(os.path.join(self.root, fn))
415 except: continue
465 except: continue
416
466
@@ -429,9 +479,9 b' class dirstate:'
429 elif c[1] != s.st_mode or c[3] != s.st_mtime:
479 elif c[1] != s.st_mode or c[3] != s.st_mtime:
430 lookup.append(fn)
480 lookup.append(fn)
431 else:
481 else:
432 if not ignore(fn): unknown.append(fn)
482 if match(fn): unknown.append(fn)
433
483
434 return (lookup, changed, added, dc.keys(), unknown)
484 return (lookup, changed, added, filter(match, dc.keys()), unknown)
435
485
436 # used to avoid circular references so destructors work
486 # used to avoid circular references so destructors work
437 def opener(base):
487 def opener(base):
@@ -493,7 +543,6 b' class localrepository:'
493 self.wopener = opener(self.root)
543 self.wopener = opener(self.root)
494 self.manifest = manifest(self.opener)
544 self.manifest = manifest(self.opener)
495 self.changelog = changelog(self.opener)
545 self.changelog = changelog(self.opener)
496 self.ignorefunc = None
497 self.tagscache = None
546 self.tagscache = None
498 self.nodetagscache = None
547 self.nodetagscache = None
499
548
@@ -503,29 +552,6 b' class localrepository:'
503 self.ui.readconfig(self.opener("hgrc"))
552 self.ui.readconfig(self.opener("hgrc"))
504 except IOError: pass
553 except IOError: pass
505
554
506 def ignore(self, f):
507 if not self.ignorefunc:
508 bigpat = ["^.hg/$"]
509 try:
510 l = file(self.wjoin(".hgignore"))
511 for pat in l:
512 if pat != "\n":
513 p = util.pconvert(pat[:-1])
514 try:
515 r = re.compile(p)
516 except:
517 self.ui.warn("ignoring invalid ignore"
518 + " regular expression '%s'\n" % p)
519 else:
520 bigpat.append(util.pconvert(pat[:-1]))
521 except IOError: pass
522
523 s = "(?:%s)" % (")|(?:".join(bigpat))
524 r = re.compile(s)
525 self.ignorefunc = r.search
526
527 return self.ignorefunc(f)
528
529 def hook(self, name, **args):
555 def hook(self, name, **args):
530 s = self.ui.config("hooks", name)
556 s = self.ui.config("hooks", name)
531 if s:
557 if s:
@@ -738,7 +764,7 b' class localrepository:'
738 else:
764 else:
739 self.ui.warn("%s not tracked!\n" % f)
765 self.ui.warn("%s not tracked!\n" % f)
740 else:
766 else:
741 (c, a, d, u) = self.changes(None, None)
767 (c, a, d, u) = self.changes()
742 commit = c + a
768 commit = c + a
743 remove = d
769 remove = d
744
770
@@ -815,7 +841,16 b' class localrepository:'
815 if not self.hook("commit", node=hex(n)):
841 if not self.hook("commit", node=hex(n)):
816 return 1
842 return 1
817
843
818 def changes(self, node1, node2, files=None):
844 def walk(self, node = None, files = [], match = util.always):
845 if node:
846 for fn in self.manifest.read(self.changelog.read(node)[0]):
847 yield 'm', fn
848 else:
849 for src, fn in self.dirstate.walk(files, match):
850 yield src, fn
851
852 def changes(self, node1 = None, node2 = None, files = [],
853 match = util.always):
819 mf2, u = None, []
854 mf2, u = None, []
820
855
821 def fcmp(fn, mf):
856 def fcmp(fn, mf):
@@ -823,16 +858,23 b' class localrepository:'
823 t2 = self.file(fn).revision(mf[fn])
858 t2 = self.file(fn).revision(mf[fn])
824 return cmp(t1, t2)
859 return cmp(t1, t2)
825
860
861 def mfmatches(node):
862 mf = dict(self.manifest.read(node))
863 for fn in mf.keys():
864 if not match(fn):
865 del mf[fn]
866 return mf
867
826 # are we comparing the working directory?
868 # are we comparing the working directory?
827 if not node2:
869 if not node2:
828 l, c, a, d, u = self.dirstate.changes(files, self.ignore)
870 l, c, a, d, u = self.dirstate.changes(files, match)
829
871
830 # are we comparing working dir against its parent?
872 # are we comparing working dir against its parent?
831 if not node1:
873 if not node1:
832 if l:
874 if l:
833 # do a full compare of any files that might have changed
875 # do a full compare of any files that might have changed
834 change = self.changelog.read(self.dirstate.parents()[0])
876 change = self.changelog.read(self.dirstate.parents()[0])
835 mf2 = self.manifest.read(change[0])
877 mf2 = mfmatches(change[0])
836 for f in l:
878 for f in l:
837 if fcmp(f, mf2):
879 if fcmp(f, mf2):
838 c.append(f)
880 c.append(f)
@@ -847,20 +889,20 b' class localrepository:'
847 if not node2:
889 if not node2:
848 if not mf2:
890 if not mf2:
849 change = self.changelog.read(self.dirstate.parents()[0])
891 change = self.changelog.read(self.dirstate.parents()[0])
850 mf2 = self.manifest.read(change[0]).copy()
892 mf2 = mfmatches(change[0])
851 for f in a + c + l:
893 for f in a + c + l:
852 mf2[f] = ""
894 mf2[f] = ""
853 for f in d:
895 for f in d:
854 if f in mf2: del mf2[f]
896 if f in mf2: del mf2[f]
855 else:
897 else:
856 change = self.changelog.read(node2)
898 change = self.changelog.read(node2)
857 mf2 = self.manifest.read(change[0])
899 mf2 = mfmatches(change[0])
858
900
859 # flush lists from dirstate before comparing manifests
901 # flush lists from dirstate before comparing manifests
860 c, a = [], []
902 c, a = [], []
861
903
862 change = self.changelog.read(node1)
904 change = self.changelog.read(node1)
863 mf1 = self.manifest.read(change[0]).copy()
905 mf1 = mfmatches(change[0])
864
906
865 for fn in mf2:
907 for fn in mf2:
866 if mf1.has_key(fn):
908 if mf1.has_key(fn):
@@ -885,7 +927,7 b' class localrepository:'
885 self.ui.warn("%s does not exist!\n" % f)
927 self.ui.warn("%s does not exist!\n" % f)
886 elif not os.path.isfile(p):
928 elif not os.path.isfile(p):
887 self.ui.warn("%s not added: mercurial only supports files currently\n" % f)
929 self.ui.warn("%s not added: mercurial only supports files currently\n" % f)
888 elif self.dirstate.state(f) == 'n':
930 elif self.dirstate.state(f) in 'an':
889 self.ui.warn("%s already tracked!\n" % f)
931 self.ui.warn("%s already tracked!\n" % f)
890 else:
932 else:
891 self.dirstate.update([f], "a")
933 self.dirstate.update([f], "a")
@@ -1268,7 +1310,7 b' class localrepository:'
1268 ma = self.manifest.read(man)
1310 ma = self.manifest.read(man)
1269 mfa = self.manifest.readflags(man)
1311 mfa = self.manifest.readflags(man)
1270
1312
1271 (c, a, d, u) = self.changes(None, None)
1313 (c, a, d, u) = self.changes()
1272
1314
1273 # is this a jump, or a merge? i.e. is there a linear path
1315 # is this a jump, or a merge? i.e. is there a linear path
1274 # from p1 to p2?
1316 # from p1 to p2?
@@ -6,6 +6,8 b''
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 import os, errno
8 import os, errno
9 from demandload import *
10 demandload(globals(), "re")
9
11
10 def unique(g):
12 def unique(g):
11 seen = {}
13 seen = {}
@@ -29,6 +31,54 b' def explain_exit(code):'
29 return "stopped by signal %d" % val, val
31 return "stopped by signal %d" % val, val
30 raise ValueError("invalid exit code")
32 raise ValueError("invalid exit code")
31
33
34 def always(fn): return True
35 def never(fn): return False
36
37 def globre(pat, head = '^', tail = '$'):
38 "convert a glob pattern into a regexp"
39 i, n = 0, len(pat)
40 res = ''
41 group = False
42 def peek(): return i < n and pat[i]
43 while i < n:
44 c = pat[i]
45 i = i+1
46 if c == '*':
47 if peek() == '*':
48 i += 1
49 res += '.*'
50 else:
51 res += '[^/]*'
52 elif c == '?':
53 res += '.'
54 elif c == '[':
55 j = i
56 if j < n and pat[j] in '!]':
57 j += 1
58 while j < n and pat[j] != ']':
59 j += 1
60 if j >= n:
61 res += '\\['
62 else:
63 stuff = pat[i:j].replace('\\','\\\\')
64 i = j + 1
65 if stuff[0] == '!':
66 stuff = '^' + stuff[1:]
67 elif stuff[0] == '^':
68 stuff = '\\' + stuff
69 res = '%s[%s]' % (res, stuff)
70 elif c == '{':
71 group = True
72 res += '(?:'
73 elif c == '}' and group:
74 res += ')'
75 group = False
76 elif c == ',' and group:
77 res += '|'
78 else:
79 res += re.escape(c)
80 return head + res + tail
81
32 def system(cmd, errprefix=None):
82 def system(cmd, errprefix=None):
33 """execute a shell command that must succeed"""
83 """execute a shell command that must succeed"""
34 rc = os.system(cmd)
84 rc = os.system(cmd)
@@ -33,15 +33,29 b' basic hg commands (use "hg help -v" for '
33 status show changed files in the working directory
33 status show changed files in the working directory
34 update update or merge working directory
34 update update or merge working directory
35 hg add: option -h not recognized
35 hg add: option -h not recognized
36 hg add FILE...
36 hg add [OPTIONS] [FILES]
37
38 -I --include
39 include path in search
40 -X --exclude
41 exclude path from search
37
42
38 add the specified files on the next commit
43 add the specified files on the next commit
39 hg add: option --skjdfks not recognized
44 hg add: option --skjdfks not recognized
40 hg add FILE...
45 hg add [OPTIONS] [FILES]
46
47 -I --include
48 include path in search
49 -X --exclude
50 exclude path from search
41
51
42 add the specified files on the next commit
52 add the specified files on the next commit
43 hg diff [-r REV1 [-r REV2]] [FILE]...
53 hg diff [-r REV1 [-r REV2]] [FILE]...
44
54
55 -I --include
56 include path in search
57 -X --exclude
58 exclude path from search
45 -r --rev
59 -r --rev
46 revision
60 revision
47
61
General Comments 0
You need to be logged in to leave comments. Login now