##// 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 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 39 path::
39 40 indicates a path on the local machine
@@ -51,11 +52,14 b' repository path::'
51 52 COMMANDS
52 53 --------
53 54
54 add [files ...]::
55 add [options] [files ...]::
55 56 Schedule files to be version controlled and added to the repository.
56 57
57 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 63 addremove::
60 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 73 place.
70 74
71 75 options:
76 -I, --include <pat> include directories matching the given patterns
77 -X, --exclude <pat> exclude directories matching the given patterns
72 78 -r, --revision <rev> annotate the specified revision
73 79 -u, --user list the author
74 80 -c, --changeset list the changeset
@@ -129,6 +135,10 b' diff [-r revision] [-r revision] [files '
129 135 revisions are specified, the working directory files are compared
130 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 142 export [-o filespec] [revision] ...::
133 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 193 init::
184 194 Initialize a new repository in the current directory.
185 195
186 locate [options] [patterns]::
187 Print all files under Mercurial control whose basenames match the
196 locate [options] [files]::
197 Print all files under Mercurial control whose names match the
188 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 200 This command searches the current directory and its
195 201 subdirectories. To search an entire repository, move to the root
196 202 of the repository.
@@ -207,9 +213,9 b' locate [options] [patterns]::'
207 213
208 214 -0, --print0 end filenames with NUL, for use with xargs
209 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 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 220 log [-r revision ...] [-p] [file]::
215 221 Print the revision history of the specified file or the entire project.
@@ -319,8 +325,10 b' serve [options]::'
319 325 -n, --name <name> name to show in web pages (default: working dir)
320 326 -t, --templatedir <path> web templates to use
321 327
322 status::
323 Show changed files in the working directory.
328 status [options] [files]::
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 333 The codes used to show the status of files are:
326 334
@@ -329,6 +337,11 b' status::'
329 337 R = removed
330 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 345 tag [-l -t <text> -d <datecode> -u <user>] <name> [revision]::
333 346 Name a particular revision using <name>.
334 347
@@ -398,6 +411,52 b' verify::'
398 411 the changelog, manifest, and tracked files, as well as the
399 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 460 SPECIFYING SINGLE REVISIONS
402 461 ---------------------------
403 462
@@ -14,6 +14,9 b' demandload(globals(), "errno socket vers'
14 14 class UnknownCommand(Exception):
15 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 20 def filterfiles(filters, files):
18 21 l = [x for x in files if x in filters]
19 22
@@ -36,6 +39,41 b' def relpath(repo, args):'
36 39 for x in args]
37 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 77 revrangesep = ':'
40 78
41 79 def revrange(ui, repo, revs, revlog=None):
@@ -60,8 +98,7 b' def revrange(ui, repo, revs, revlog=None'
60 98 try:
61 99 num = revlog.rev(revlog.lookup(val))
62 100 except KeyError:
63 ui.warn('abort: invalid revision identifier %s\n' % val)
64 sys.exit(1)
101 raise Abort('invalid revision identifier %s', val)
65 102 return num
66 103 for spec in revs:
67 104 if spec.find(revrangesep) >= 0:
@@ -91,6 +128,7 b' def make_filename(repo, r, pat, node=Non'
91 128 'b': lambda: os.path.basename(repo.root),
92 129 }
93 130
131 try:
94 132 if node:
95 133 expander.update(node_expander)
96 134 if node and revwidth is not None:
@@ -114,6 +152,21 b' def make_filename(repo, r, pat, node=Non'
114 152 newname.append(c)
115 153 i += 1
116 154 return ''.join(newname)
155 except KeyError, inst:
156 raise Abort("invalid format spec '%%%s' in output file name",
157 inst.args[0])
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 171 def dodiff(fp, ui, repo, files=None, node1=None, node2=None):
119 172 def date(c):
@@ -288,9 +341,17 b' def help_(ui, cmd=None):'
288 341
289 342 # Commands start here, listed alphabetically
290 343
291 def add(ui, repo, file1, *files):
344 def add(ui, repo, *pats, **opts):
292 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 356 def addremove(ui, repo, *files):
296 357 """add all new files, delete all missing files"""
@@ -307,11 +368,11 b' def addremove(ui, repo, *files):'
307 368 elif s not in 'nmai' and isfile:
308 369 u.append(f)
309 370 else:
310 (c, a, d, u) = repo.changes(None, None)
371 (c, a, d, u) = repo.changes()
311 372 repo.add(u)
312 373 repo.remove(d)
313 374
314 def annotate(ui, repo, file1, *files, **opts):
375 def annotate(ui, repo, *pats, **opts):
315 376 """show changeset information per file line"""
316 377 def getnode(rev):
317 378 return hg.short(repo.changelog.node(rev))
@@ -342,8 +403,8 b' def annotate(ui, repo, file1, *files, **'
342 403 node = repo.dirstate.parents()[0]
343 404 change = repo.changelog.read(node)
344 405 mmap = repo.manifest.read(change[0])
345 for f in relpath(repo, (file1,) + files):
346 lines = repo.file(f).annotate(mmap[f])
406 for src, abs, rel in walk(repo, pats, opts, emptyok = False):
407 lines = repo.file(abs).annotate(mmap[abs])
347 408 pieces = []
348 409
349 410 for o, f in opmap:
@@ -362,16 +423,7 b' def cat(ui, repo, file1, rev=None, **opt'
362 423 n = r.lookup(rev)
363 424 else:
364 425 n = r.tip()
365 if opts['output'] and opts['output'] != '-':
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
426 fp = make_file(repo, r, opts['output'], node=n)
375 427 fp.write(r.read(n))
376 428
377 429 def clone(ui, source, dest=None, **opts):
@@ -475,8 +527,7 b' def debugcheckstate(ui, repo):'
475 527 ui.warn("%s in manifest1, but listed as state %s" % (f, state))
476 528 errors += 1
477 529 if errors:
478 ui.warn(".hg/dirstate inconsistent with current parent's manifest\n")
479 sys.exit(1)
530 raise Abort(".hg/dirstate inconsistent with current parent's manifest")
480 531
481 532 def debugstate(ui, repo):
482 533 """show the contents of the current dirstate"""
@@ -509,21 +560,18 b' def debugindexdot(ui, file_):'
509 560 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
510 561 ui.write("}\n")
511 562
512 def diff(ui, repo, *files, **opts):
563 def diff(ui, repo, *pats, **opts):
513 564 """diff working directory (or selected files)"""
514 565 revs = []
515 566 if opts['rev']:
516 567 revs = map(lambda x: repo.lookup(x), opts['rev'])
517 568
518 569 if len(revs) > 2:
519 ui.warn("too many revisions to diff\n")
520 sys.exit(1)
570 raise Abort("too many revisions to diff")
521 571
522 if files:
523 files = relpath(repo, files)
524 else:
525 files = relpath(repo, [""])
526
572 files = []
573 for src, abs, rel in walk(repo, pats, opts):
574 files.append(abs)
527 575 dodiff(sys.stdout, ui, repo, files, *revs)
528 576
529 577 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
@@ -531,19 +579,11 b' def doexport(ui, repo, changeset, seqno,'
531 579 prev, other = repo.changelog.parents(node)
532 580 change = repo.changelog.read(node)
533 581
534 if opts['output'] and opts['output'] != '-':
535 try:
536 outname = make_filename(repo, repo.changelog, opts['output'],
582 fp = make_file(repo, repo.changelog, opts['output'],
537 583 node=node, total=total, seqno=seqno,
538 584 revwidth=revwidth)
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
585 if fp != sys.stdout:
586 ui.note("Exporting patch to '%s'.\n" % fp.name)
547 587
548 588 fp.write("# HG changeset patch\n")
549 589 fp.write("# User %s\n" % change[1])
@@ -555,12 +595,12 b' def doexport(ui, repo, changeset, seqno,'
555 595 fp.write("\n\n")
556 596
557 597 dodiff(fp, ui, repo, None, prev, node)
598 if fp != sys.stdout: fp.close()
558 599
559 600 def export(ui, repo, *changesets, **opts):
560 601 """dump the header and diffs for one or more changesets"""
561 602 if not changesets:
562 ui.warn("error: export requires at least one changeset\n")
563 sys.exit(1)
603 raise Abort("export requires at least one changeset")
564 604 seqno = 0
565 605 revs = list(revrange(ui, repo, changesets))
566 606 total = len(revs)
@@ -586,7 +626,7 b' def identify(ui, repo):'
586 626 return
587 627
588 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 630 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
591 631 (c or a or d) and "+" or "")]
592 632
@@ -654,8 +694,7 b' def import_(ui, repo, patch1, *patches, '
654 694 files.append(pf)
655 695 patcherr = f.close()
656 696 if patcherr:
657 sys.stderr.write("patch failed")
658 sys.exit(1)
697 raise Abort("patch failed")
659 698
660 699 if len(files) > 0:
661 700 addremove(ui, repo, *files)
@@ -665,52 +704,20 b' def init(ui, source=None):'
665 704 """create a new repository in the current directory"""
666 705
667 706 if source:
668 ui.warn("no longer supported: use \"hg clone\" instead\n")
669 sys.exit(1)
707 raise Abort("no longer supported: use \"hg clone\" instead")
670 708 hg.repository(ui, ".", create=1)
671 709
672 710 def locate(ui, repo, *pats, **opts):
673 711 """locate files matching specific patterns"""
674 if [p for p in pats if os.sep in p]:
675 ui.warn("error: patterns may not contain '%s'\n" % os.sep)
676 ui.warn("use '-i <dir>' instead\n")
677 sys.exit(1)
678 def compile(pats, head='^', tail=os.sep, on_empty=True):
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'])
712 if opts['print0']: end = '\0'
713 else: end = '\n'
714 opts['rootless'] = True
715 for src, abs, rel in walk(repo, pats, opts):
716 if repo.dirstate.state(abs) == '?': continue
717 if opts['fullpath']:
718 ui.write(os.path.join(repo.root, abs), end)
694 719 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']:
707 f = os.path.join(repo.root, f)
708 elif cwd:
709 f = f[len(cwd_plus):]
710 found.append(f)
711 found.sort()
712 for f in found:
713 ui.write(f, end)
720 ui.write(rel, end)
714 721
715 722 def log(ui, repo, f=None, **opts):
716 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 753 dodiff(sys.stdout, ui, repo, files, prev, changenode)
747 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 761 def manifest(ui, repo, rev=None):
750 762 """output the latest or given revision of the project manifest"""
751 763 if rev:
@@ -978,7 +990,7 b' def serve(ui, repo, **opts):'
978 990 ui.status('listening at http://%s/\n' % addr)
979 991 httpd.serve_forever()
980 992
981 def status(ui, repo):
993 def status(ui, repo, *pats, **opts):
982 994 '''show changed files in the working directory
983 995
984 996 C = changed
@@ -986,7 +998,8 b' def status(ui, repo):'
986 998 R = removed
987 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 1003 (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u))
991 1004
992 1005 for f in c:
@@ -1017,7 +1030,7 b' def tag(ui, repo, name, rev=None, **opts'
1017 1030 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1018 1031 return
1019 1032
1020 (c, a, d, u) = repo.changes(None, None)
1033 (c, a, d, u) = repo.changes()
1021 1034 for x in (c, a, d, u):
1022 1035 if ".hgtags" in x:
1023 1036 ui.warn("abort: working copy of .hgtags is changed!\n")
@@ -1088,11 +1101,16 b' def verify(ui, repo):'
1088 1101 # Command options and aliases are listed here, alphabetically
1089 1102
1090 1103 table = {
1091 "^add": (add, [], "hg add FILE..."),
1092 "addremove": (addremove, [], "hg addremove [FILE]..."),
1104 "^add": (add,
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 1109 "^annotate":
1094 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 1114 ('u', 'user', None, 'show user'),
1097 1115 ('n', 'number', None, 'show revision number'),
1098 1116 ('c', 'changeset', None, 'show changeset')],
@@ -1120,7 +1138,9 b' table = {'
1120 1138 "debugindexdot": (debugindexdot, [], 'debugindexdot FILE'),
1121 1139 "^diff":
1122 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 1144 'hg diff [-r REV1 [-r REV2]] [FILE]...'),
1125 1145 "^export":
1126 1146 (export,
@@ -1140,15 +1160,19 b' table = {'
1140 1160 (locate,
1141 1161 [('0', 'print0', None, 'end records with NUL'),
1142 1162 ('f', 'fullpath', None, 'print complete paths'),
1143 ('i', 'include', [], 'include path in search'),
1163 ('I', 'include', [], 'include path in search'),
1144 1164 ('r', 'rev', '', 'revision'),
1145 ('x', 'exclude', [], 'exclude path from search')],
1165 ('X', 'exclude', [], 'exclude path from search')],
1146 1166 'hg locate [OPTION]... [PATTERN]...'),
1147 1167 "^log|history":
1148 1168 (log,
1149 1169 [('r', 'rev', [], 'revision'),
1150 1170 ('p', 'patch', None, 'show patch')],
1151 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 1176 "manifest": (manifest, [], 'hg manifest [REV]'),
1153 1177 "parents": (parents, [], 'hg parents [REV]'),
1154 1178 "^pull":
@@ -1183,7 +1207,10 b' table = {'
1183 1207 ('', 'stdio', None, 'for remote clients'),
1184 1208 ('t', 'templates', "", 'template map')],
1185 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 1214 "tag":
1188 1215 (tag,
1189 1216 [('l', 'local', None, 'make the tag local'),
@@ -1344,6 +1371,9 b' def dispatch(args):'
1344 1371 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
1345 1372 else:
1346 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 1377 except TypeError, inst:
1348 1378 # was this an argument error?
1349 1379 tb = traceback.extract_tb(sys.exc_info()[2])
@@ -277,6 +277,36 b' class dirstate:'
277 277 self.map = None
278 278 self.pl = None
279 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 311 def __del__(self):
282 312 if self.dirty:
@@ -298,8 +328,12 b' class dirstate:'
298 328 self.read()
299 329 return self.pl
300 330
331 def markdirty(self):
332 if not self.dirty:
333 self.dirty = 1
334
301 335 def setparents(self, p1, p2 = nullid):
302 self.dirty = 1
336 self.markdirty()
303 337 self.pl = p1, p2
304 338
305 339 def state(self, key):
@@ -334,7 +368,7 b' class dirstate:'
334 368
335 369 def copy(self, source, dest):
336 370 self.read()
337 self.dirty = 1
371 self.markdirty()
338 372 self.copies[dest] = source
339 373
340 374 def copied(self, file):
@@ -349,7 +383,7 b' class dirstate:'
349 383
350 384 if not files: return
351 385 self.read()
352 self.dirty = 1
386 self.markdirty()
353 387 for f in files:
354 388 if state == "r":
355 389 self.map[f] = ('r', 0, 0, 0)
@@ -360,7 +394,7 b' class dirstate:'
360 394 def forget(self, files):
361 395 if not files: return
362 396 self.read()
363 self.dirty = 1
397 self.markdirty()
364 398 for f in files:
365 399 try:
366 400 del self.map[f]
@@ -370,7 +404,7 b' class dirstate:'
370 404
371 405 def clear(self):
372 406 self.map = {}
373 self.dirty = 1
407 self.markdirty()
374 408
375 409 def write(self):
376 410 st = self.opener("dirstate", "w")
@@ -383,34 +417,50 b' class dirstate:'
383 417 st.write(e + f)
384 418 self.dirty = 0
385 419
386 def changes(self, files, ignore):
420 def walk(self, files = None, match = util.always):
387 421 self.read()
388 422 dc = self.map.copy()
389 lookup, changed, added, unknown = [], [], [], []
390
391 # compare all files by default
423 # walk all files by default
392 424 if not files: files = [self.root]
393
394 # recursive generator of all files listed
395 def walk(files):
425 def traverse():
396 426 for f in util.unique(files):
397 427 f = os.path.join(self.root, f)
398 428 if os.path.isdir(f):
399 429 for dir, subdirs, fl in os.walk(f):
400 430 d = dir[len(self.root) + 1:]
431 if d == '.hg':
432 subdirs[:] = []
433 continue
401 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 437 subdirs.remove(sd)
404 438 for fn in fl:
405 439 fn = util.pconvert(os.path.join(d, fn))
406 yield fn
440 yield 'f', fn
407 441 else:
408 yield f[len(self.root) + 1:]
442 yield 'f', f[len(self.root) + 1:]
409 443
410 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 464 try: s = os.stat(os.path.join(self.root, fn))
415 465 except: continue
416 466
@@ -429,9 +479,9 b' class dirstate:'
429 479 elif c[1] != s.st_mode or c[3] != s.st_mtime:
430 480 lookup.append(fn)
431 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 486 # used to avoid circular references so destructors work
437 487 def opener(base):
@@ -493,7 +543,6 b' class localrepository:'
493 543 self.wopener = opener(self.root)
494 544 self.manifest = manifest(self.opener)
495 545 self.changelog = changelog(self.opener)
496 self.ignorefunc = None
497 546 self.tagscache = None
498 547 self.nodetagscache = None
499 548
@@ -503,29 +552,6 b' class localrepository:'
503 552 self.ui.readconfig(self.opener("hgrc"))
504 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 555 def hook(self, name, **args):
530 556 s = self.ui.config("hooks", name)
531 557 if s:
@@ -738,7 +764,7 b' class localrepository:'
738 764 else:
739 765 self.ui.warn("%s not tracked!\n" % f)
740 766 else:
741 (c, a, d, u) = self.changes(None, None)
767 (c, a, d, u) = self.changes()
742 768 commit = c + a
743 769 remove = d
744 770
@@ -815,7 +841,16 b' class localrepository:'
815 841 if not self.hook("commit", node=hex(n)):
816 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 854 mf2, u = None, []
820 855
821 856 def fcmp(fn, mf):
@@ -823,16 +858,23 b' class localrepository:'
823 858 t2 = self.file(fn).revision(mf[fn])
824 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 868 # are we comparing the working directory?
827 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 872 # are we comparing working dir against its parent?
831 873 if not node1:
832 874 if l:
833 875 # do a full compare of any files that might have changed
834 876 change = self.changelog.read(self.dirstate.parents()[0])
835 mf2 = self.manifest.read(change[0])
877 mf2 = mfmatches(change[0])
836 878 for f in l:
837 879 if fcmp(f, mf2):
838 880 c.append(f)
@@ -847,20 +889,20 b' class localrepository:'
847 889 if not node2:
848 890 if not mf2:
849 891 change = self.changelog.read(self.dirstate.parents()[0])
850 mf2 = self.manifest.read(change[0]).copy()
892 mf2 = mfmatches(change[0])
851 893 for f in a + c + l:
852 894 mf2[f] = ""
853 895 for f in d:
854 896 if f in mf2: del mf2[f]
855 897 else:
856 898 change = self.changelog.read(node2)
857 mf2 = self.manifest.read(change[0])
899 mf2 = mfmatches(change[0])
858 900
859 901 # flush lists from dirstate before comparing manifests
860 902 c, a = [], []
861 903
862 904 change = self.changelog.read(node1)
863 mf1 = self.manifest.read(change[0]).copy()
905 mf1 = mfmatches(change[0])
864 906
865 907 for fn in mf2:
866 908 if mf1.has_key(fn):
@@ -885,7 +927,7 b' class localrepository:'
885 927 self.ui.warn("%s does not exist!\n" % f)
886 928 elif not os.path.isfile(p):
887 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 931 self.ui.warn("%s already tracked!\n" % f)
890 932 else:
891 933 self.dirstate.update([f], "a")
@@ -1268,7 +1310,7 b' class localrepository:'
1268 1310 ma = self.manifest.read(man)
1269 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 1315 # is this a jump, or a merge? i.e. is there a linear path
1274 1316 # from p1 to p2?
@@ -6,6 +6,8 b''
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 import os, errno
9 from demandload import *
10 demandload(globals(), "re")
9 11
10 12 def unique(g):
11 13 seen = {}
@@ -29,6 +31,54 b' def explain_exit(code):'
29 31 return "stopped by signal %d" % val, val
30 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 82 def system(cmd, errprefix=None):
33 83 """execute a shell command that must succeed"""
34 84 rc = os.system(cmd)
@@ -33,15 +33,29 b' basic hg commands (use "hg help -v" for '
33 33 status show changed files in the working directory
34 34 update update or merge working directory
35 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 43 add the specified files on the next commit
39 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 52 add the specified files on the next commit
43 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 59 -r --rev
46 60 revision
47 61
General Comments 0
You need to be logged in to leave comments. Login now